spark笔记-UDF&&UDAF


使用实例见大数据处理的一些方法及代码

1. 应用UDF函数

  1. 通过spark.udf.register(name,func)来注册一个UDF函数,name是UDF调用时的标识符,fun是一个函数,用于处理字段。
  2. 需要将一个DF或者DS注册为一个临时表。
  3. 通过spark.sql去运行一个SQL语句,在SQL语句中可以通过 name(列名) 方式来应用UDF函数。

2. UDAF 用户自定义聚合函数

2.1 弱类型用户自定义聚合函数

1. 新建一个Class 继承UserDefinedAggregateFunction  ,然后复写方法:
    
    //聚合函数需要输入参数的数据类型
    override def inputSchema: StructType = ???

    //可以理解为保存聚合函数业务逻辑数据的一个数据结构
	override def bufferSchema: StructType = ???

    // 返回值的数据类型
	override def dataType: DataType = ???

    // 对于相同的输入一直有相同的输出
	override def deterministic: Boolean = true

     //用于初始化你的数据结构
	override def initialize(buffer: MutableAggregationBuffer): Unit = ???

     //用于同分区内Row对聚合函数的更新操作
	override def update(buffer: MutableAggregationBuffer, input: Row): Unit = ???

    //用于不同分区对聚合结果的聚合。
	override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = ???

    //计算最终结果
	override def evaluate(buffer: Row): Any = ???
	
	
2. 你需要通过spark.udf.resigter去注册你的UDAF函数。
3. 需要通过spark.sql去运行你的SQL语句,可以通过 select UDAF(列名) 来应用你的用户自定义聚合函数。
  • 实例:实现求平均工资
/*
定义类继承UserDefinedAggregateFunction,并重写其中方法
*/
class MyAveragUDAF extends UserDefinedAggregateFunction {

  // 聚合函数输入参数的数据类型
  def inputSchema: StructType = StructType(Array(StructField("age",IntegerType)))

  // 聚合函数缓冲区中值的数据类型(age,count)
  def bufferSchema: StructType = {
    StructType(Array(StructField("sum",LongType),StructField("count",LongType)))
  }

  // 函数返回值的数据类型
  def dataType: DataType = DoubleType

  // 稳定性:对于相同的输入是否一直返回相同的输出。
  def deterministic: Boolean = true

  // 函数缓冲区初始化
  def initialize(buffer: MutableAggregationBuffer): Unit = {
    // 存年龄的总和
    buffer(0) = 0L
    // 存年龄的个数
    buffer(1) = 0L
  }

  // 更新缓冲区中的数据
  def update(buffer: MutableAggregationBuffer,input: Row): Unit = {
    if (!input.isNullAt(0)) {
      buffer(0) = buffer.getLong(0) + input.getInt(0)
      buffer(1) = buffer.getLong(1) + 1
    }
  }

  // 合并缓冲区
  def merge(buffer1: MutableAggregationBuffer,buffer2: Row): Unit = {
    buffer1(0) = buffer1.getLong(0) + buffer2.getLong(0)
    buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1)
  }

  // 计算最终结果
  def evaluate(buffer: Row): Double = buffer.getLong(0).toDouble / buffer.getLong(1)
}

2.2 强类型的用户自定义聚合函数

1. 新建一个class,继承Aggregator[Employee, Average, Double],其中Employee是在应用聚合函数的时候传入的对象,Average是聚合函数在运行的时候内部需要的数据结构,Double是聚合函数最终需要输出的类型。这些可以根据自己的业务需求去调整。复写想对应的方法:
    
       //用于定义一个聚合函数内部需要的数据结构
	override def zero: Average = ???

        //针对每个分区内部每一个输入来更新你的数据结构
	override def reduce(b: Average, a: Employee): Average = ???

       //用于对于不同分区的结构进行聚合
	override def merge(b1: Average, b2: Average): Average = ???

       //计算输出
	override def finish(reduction: Average): Double = ???

       //用于数据结构他的转换
	override def bufferEncoder: Encoder[Average] = ???

       //用于最终结果的转换
	override def outputEncoder: Encoder[Double] = ???


2. 新建一个UDAF实例,通过DF或者DS的DSL风格语法去应用。
  • 实例:实现求平均工资
//输入数据类型
case class User01(username:String,age:Long)
//缓存类型
case class AgeBuffer(var sum:Long,var count:Long)
/**
  * 定义类继承org.apache.spark.sql.expressions.Aggregator
  * 重写类中的方法
  */
class MyAveragUDAF1 extends Aggregator[User01,AgeBuffer,Double]{
  override def zero: AgeBuffer = {
    AgeBuffer(0L,0L)
  }

  override def reduce(b: AgeBuffer, a: User01): AgeBuffer = {
    b.sum = b.sum + a.age
    b.count = b.count + 1
    b
  }

  override def merge(b1: AgeBuffer, b2: AgeBuffer): AgeBuffer = {
    b1.sum = b1.sum + b2.sum
    b1.count = b1.count + b2.count
    b1
  }

  override def finish(buff: AgeBuffer): Double = {
    buff.sum.toDouble/buff.count
  }
  //DataSet默认额编解码器,用于序列化,固定写法
  //自定义类型就是produce   自带类型根据类型选择
  override def bufferEncoder: Encoder[AgeBuffer] = {
    Encoders.product
  }
    override def outputEncoder: Encoder[Double] = {
    Encoders.scalaDouble
  }
}

使用实例见大数据处理的一些方法及代码

猜你喜欢

转载自blog.csdn.net/weixin_42526352/article/details/104871203