1. SparkSql如何自定义函数

2. 示例:Average

3. 类型安全的自定义函数

1. SparkSql如何自定义函数?

  spark中我们定义一个函数,需要继承 UserDefinedAggregateFunction这个抽象类,实现这个抽象类中所定义的方法,这是一个模板设计模式? 我只要实现抽象类的中方法,具体的所有的计算步骤由内部完成。而我们可以看一下UserDefinedAggregateFunction这个抽象类。

package org.apache.spark.sql.expressions
@org.apache.spark.annotation.InterfaceStability.Stable
abstract class UserDefinedAggregateFunction() extends scala.AnyRef with scala.Serializable {
def inputSchema : org.apache.spark.sql.types.StructType
def bufferSchema : org.apache.spark.sql.types.StructType
def dataType : org.apache.spark.sql.types.DataType
def deterministic : scala.Boolean
def initialize(buffer : org.apache.spark.sql.expressions.MutableAggregationBuffer) : scala.Unit
def update(buffer : org.apache.spark.sql.expressions.MutableAggregationBuffer, input : org.apache.spark.sql.Row) : scala.Unit
def merge(buffer1 : org.apache.spark.sql.expressions.MutableAggregationBuffer, buffer2 : org.apache.spark.sql.Row) : scala.Unit
def evaluate(buffer : org.apache.spark.sql.Row) : scala.Any
@scala.annotation.varargs
def apply(exprs : org.apache.spark.sql.Column*) : org.apache.spark.sql.Column = { /* compiled code */ }
@scala.annotation.varargs
def distinct(exprs : org.apache.spark.sql.Column*) : org.apache.spark.sql.Column = { /* compiled code */ }
}

  也就是说对于这几个函数,我们只要依次实现他们的功能,其余的交给spark就可以了。

  

2. 自定义Average函数

  首先新建一个Object类MyAvage类,继承UserDefinedAggregateFunction。下面对每一个函数的实现进行解释。

  def inputSchema: StructType = StructType(StructField("inputColumn", LongType) :: Nil)

  这个规定了输入数据的数据结构

def bufferSchema: StructType = {
StructType(StructField("sum", LongType) :: StructField("count", LongType) :: Nil)
}

  这个规定了缓存区的数据结构

  def dataType: DataType = DoubleType

  这个规定了返回值的数据类型

def deterministic: Boolean = true
def initialize(buffer: MutableAggregationBuffer): Unit = {
buffer(0) = 0L
buffer(1) = 0L
}  

进行初始化,这里要说明一下,官网中提到:

// Initializes the given aggregation buffer. The buffer itself is a `Row` that in addition to
// standard methods like retrieving a value at an index (e.g., get(), getBoolean()), provides
// the opportunity to update its values. Note that arrays and maps inside the buffer are still
// immutable.

这里翻译一下:

我们为我们的缓冲区设置初始值,我们不仅可以设置数字,还可以使用index getBoolen等去改变他的值,但是我们需要知道的是,在这个缓冲区中,数组和map依然是不可变的。

其实最后一句我也是不太明白,等我以后如果能研究并理解这句话,再回来补充吧。

def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
if (!input.isNullAt(0)) {
buffer(0) = buffer.getLong(0) + input.getLong(0)
buffer(1) = buffer.getLong(1) + 1
}
}

  这个是重要的update函数,对于平均值,我们可以不断迭代输入的值进行累加。buffer(0)统计总和,buffer(1)统计长度。

def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
buffer1(0) = buffer1.getLong(0) + buffer2.getLong(0)
buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1)
}

  在做完update后spark 需要将结果进行merge到我们的区域,因此有一个merge 进行覆盖buffer

  def evaluate(buffer: Row): Double = buffer.getLong(0).toDouble / buffer.getLong(1)

  这是将最终的结果进行计算。

在写完这个类以后我们在我们的sparksession里面进行编写测试案例。

spark.sparkContext.textFile("file:///Users/4pa/Desktop/people.txt")
.map(_.split(","))
.map(agg=>Person(agg(0),agg(1).trim.toInt))
.toDF().createOrReplaceTempView("people")
spark.udf.register("myAverage",Myaverage)
val udfRes = spark.sql("select name,myAverage(age) as avgAge from people group by name")
udfRes.show()

  

3. 类型安全的自定义函数

从上面我们可以看出来,这种自定义函数不是类型安全的,因此能否实现一个安全的自定义函数呢?

个人觉得最好的例子还是官网给的例子,具体的解释都已经给了出来,思路其实和上面是一样的,只不过定义了两个caseclass,用于类型的验证。

case class Employee(name: String, salary: Long)
case class Average(var sum: Long, var count: Long) object MyAverage extends Aggregator[Employee, Average, Double] {
// 初始化
def zero: Average = Average(0L, 0L)
// 这个其实有点map-reduce的意思,只不过是对一个类的reduce,第一个值是和,第二个是总数
def reduce(buffer: Average, employee: Employee): Average = {
buffer.sum += employee.salary
buffer.count += 1
buffer
}
// 实现缓冲区的一个覆盖
def merge(b1: Average, b2: Average): Average = {
b1.sum += b2.sum
b1.count += b2.count
b1
}
// 计算最终数值
def finish(reduction: Average): Double = reduction.sum.toDouble / reduction.count
// Specifies the Encoder for the intermediate value type
def bufferEncoder: Encoder[Average] = Encoders.product
// 指定返回类型
def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}

  

 

SparkSQL 如何自定义函数的更多相关文章

  1. Spark学习之路 (十九)SparkSQL的自定义函数UDF

    在Spark中,也支持Hive中的自定义函数.自定义函数大致可以分为三种: UDF(User-Defined-Function),即最基本的自定义函数,类似to_char,to_date等 UDAF( ...

  2. Spark(十三)SparkSQL的自定义函数UDF与开窗函数

    一 自定义函数UDF 在Spark中,也支持Hive中的自定义函数.自定义函数大致可以分为三种: UDF(User-Defined-Function),即最基本的自定义函数,类似to_char,to_ ...

  3. Spark学习之路 (十九)SparkSQL的自定义函数UDF[转]

    在Spark中,也支持Hive中的自定义函数.自定义函数大致可以分为三种: UDF(User-Defined-Function),即最基本的自定义函数,类似to_char,to_date等 UDAF( ...

  4. sparksql udf自定义函数中参数过多问题的解决

    在进行spark sql数据库操作中,常常需要一些spark系统本身不支持的函数,如获取某一列值中的字符串. 如要获取 “aaaakkkkk”中的第4-第8个字符. 针对这种需求,只有设置UDF来实现 ...

  5. 【Spark篇】---SparkSql之UDF函数和UDAF函数

    一.前述 SparkSql中自定义函数包括UDF和UDAF UDF:一进一出  UDAF:多进一出 (联想Sum函数) 二.UDF函数 UDF:用户自定义函数,user defined functio ...

  6. SparkSQL中的自定义函数UDF

    在Spark中,也支持Hive中的自定义函数.自定义函数大致可以分为三种: UDF(User-Defined-Function),即最基本的自定义函数,类似to_char,to_date等 UDAF( ...

  7. spark自定义函数之——UDF使用详解及代码示例

    前言 本文介绍如何在Spark Sql和DataFrame中使用UDF,如何利用UDF给一个表或者一个DataFrame根据需求添加几列,并给出了旧版(Spark1.x)和新版(Spark2.x)完整 ...

  8. 入门大数据---SparkSQL常用聚合函数

    一.简单聚合 1.1 数据准备 // 需要导入 spark sql 内置的函数包 import org.apache.spark.sql.functions._ val spark = SparkSe ...

  9. Mysql - 存储过程/自定义函数

    在数据库操作中, 尤其是碰到一些复杂一些的系统, 不可避免的, 会用到函数/自定义函数, 或者存储过程. 实际项目中, 自定义函数和存储过程是越少越好, 因为这个东西多了, 也是一个非常难以维护的地方 ...

随机推荐

  1. 记一次手工清除挖矿病毒WannaMine V4.0的经历

    [作者:byeyear    邮箱:byeyear@hotmail.com    转载请注明] 前两天公司信息安全处通知我的计算机存在永恒之蓝漏洞并已被病毒感染,使用多方杀软及专杀工具均无法有效清除, ...

  2. Linux基础:Linux下常用命令

    常用命令 shutdown ​ 用来系统关机命令.shutdown指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动作. ​ shutdown (选项)(参数) -c: 当执行"s ...

  3. UITapGestureRecognizer 和 UICollectionView、UITableView的点击事件冲突问题

    解决:https://blog.gocy.tech/2016/11/19/iOS-touch-handling/ 1. tap.delegate = self; tap加个代理<UIGestur ...

  4. 怎么彻底删除用友通T3财务软件?

    [问题现象]怎么彻底删除用友通T3财务软件? [原因分析]通过"添加或删除程序"无法正常卸载用友通T3,也尝试了360安全卫士强力卸载,都无法完全卸载,有没有办法可以彻底删除用友通 ...

  5. 【ZooKeeper系列】3.ZooKeeper源码环境搭建

    前文阅读: [ZooKeeper系列]1.ZooKeeper单机版.伪集群和集群环境搭建 [ZooKeeper系列]2.用Java实现ZooKeeper API的调用 在系列的前两篇文章中,介绍了Zo ...

  6. ansible批量部署tomcat

    1.1 构建目录结构 此操作是安装nginx+mysql+tomcat+db的目录结构,可以参考一下,不错~ mkdir -p /ansible/roles/{nginx,mysql,tomcat,d ...

  7. 开启 Django 博客的 RSS 功能

    作者:HelloGitHub-追梦人物 文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 博客提供 RSS 订阅应该是标配,这样读者就可以通过一些聚合阅读工具订阅你的博客,时 ...

  8. 初次在cmd使用git命令上传项目至github方法(笔记)

    在一切开始之前,先推荐一个git简易工具书--Git_Cheat_Sheet,非常适合新手.自行搜索即可,也有热心者提供了中文版. 一.下载 Git 从Git官网下载Git安装包 https://gi ...

  9. UML类图基础

    UML( Unified Modeling Language) 统一建模语言, 它是一个支持模型化和软件系统开发的图形化语言,为软件开发的所有阶段提供模型化和可视化支持,包括由需求分析到规格,到构造和 ...

  10. 「CF242E」XOR on Segment 解题报告

    题面 长度为\(n\)的数列,现有两种操作: 1.区间异或操作 2.区间求和操作 对于每个查询,输出答案 思路: 线段树+二进制拆位 线段树区间修改一般使用的都是懒标记的方法,但是对于异或,懒标记的方 ...