UDF(User-Defined-Function)

UDF是用于处理一行数据的,接受一行输入产生一个输出,类似与map()算子,

UDAF(User- Defined Aggregation Funcation)

UDAF用于接收一组输入数据然后产生一个输出结果。

UDAF需要使用继承UserDefinedAggregateFunction的自定义类来实现功能,UserDefinedAggregateFunction中提供了8个抽象方法来帮助我们实现UDAF的构建。

public StructType inputSchema()

用于指定UDAF所输入数据的schmema的,也就是需要在这个方法类定义UDAF输入数据的字段的名称合字段的类型。

StructType bufferSchema()

因为UDAF是将数据进行聚合的,因此会使用到中间的临时变量进行数据存储,这个方法是用于定义这些中间的临时变量的Schema的。

DataType dataType()

这个方法是用于定义UDAF的返回结果的数据结构的。

boolean deterministic()

这个方法用于返回聚合函数是否是幂等的,即相同输入是否总是能得到相同输出。

为什么会有这个方法呢?这源于spark的推测执行(spark.speculation=true推测执行开启):推测执行是指对于Spark程序里面少部分运行慢的Task,会在其他节点的Executor上再次启动这个task,如果其中一个Task实例运行成功则将这个最先完成的Task的计算结果作为最终结果,同时会干掉其他Executor上运行的实例,从而加快运行速度。但是推测执行只有在函数是幂等的情况下才会这样运作,如果不是幂等的函数只会一直等待该Task执行。

void initialize(MutableAggregationBuffer buffer)

该方法用于初始化缓冲区的字段。

void update(MutableAggregationBuffer buffer, Row row)

该方法用于处理相同的executor间的数据合并,当有新的输入数据时,update用户更新缓存变量。

void merge(MutableAggregationBuffer buffer, Row row):

该方法用于不同excutor间已经进行初步聚合的数据进行合并。

Object evaluate(Row row):

通过前面的缓冲区完成聚合后,在这个方法里对聚合的字段进行最终的运算。

实例:

import org.apache.spark.sql.Row;
import org.apache.spark.sql.expressions.MutableAggregationBuffer;
import org.apache.spark.sql.expressions.UserDefinedAggregateFunction;
import org.apache.spark.sql.types.DataType;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType; import java.util.ArrayList;
import java.util.List; public class MyUDAF extends UserDefinedAggregateFunction {
private StructType inputSchema;
private StructType bufferSchema; public MyUDAF() {
List<StructField> inputFields = new ArrayList<>();
inputFields.add(DataTypes.createStructField("inputColumn", DataTypes.DoubleType, true));
inputSchema = DataTypes.createStructType(inputFields); List<StructField> bufferFields = new ArrayList<>();
bufferFields.add(DataTypes.createStructField("sum", DataTypes.DoubleType, true));
bufferFields.add(DataTypes.createStructField("count", DataTypes.DoubleType, true));
bufferSchema = DataTypes.createStructType(bufferFields);
} //1、该聚合函数的输入参数的数据类型
public StructType inputSchema() {
return inputSchema;
} //2、聚合缓冲区中的数据类型.(有序性)
public StructType bufferSchema() {
return bufferSchema;
} //3、返回值的数据类型
public DataType dataType() {
return DataTypes.DoubleType;
} //4、这个函数是否总是在相同的输入上返回相同的输出,一般为true
public boolean deterministic() {
return true;
} //5、初始化给定的聚合缓冲区,在索引值为0的sum=0;索引值为1的count=1;
public void initialize(MutableAggregationBuffer buffer) {
buffer.update(0, 0D);
buffer.update(1, 0D);
} //6、更新
public void update(MutableAggregationBuffer buffer, Row input) {
//如果input的索引值为0的值不为0
if (!input.isNullAt(0)) {
double updateSum = buffer.getDouble(0) + input.getDouble(0);
double updateCount = buffer.getDouble(1) + 1;
buffer.update(0, updateSum);
buffer.update(1, updateCount);
}
} //7、合并两个聚合缓冲区,并将更新后的缓冲区值存储回“buffer1”
public void merge(MutableAggregationBuffer buffer1, Row buffer2) {
double mergeSum = buffer1.getDouble(0) + buffer2.getDouble(0);
double mergeCount = buffer1.getDouble(1) + buffer2.getDouble(1);
buffer1.update(0, mergeSum);
buffer1.update(1, mergeCount);
} //8、计算出最终结果
public Double evaluate(Row buffer) {
return buffer.getDouble(0) / buffer.getDouble(1);
}
}

main函数:

import org.apache.spark.SparkContext;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.api.java.UDF1;
import org.apache.spark.sql.types.DataTypes; import java.math.BigDecimal; public class UDAFJAVA {
public static void main(String[] args) {
SparkSession spark = SparkSession
.builder()
.appName("RunMyUDAF")
.master("local")
.getOrCreate();
SparkContext sc = spark.sparkContext();
sc.setLogLevel("ERROR"); // Register the function to access it
spark.udf().register("myAverage", new MyUDAF()); Dataset<Row> df = spark.read().json("D:\\02Code\\0901\\sd_demo\\src\\data\\udaf.json");
df.createOrReplaceTempView("employees");
df.show(); //保留两位小数,四舍五入
spark.udf().register("twoDecimal", new UDF1<Double, Double>() {
@Override
public Double call(Double in) throws Exception {
BigDecimal b = new BigDecimal(in);
double res = b.setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue();
return res;
}
}, DataTypes.DoubleType); Dataset<Row> result = spark
.sql("SELECT name,twoDecimal(myAverage(salary)) as avg_salary FROM employees group by name");
result.show();
spark.stop();
}
}

udaf.json:

{"name":"Michael","salary":0}
{"name":"Andy","salary":4537}
{"name":"Justin","salary":3500.0}
{"name":"Berta","salary":0}
{"name":"Michael","salary":3000.0}
{"name":"Andy","salary":4500.0}
{"name":"Justin","salary":3500.0}
{"name":"Berta","salary":4000.0}
{"name":"Andy","salary":4500.0}

Spark UDF/UDAF(JAVA)的更多相关文章

  1. Spark 自定义函数(udf,udaf)

    Spark 版本 2.3 文中测试数据(json) {"name":"lillcol", "age":24,"ip":& ...

  2. Spark(十三)【SparkSQL自定义UDF/UDAF函数】

    目录 一.UDF(一进一出) 二.UDAF(多近一出) spark2.X 实现方式 案例 ①继承UserDefinedAggregateFunction,实现其中的方法 ②创建函数对象,注册函数,在s ...

  3. 45、sparkSQL UDF&UDAF

    一.UDF 1.UDF UDF:User Defined Function.用户自定义函数. 2.scala案例 package cn.spark.study.sql import org.apach ...

  4. spark2.1注册内部函数spark.udf.register("xx", xxx _),运行时抛出异常:Task not serializable

    函数代码: class MySparkJob{ def entry(spark:SparkSession):Unit={ def getInnerRsrp(outer_rsrp: Double, we ...

  5. Spark 用户自定义函数 Java 示例

    Spark UDF Java 示例 在这篇文章中提到了用Spark做用户昵称文本聚类分析,聚类需要选定K个中心点,然后迭代计算其他样本点到中心点的距离.由于中文文字分词之后(n-gram)再加上昵称允 ...

  6. Spark之UDAF

    import org.apache.spark.sql.{Row, SparkSession} import org.apache.spark.sql.expressions.{MutableAggr ...

  7. UDF/UDAF开发总结

    参考文章: https://www.cnblogs.com/itxuexiwang/p/6264547.html https://www.cnblogs.com/eRrsr/p/6096989.htm ...

  8. 在spark udf中读取hdfs上的文件

    某些场景下,我们在写UDF实现业务逻辑时候,可能需要去读取某个文件. 我们可以将此文件上传个hdfs某个路径下,然后通过hdfs api读取该文件,但是需要注意: UDF中读取文件部分最好放在静态代码 ...

  9. UserView--第二种方式(避免第一种方式Set饱和),基于Spark算子的java代码实现

      UserView--第二种方式(避免第一种方式Set饱和),基于Spark算子的java代码实现   测试数据 java代码 package com.hzf.spark.study; import ...

  10. UserView--第一种方式set去重,基于Spark算子的java代码实现

    UserView--第一种方式set去重,基于Spark算子的java代码实现 测试数据 java代码 package com.hzf.spark.study; import java.util.Ha ...

随机推荐

  1. NOIp2020复赛后的故事

    NOIp2020复赛后的故事 过渡 \(15\)号,学校开了一个竞赛生大会. 胡校长大致就是说了些鼓励我们的话,让我们放心大胆的去拼搏,别怕因为停课而落下了\(whk\). \(18\sim19\)号 ...

  2. 手把手教你网络爬虫(爬取豆瓣电影top250,附带源代码)

    概念 网络爬虫就是按照一定的规则,自动抓取互联网信息的程序或脚本.其本质就是模拟浏览器打开网页,获取网页中我们需要的数据. 基本流程 准备工作(构建流程) 获取数据 解析内容 保存数据 1. 准备工作 ...

  3. Etcd异地容灾方案

    一.方案选型 ETCD官网提供了一种实时镜像同步数据的工具mirror-maker,如果出现主机房服务挂掉可以通过切换域名的形式切换到灾备机房,这个过程数据是可以保持一致的. 注意:make-mirr ...

  4. 特殊恢复:最简单的BBED修改ASM的数据块的方法

    我们的文章会在微信公众号Oracle恢复实录和博客网站同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢! 由于博客中有大量代码,通过页面浏览效果更佳. 前天在客户现场遇 ...

  5. AgenticSeek - 完全本地的AI助手替代方案

    English | 中文 | 繁體中文 | Français | 日本語 | Português (Brasil) 100%本地运行的Manus AI替代品,支持语音的AI助手,可自主浏览网页.编写代 ...

  6. 如何在FastAPI中实现权限隔离并让用户乖乖听话?

    title: 如何在FastAPI中实现权限隔离并让用户乖乖听话? date: 2025/06/18 17:24:12 updated: 2025/06/18 17:24:12 author: cmd ...

  7. 一文搞定AB测试

    import numpy as npimport scipy.stats as statsimport pandas as pd'''场景:当已知总体标准差,对总体均值进行估计,用z检验'''def ...

  8. WinUI 3 支持的三种窗口 及 受限的窗口透明

    我的目标 希望能够熟悉 WinUI 3 窗口的基本使用方式,了解可能出现的问题 . WinUI 3 支持三种窗口模式,分别为:常规窗口模式.画中画模式.全屏模式. 窗口模式:常规 即我们最常见的普通窗 ...

  9. pycharm无法正常调试问题

    pycharm无法正常调试问题 1.错误代码 已连接到 pydev 调试器(内部版本号 231.8109.197)Traceback (most recent call last): File &qu ...

  10. Redis基于@Cacheable注解实现接口缓存

    说明 @Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的.也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法. 属性 ...