spark2.4.0 sql dataframe向上補齊操作(含udaf操作)

方案一

使用窗口函數以及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|
±--------±----±----+

思考

向下補齊,同理,不過最好對指定列進行降序排序,再用窗口函數,否則性能會有所損失

如有不對的地方請不吝賜教

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章