方案一
使用窗口函数以及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|
±--------±----±----+
思考
向下补齐,同理,不过最好对指定列进行降序排序,再用窗口函数,否则性能会有所损失
如有不对的地方请不吝赐教