第1关:Spark SQL自定义函数(UDF)
编程要求
- 文件路径:
/data/workspace/myshixun/step1/student.txt
; - 依据左侧的知识点介绍,处理
student.txt
文件:- 依据
english
打上标签“分数<70”、“70<分数<80”、“分数>80”; - 将
subject
列中的分隔符统一为“/”。
- 依据
student.txt
文件内容如下:
studentID,name,sex,english,subject
20220411,张三,m,65,Python Hadoop MySQL HBase
20220412,王五,f,70,机器学习、数据挖掘 Python
20220413,李四,m,72,JavaWeb/Java/HTML/JQuery
20220414,韩梅梅,f,78,大数据基础与应用/Linux/Hadoop、Java
20220415,李雷,m,85,模具设计、C++
20220416,马冬梅,f,88,Java、HTML/JavaWeb/MySQL
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{Dataset, SparkSession}
/********** Begin **********/
object createUDF {
def main(args: Array[String]): Unit = {
//创建spark对象
val spark = SparkSession.builder().master("local[*]").appName("createUDF").getOrCreate()
//创建上下文对象
val sc: SparkContext = spark.sparkContext
//导入隐式转换
import spark.implicits._
//读取文件,跳过首行,转换成dataframe
val data_txt = sc.textFile("/data/workspace/myshixun/step1/student.txt").map(x => {
val line = x.split(","); (line(0), line(1), line(2), line(3), line(4))})
//去除首行
val firstline = data_txt.first()
val df = data_txt.filter(_ != firstline).toDF("studentID","name","sex","english","subject")
//展示数据
df.show(false)
//注册自定义函数
spark.udf.register("transform",(x:String)=>{
if (x.contains("、")){
x.replace("、","/")
}else if(x.contains(" ")){
x.replace(" ","/")
}else{
x
}
})
spark.udf.register("scoreTag",(x:Int)=>{
if (x<70){
"分数<70"
}else if (70<x && x<80){
"70<分数<80"
}else{
"分数>80"
}
})
//注册临时视图
df.createOrReplaceTempView("df")
//使用自定义函数
spark.sql("select name,scoreTag(english) as scoretag,transform(subject) as subject from df".stripMargin).show(false)
//关闭资源
spark.close()
}
}
/********** End **********/
第2关:Spark SQL自定义聚合函数(UDAF)
知识点
用户在自定义聚合函数时,须要继承 Aggregator[IN,BUF,OUT]
抽象类,还必须重写这个类中的zero、reduce、merge、finish、bufferEncoder、outputEncoder 这几个函数。
Aggregator 中的类型介绍:
-
IN : 聚合的输入类型
-
BUF : 减少的中间值的输入类型
-
OUT : 最终输出结果的类型
对于 Aggregator 类中重写这几个函数的介绍如下所示:
-
zero : BUF 聚合的中间结果的初始值。
-
reduce(b: BUF,a: IN) : BUF 将输入值聚合a为当前中间值。为了性能,该函数可能会修改b并返回它,而不是构造一个新对象。
-
merge(b1: BUF, b2: BUF) : BUF 合并两个中间值。
-
finish(reduction: BUF): OUT 最终的结果输出。
-
bufferEncoder:Encoder[BUF] 指定中间值类型的编码器。
-
outputEncoder: Encoder[OUT] 指定最终输出值类型的编码器。
编程要求
使用自定义聚合函数求取学科最优成绩。 文件路径如下:/data/workspace/myshixun/step2/salary.txt
。 salary.txt
部分文件内容如下:
Cendy,3000
Andy,4500
Justin,3500
Mike,4000
......
import org.apache.spark.sql.{Encoder, Encoders, SparkSession, functions}
import org.apache.spark.sql.expressions.Aggregator
import org.apache.log4j.{Level, Logger}
/********** Begin **********/
object createUDAF {
//定义聚合缓冲器类型
case class Buff(var salary:Long)
//创建自定义函数
class MyMaxFunction extends Aggregator[Long,Buff,Long]{
//初始化
override def zero: Buff = Buff(Integer.MIN_VALUE)
//局部计算逻辑
override def reduce(acc: Buff, sal: Long): Buff = {
if (sal>acc.salary){acc.salary=sal}
acc
}
//合并计算结果
override def merge(acc1: Buff, acc2: Buff): Buff = {
if (acc1.salary>acc2.salary) {acc1}
else {acc2}
}
//综合计算
override def finish(reduction: Buff): Long = reduction.salary
//缓冲区的编码设置
override def bufferEncoder: Encoder[Buff] = Encoders.product
//输出的编码设置
override def outputEncoder: Encoder[Long] = Encoders.scalaLong
}
def main(args: Array[String]): Unit = {
//设置日志级别
Logger.getLogger("org").setLevel(Level.ERROR)
//创建spark对象
val spark = SparkSession.builder().appName("strongUDAF")
.config("spark.testing.memory", "2147480000").master("local[*]").getOrCreate()
//导入隐式转换
import spark.implicits._
//读取数据
val data1 = spark.read.textFile("/data/workspace/myshixun/step2/salary.txt").filter(row=>row!=null && row.length>0).map(x=>{
val line = x.split(",");
(line(0),line(1).toLong)
}).toDF("name","salary")
//创建临时视图
data1.createOrReplaceTempView("df")
//注册函数
val mymaxfunc = new MyMaxFunction
spark.udf.register("MaxFunc",functions.udaf(mymaxfunc))
//使用函数
spark.sql("select MaxFunc(salary) from df").show(false)
//关闭资源
spark.close()
}
}
/********** End **********/