spark-12.sparkSQL_3_sparkSQL自定義函數

UDF函數

通過spark.udf.register(“name”,func)來進行註冊。使用select func() … 來直接調用。如:

val peopleDF = spark.read.json("examples/src/main/resources/people.json")
peopleDF.createOrReplaceTempView("people")
spark.udf.register("add",(x:String)=>"A:"+x)
spark.sql("select add(name) from people").show

UDAF函數

1、弱類型UDAF函數

  • 需要繼承 UserDefinedAggregateFunction類,並複寫方法。
  • 註冊一個UDAF函數。
  • 使用自定以的UDAF函數。

如:

package com.dengdan.sparksql

import org.apache.spark.SparkConf
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, DoubleType, IntegerType, LongType, StructField, StructType}

/**
 * 自定義UDAF函數
 * 樣例數據:
 * {"name":"Michael", "salary":3000}
 * {"name":"Andy", "salary":4500}
 * {"name":"Justin", "salary":3500}
 * {"name":"Berta", "salary":4000}
 * 目標:求平均工資【工資的總額,工資的個數】
 */
class AverageSal extends UserDefinedAggregateFunction {
  //輸入數據
  override def inputSchema: StructType = StructType(StructField("salary", LongType) :: Nil)

  //每個分區中的 共享變量
  override def bufferSchema: StructType = StructType(StructField("sum", LongType) :: StructField("count", IntegerType) :: Nil)

  //表示UDAF函數的輸出類型
  override def dataType: DataType = DoubleType

  //表示 如果有相同的輸入是否會存在相同的輸出,如果是則true
  override def deterministic: Boolean = true

  //初始化 每個分區中的共享變量
  override def initialize(buffer: MutableAggregationBuffer): Unit = {
    buffer(0) = 0L
    buffer(1) = 0
  }

  //每個分區中的每條數據聚合的時候需要調用該方法
  override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
    //獲取這一行中的工資,將工資加入到sum中。
    buffer(0) = buffer.getLong(0) + input.getLong(0)
    //將工資的個數加1
    buffer(1) = buffer.getInt(1) + 1
  }

  //將每一個分區的數據合併,形成最後的數據
  override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
    //合併總的工資
    buffer1(0) = buffer1.getLong(0) + buffer2.getLong(0)
    //合併總的工資個數
    buffer1(1) = buffer1.getInt(1) + buffer2.getInt(1)
  }

  //給出計算結果
  override def evaluate(buffer: Row): Any = {
    //總的工資 / 總的工資個數
    buffer.getLong(0).toDouble / buffer.getInt(1)
  }
}

object AverageSal {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setAppName("udaf").setMaster("local[*]")
    val spark = SparkSession.builder().config(sparkConf).getOrCreate()
    //讀入數據
    val employee = spark.read.json("D:\\idea_workspace2020\\spark\\sparksql\\doc\\employees.json")
    employee.createOrReplaceTempView("employee")
    spark.udf.register("average", new AverageSal)
    spark.sql("select average(salary) from employee").show

    spark.stop()
  }
}

2、強類型UDAF函數

  • 主要在DSL風格中使用,繼承Aggregator抽象類。依次配置輸入、共享變量、輸出的類型,需要用到case class。
  • 使用時,調用UDAF函數是一個DataSet對象。
    代碼實現:
package com.dengdan.sparksql

import org.apache.spark.SparkConf
import org.apache.spark.sql.{Encoder, Encoders, SparkSession}
import org.apache.spark.sql.expressions.Aggregator


case class Employee(name: String, salary: Long)

case class Aver(var sum: Long, var count: Int)

/**
 * 求平均工資
 */
class Average extends Aggregator[Employee, Aver, Double] {
  //初始化每個分區中的共享變量
  override def zero: Aver = Aver(0L, 0)

  //每個分區中每一條數據聚合的時候需要調的方法
  override def reduce(b: Aver, a: Employee): Aver = {
    b.sum += a.salary
    b.count += 1
    b
  }

  //將每個分區的輸出 合併 形成最後的數據
  override def merge(b1: Aver, b2: Aver): Aver = {
    b1.sum += b2.sum
    b1.count += b2.count
    b1
  }

  //給出計算結果
  override def finish(reduction: Aver): Double = reduction.sum.toDouble / reduction.count

  //主要用於對共享變量進行編碼
  override def bufferEncoder: Encoder[Aver] = Encoders.product

  //主要用於將輸出進行編碼
  override def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}

/**
 * 求平均工資
 */
object Average {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setAppName("udaf").setMaster("local[*]")
    val spark = SparkSession.builder().config(sparkConf).getOrCreate()
    //讀入數據並創建DataSet變量
    import spark.implicits._
    val employee = spark.read.json("D:\\idea_workspace2020\\spark\\sparksql\\doc\\employees.json").as[Employee]
    val aver = new Average().toColumn.name("average")
    employee.select(aver).show()

    spark.stop()
  }
}

開窗函數

  • rank() 跳躍排序,有兩個第二名時,後邊跟着的時第四名。
  • dense_rank() 連續排序,有兩個第二名時仍然跟着第三名。
  • over () 開窗函數:
    在使用聚合函數後,會將多行變成一行,而開窗函數是將一行變成多行。並且在使用聚合函數後,如果要顯示其他的列必須將列加入到group by中。而使用開窗函數後,可以不使用group by,直接將所有信息顯示出來。開窗函數適用於每一行的最後一列添加聚合函數的結果。

常用的開窗函數有:

  1. 爲每條數據顯示聚合信息。聚合函數 () over()
  2. 爲每條數據提供分組的聚合函數結果。聚合函數() over(partition by 字段) as 別名
    –按字段分組,分組後進行計算。
  3. 與排名函數一起使用。row number() over(order by 字段) as 別名

常用分析函數:

  1. row_number() over(partition by ... order by ...)
  2. rank() over(partition by ... order by ...)
  3. dense_rank() over(partition by ... order by ...)
  4. count() over(partition by ... order by ...)
  5. max() over(partition by ... order by ...)
  6. min() over(partition by ... order by ...)
  7. sum() over(partition by ... order by ...)
  8. avg() over(partition by ... order by ...)
  9. first_value() over(partition by ... order by ...)
  10. last_value() over(partition by ... order by ...)
  11. lag() over(partition by ... order by ...)
  12. lead() over(partition by ... order by ...)

lag和lead可以獲取結果集中,按一定排序所排列的當前行的上下相鄰若干offset的某個行的某個列(不用結果集的自關聯);lag,lead分別是向前,向後;lag與lead有三個參數,第一個參數是列名,第二個參數是偏移的offset,第三個參數是超出記錄窗口時的默認值。

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