方案一
使用窗口函數以及index實現
具體操作放大圖查看
方案二
使用窗口函數的特性以及spark2.4.0 新的array特性進行結合
具體操作放大圖查看
其中所指的窗口函數
聚合函數(index索引) over(order by index rows between unbounded preceding and current row)
表示針對每一行的記錄來說。只計算當前行的index 之前所有行到當前行的範圍內對指定字段進行聚合計算,
此窗口函數類似於神經網絡中的卷積層,用來掃描一定範圍內的數據,並對這些數據進行指定操作。
在spark sql 中的catelog邏輯執行計劃項目中,對於窗口函數的聚類計算做了一定層度的優化,其會保留上次窗口得到的結果,以及重疊狀態依次進行如下計算,因此相率還是可以的
方案三使用 udaf創建自定義用戶聚合函數,用來註冊到sparksession中
package com.homewell.feature.udaf
import org.apache.spark.sql.Row
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, DoubleType, StructField, StructType}
class UpperUDAF extends UserDefinedAggregateFunction{
// 用戶輸入 schema
override def inputSchema: StructType = StructType(
StructField("metric" , DoubleType)::Nil
)
// 在shuffle過程中的中間過程schema
override def bufferSchema: StructType = StructType(
StructField("mid" , DoubleType)::Nil
)
// 定義udaf最終輸出的格式
override def dataType: DataType = DoubleType
// 數學概念中的最終一致性
override def deterministic: Boolean = true
// 初始化中間變量值
override def initialize(buffer: MutableAggregationBuffer): Unit = {
buffer.update(0,null)
}
// 用後面的函數更新數據值
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
println("buffer",buffer )
val inputvalue = input.get(0)
val before = buffer.get(0)
println("update ", inputvalue)
if(inputvalue !=null) {
buffer.update(0, inputvalue)
}
}
// shuffle合併的時候出現的情況
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
val leftvalue = buffer1.get(0)
val rightvalue = buffer2.get(0)
if(rightvalue !=null) {
buffer1.update(0, rightvalue)
}
}
// 獲取原數據
override def evaluate(buffer: Row): Double = {
return buffer.getDouble(0)
}
}
用戶只要繼承spark 提供的UserDefinedAggregateFunction接口
並註冊到spark中 如下使用
val conf = new SparkConf().setAppName("adb").setMaster("local")
// val sc = new SparkContext(conf)
val scc = SparkSession.builder().config(conf).getOrCreate()
scc.udf.register("upperfill", new UpperUDAF())
val txt: DataFrame = scc.read.format("csv")
.option("sep",",")
.option("inferSchema", "true")
.option("header", "true")
.load("data/udafupper.txt")
txt.createOrReplaceTempView("txt")
import scc.sql
val data: DataFrame = sql("select *,upperfill(score) over(order by name) as upper from txt")
data.show()
±--------±----±----+
| name|score|upper|
±--------±----±----+
| zhangll| 99| 99.0|
|zhangll17| null| 99.0|
| zhangll2| 98| 98.0|
| zhangll3| 85| 85.0|
| zhangll4| null| 85.0|
| zhangll5| 87| 87.0|
±--------±----±----+
思考
向下補齊,同理,不過最好對指定列進行降序排序,再用窗口函數,否則性能會有所損失
如有不對的地方請不吝賜教