spark sql中的udf和udaf实现

今天没什么事,突然想起之前写过的sqark中SQL中的UDAF方法,这个还是挺有意思的,难度比蜂房中UDAF高,其中直接体现了火花的分而治之的细想,所以打算今天的博客在加一个火花SQL的UDF和UDAF编写。

直接进入正题。

1.udf函数的
编写.sqlContext.udf.register(“CTOF”,(degreesCelcius:Double)=>((degreesCelcius * 9.0 / 5.0)+ 32.0))
sqlContext.sql(“SELECT city,CTOF(avgLow)AS avgLowF ,CTOF(avgHigh)AS avgHighF FROM citytemps“)。show()
较为简单不做过多描述。

2.udfa函数的编写。

import org.apache.spark.sql.Row
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, StringType, StructField, StructType}
/**
  * @author 李春凯
  * 在开始前有必要了解一下 StructField.DataType 中支持的类型
  * 类型有NullType, DateType, TimestampType, BinaryType,IntegerType, BooleanType, LongType, DoubleType, FloatType, ShortType, ByteType, StringType
  * 用法  :  在要使用该函数的地方需要使用sqlContext声明 函数名 才能使用
  * e.g sqlContext.udf.register("numsAvg", new MyUDAF)
  */

class MyUDAF extends  UserDefinedAggregateFunction {
  /**
    * 指定具体的输入数据的类型  支持多个值输入
    * 自段名称随意:Users can choose names to identify the input arguments - 这里可以是“name”,或者其他任意串
    * 这里支持的格式有 NullType, DateType, TimestampType, BinaryType,
    * IntegerType, BooleanType, LongType, DoubleType, FloatType, ShortType, ByteType, StringType
    *
    */
  override def inputSchema: StructType = StructType(StructField("docid",StringType)::Nil)

  /**
    * 在进行聚合操作的时候所要处理的数据的中间结果类型
    *  支持多个值进行计算
    *  根据具体需求声明参数个数 可以是一个或者是多个 在initialize方法中初始化
    */
  override def bufferSchema: StructType = StructType(StructField("val",StringType)::Nil)

  /**
    * 输出类型的定义,可以是对应类型的Array或者是AraayBuffer
    * e.g StringType  可以输出 String  Array[String] 和  AraayBuffer[String]  但是不能是String[]
    */
  override def dataType: DataType = StringType

  override def deterministic: Boolean = true

  /**
    * 初始化Buffer
    *
    * @param buffer 用于接收和存储输入的值
    *               buffer(0)对应bufferSchema的第一个类型的值
    *               buffer(1)对应bufferSchema的第二个类型的值
    *               以此类推
    *  buffer(0) 不支持数组,集合和可变集合
    *  e.g  支持StringType但是不支持Arry[String] ArryBuffer[String] 和 String[]
    *  --- 该类主要用于参考
    *  --- 该类实现另外多个列的一个字段组合成了一个列的字段
    */
  override def initialize(buffer: MutableAggregationBuffer): Unit = {
    buffer(0) = ""
  }

  /**
    *  将同久值与新值合并的地方
    *  注意: 这里的新参数【buffer】因为在初始化的时候就转换了所以在这里不用再次转换,
    *         但是input【inputSchema】是传入的新值类型是IntegerTyper,和inputSchema的bufferSchema的类型不一致 ,
    *         所以需要转换
    * @param buffer  旧值  如果之前没有值传入吗,这个值就是初始值
    * @param input   新值函数每次接收的值,空值不会传进来
    */
  override def update(buffer: MutableAggregationBuffer, input: Row): Unit ={
    val Str1 = buffer.getAs[String](0)
    val Str2 = input.getAs[String](0)
    if(Str1.trim.equals("")){
      buffer(0)=buffer.getAs[String](0)+input.getAs[String](0)
    }else{
      buffer(0) =buffer.getAs[String](0)+"||"+input.getAs[String](0)
    }
  }

  /**
    *  将多个线程中的的内容合并在一个 Buffer1中,
    *  这个操作类似于mapreduce中的conbiner
    *  也类似于spark中的conbinerbykey函数的分区合并
    * @param buffer1 第一个线程/上一个线程【或者是分区,这里我不是很清楚到底有没有分区的概念 】
    * @param buffer2  第二个线程或者说是下一个线程
    */
  override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
    val  str1 = buffer1.getString(0)
    val  str2 = buffer2.getString(0)
    if(str1.trim.equals("")){
      buffer1(0)=str1+str2
    }else{
      buffer1(0) = str1+"||"+str2
    }
   // buffer1(0) = buffer1.getAs[String](0) +"="+buffer2.getAs[String](0)
  }

  /**
    *  指定返回值的内容 这里的返回类型只支持DataType对应的类型  不支持数组,集合和可变集合
    *  e.g  支持StringType但是不支持Arry[String] ArryBuffer[String] 和 String[]
    * @param buffer
    * @return
    */
  override def evaluate(buffer: Row): Any = {
   // buffer.getAs[String](0)
    buffer.getString(0)
  }
}

3.这里我在添加一个多个字段组合成一个字段的UDAF,这个类主要是方便大家对比理解其中的思想。
 

package com.ql.UDAFUtil
import org.apache.spark.sql.Row
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, StringType, StructField, StructType}
/**
  * 将多行多个字段组合成一行一个字段
  */

class kCollect_Set extends  UserDefinedAggregateFunction{


  /*
   * collect_set(CONCAT(\"http://112.124.126.220:8080/slfh/download?path=\",da.filePath,da.fileName,\"&fileName=\",da.sourceFileName
   * String
   * |-- fileName: string (nullable = true)
   * |-- filePath: string (nullable = true)
   * String
   * |-- sourceFileName: string (nullable = true)
   */

  override def inputSchema: StructType = StructType(
      StructField("http",StringType)::
      StructField("filePath",StringType)::
      StructField("filePath",StringType)::
      StructField("symbol",StringType)::
      StructField("str",StringType)::
      StructField("sourceFileName",StringType)::
      Nil)

  override def bufferSchema: StructType =  StructType(
    StructField("http_buffer",StringType)::
    Nil)

  override def dataType: DataType = StringType

  override def deterministic: Boolean = true

  override def initialize(buffer: MutableAggregationBuffer): Unit = {
    buffer(0)=""
  }

  override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {

    val input1 = input.getAs[String](0)
    val input2 = input.getAs[String](1)
    val input3 = input.getAs[String](2)
    val input4 = input.getAs[String](3)
    val input5 = input.getAs[String](4)
    val input6 = input.getAs[String](5)
    var str = ""
    // collect_set(CONCAT(\"http://112.124.126.220:8080/slfh/download?path=\",da.filePath,da.fileName,\"&fileName=\",da.sourceFileName
    if(input1!=null){ str = str+input1}
    if(input2!=null){ str = str+input2 }
    if(input3!=null){ str = str+input3 }
    if(input4!=null){ str = str+input4 }
    if(input5!=null){ str = str+input5 }
    if(input6!=null){ str = str+input6 }
    val buf = buffer.getAs[String](0)
    if(buf.trim.equals("")){
      buffer(0)=buf+str
    }else{
      buffer(0)=buf+"||"+str
    }
  }

  override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
      var str1 = buffer1.getString(0)
      var str2 = buffer2.getString(0)
    if(str1.trim.equals("")){
      buffer1(0)=str1+str2
    }else{
      buffer1(0)=str1+"||"+str2
    }
  }

  override def evaluate(buffer: Row): Any = {
    buffer.getString(0)

  }
}

 4.总结spark sql中的udaf类的操作稍微复杂一点,但是并不是不能理解的,在我的第一个中中【myudaf】中有着详细的解释,在第二个实例类中有着多个字段的对比写法,这俩个实例可以但对比观看,查看其中区别和奥妙。在下面的文字中我会简单写出UDAF的番薯执行顺序和功能。
执行顺序,和我写的实例中的函数排列顺序一样
.a)中声明输入的类型,名字不用在意
b)中声明缓冲区中初始值的类型,名字不用在意
c)中声明输出类型
d)定义函数的确定性为真,这个的作用我没有看懂,在什么情况下可以为flase,读者知道的话,希望可以告诉我,我会留下联系方式
.e)初始化缓冲区的第一个值
.f)对分区/线程中的值进行操作.g
)分区/线程间的结果进行合并处理.h
)输出最后的结果
.i)在主函数中定义该类的函数,并在spark_sql中直接使用。
在主函数中定义如下:

//生成collect_set函数,针对列级一个字段 整合为一个字段的函数
  sqlContext.udf.register("collect_set", new MyUDAF)
//生成collect_sets函数,针对列级多字段 整合为一个字段的函数
sqlContext.udf.register("collect_sets",new Collect_Set)

联系方式: 

猜你喜欢

转载自blog.csdn.net/qq_35866165/article/details/83789836
今日推荐