如果在SPARK函数中使用UDF或UDAF

Spark目前已经内置的函数参见:

Spark 1.5 DataFrame API Highlights: Date/Time/String Handling, Time Intervals, and UDAFs

如果在SPARK函数中使用UDF或UDAF, 详见示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package cn.com.systex
import scala.reflect.runtime.universe
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.annotation.DeveloperApi
import org.apache.spark.annotation.Experimental
import org.apache.spark.sql.functions.callUDF
import org.apache.spark.sql.functions.lit
import org.apache.spark.sql.functions.udf
import org.apache.spark.sql.types.ArrayType
import org.apache.spark.sql.types.StringType
import java.sql.Timestamp
import java.sql.Date
import org.apache.spark.sql.expressions.UserDefinedAggregateFunction
import org.apache.spark.sql.expressions.MutableAggregationBuffer
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.DateType
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types.DoubleType
import org.apache.spark.sql.types.StructField
/**
* DateTime: 2015年12月25日 上午10:41:42
*
*/
//定义一个日期范围类
case class DateRange(startDate : Timestamp, endDate : Timestamp) {
def in(targetDate : Date) : Boolean = {
targetDate.before(endDate) && targetDate.after(startDate)
}
override def toString() : String = {
startDate.toLocaleString() + " " + endDate.toLocaleString();
}
}
//定义UDAF函数,按年聚合后比较,需要实现UserDefinedAggregateFunction中定义的方法
class YearOnYearCompare(current : DateRange) extends UserDefinedAggregateFunction {
val previous : DateRange = DateRange(subtractOneYear(current.startDate), subtractOneYear(current.endDate))
println(current)
println(previous)
//UDAF与DataFrame列有关的输入样式,StructField的名字并没有特别要求,完全可以认为是两个内部结构的列名占位符。
//至于UDAF具体要操作DataFrame的哪个列,取决于调用者,但前提是数据类型必须符合事先的设置,如这里的DoubleType与DateType类型
def inputSchema : StructType = {
StructType(StructField( "metric" , DoubleType) :: StructField( "timeCategory" , DateType) :: Nil)
}
//定义存储聚合运算时产生的中间数据结果的Schema
def bufferSchema : StructType = {
StructType(StructField( "sumOfCurrent" , DoubleType) :: StructField( "sumOfPrevious" , DoubleType) :: Nil)
}
//标明了UDAF函数的返回值类型
def dataType : org.apache.spark.sql.types.DataType = DoubleType
//用以标记针对给定的一组输入,UDAF是否总是生成相同的结果
def deterministic : Boolean = true
//对聚合运算中间结果的初始化
def initialize(buffer : MutableAggregationBuffer) : Unit = {
buffer.update( 0 , 0.0 )
buffer.update( 1 , 0.0 )
}
//第二个参数input: Row对应的并非DataFrame的行,而是被inputSchema投影了的行。以本例而言,每一个input就应该只有两个Field的值
def update(buffer : MutableAggregationBuffer, input : Row) : Unit = {
if (current.in(input.getAs[Date]( 1 ))) {
buffer( 0 ) = buffer.getAs[Double]( 0 ) + input.getAs[Double]( 0 )
}
if (previous.in(input.getAs[Date]( 1 ))) {
buffer( 1 ) = buffer.getAs[Double]( 0 ) + input.getAs[Double]( 0 )
}
}
//负责合并两个聚合运算的buffer,再将其存储到MutableAggregationBuffer中
def merge(buffer 1 : MutableAggregationBuffer, buffer 2 : Row) : Unit = {
buffer 1 ( 0 ) = buffer 1 .getAs[Double]( 0 ) + buffer 2 .getAs[Double]( 0 )
buffer 1 ( 1 ) = buffer 1 .getAs[Double]( 1 ) + buffer 2 .getAs[Double]( 1 )
}
//完成对聚合Buffer值的运算,得到最后的结果
def evaluate(buffer : Row) : Any = {
if (buffer.getDouble( 1 ) == 0.0 ) {
0.0
} else {
(buffer.getDouble( 0 ) - buffer.getDouble( 1 )) / buffer.getDouble( 1 ) * 100
}
}
private def subtractOneYear(date : Timestamp) : Timestamp = {
val prev = new Timestamp(date.getTime)
prev.setYear(prev.getYear - 1 )
prev
}
}
/**
* Spark 1.5 DataFrame API Highlights: Date/Time/String Handling, Time Intervals, and UDAFs
* https://databricks.com/blog/2015/09/16/spark-1-5-dataframe-api-highlights-datetimestring-handling-time-intervals-and-udafs.html
*/
object SimpleDemo {
def main(args : Array[String]) : Unit = {
val dir = "D:/Program/spark/examples/src/main/resources/" ;
val sc = new SparkContext( new SparkConf().setMaster( "local[4]" ).setAppName( "sqltest" ))
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
import sqlContext.implicits. _
//用$符号来包裹一个字符串表示一个Column,定义在SQLContext对象implicits中的一个隐式转换
//DataFrame的API可以接收Column对象,UDF的定义不能直接定义为Scala函数,而是要用定义在org.apache.spark.sql.functions中的udf方法来接收一个函数。
//这种方式无需register
//如果需要在函数中传递一个变量,则需要org.apache.spark.sql.functions中的lit函数来帮助
//创建DataFrame
val df = sqlContext.createDataFrame(Seq(
( 1 , "张三峰" , "广东 广州 天河" , 24 ),
( 2 , "李四" , "广东 广州 东山" , 36 ),
( 3 , "王五" , "广东 广州 越秀" , 48 ),
( 4 , "赵六" , "广东 广州 海珠" , 29 ))).toDF( "id" , "name" , "addr" , "age" )
//定义函数
def splitAddrFunc : String = > Seq[String] = {
_ .toLowerCase.split( "\\s" )
}
val longLength = udf((str : String, length : Int) = > str.length > length)
val len = udf((str : String) = > str.length)
//使用函数
val df 2 = df.withColumn( "addr-ex" , callUDF(splitAddrFunc, new ArrayType(StringType, true ), df( "addr" )))
val df 3 = df 2 .withColumn( "name-len" , len($ "name" )).filter(longLength($ "name" , lit( 2 )))
println( "打印DF Schema及数据处理结果" )
df.printSchema()
df 3 .printSchema()
df 3 .foreach { println }
//SQL模型
//定义普通的scala函数,然后在SQLContext中进行注册,就可以在SQL中使用了。
def slen(str : String) : Int = str.length
def slengthLongerThan(str : String, length : Int) : Boolean = str.length > length
sqlContext.udf.register( "len" , slen _ )
sqlContext.udf.register( "longLength" , slengthLongerThan _ )
df.registerTempTable( "user" )
println( "打印SQL语句执行结果" )
sqlContext.sql( "select name,len(name) from user where longLength(name,2)" ).foreach(println)
println( "打印数据过滤结果" )
df.filter( "longLength(name,2)" ).foreach(println)
//如果定义UDAF(User Defined Aggregate Function)
//Spark为所有的UDAF定义了一个父类UserDefinedAggregateFunction。要继承这个类,需要实现父类的几个抽象方法
val salesDF = sqlContext.createDataFrame(Seq(
( 1 , "Widget Co" , 1000.00 , 0.00 , "AZ" , "2014-01-02" ),
( 2 , "Acme Widgets" , 2000.00 , 500.00 , "CA" , "2014-02-01" ),
( 3 , "Widgetry" , 1000.00 , 200.00 , "CA" , "2015-01-11" ),
( 4 , "Widgets R Us" , 5000.00 , 0.0 , "CA" , "2015-02-19" ),
( 5 , "Ye Olde Widgete" , 4200.00 , 0.0 , "MA" , "2015-02-18" ))).toDF( "id" , "name" , "sales" , "discount" , "state" , "saleDate" )
salesDF.registerTempTable( "sales" )
val current = DateRange(Timestamp.valueOf( "2015-01-01 00:00:00" ), Timestamp.valueOf( "2015-12-31 00:00:00" ))
//在使用上,除了需要对UDAF进行实例化之外,与普通的UDF使用没有任何区别
val yearOnYear = new YearOnYearCompare(current)
sqlContext.udf.register( "yearOnYear" , yearOnYear)
val dataFrame = sqlContext.sql( "select yearOnYear(sales, saleDate) as yearOnYear from sales" )
salesDF.printSchema()
dataFrame.show()
}
}
分享到:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值