如何在Java/Kotlin中创建返回复杂类型的Spark UDF?
我正在尝试编写一个返回复杂类型的UDF:如何在Java/Kotlin中创建返回复杂类型的Spark UDF?,java,apache-spark,kotlin,user-defined-functions,Java,Apache Spark,Kotlin,User Defined Functions,我正在尝试编写一个返回复杂类型的UDF: private val toPrice = UDF1<String, Map<String, String>> { s -> val elements = s.split(" ") mapOf("value" to elements[0], "currency" to elements[1]) } val type = DataTypes.createStructType(listOf(
private val toPrice = UDF1<String, Map<String, String>> { s ->
val elements = s.split(" ")
mapOf("value" to elements[0], "currency" to elements[1])
}
val type = DataTypes.createStructType(listOf(
DataTypes.createStructField("value", DataTypes.StringType, false),
DataTypes.createStructField("currency", DataTypes.StringType, false)))
df.sqlContext().udf().register("toPrice", toPrice, type)
我得到一个神秘的错误:
原因:org.apache.spark.SparkException:无法执行用户定义的函数($anonfun$28:(字符串)=>struct)
位于org.apache.spark.sql.catalyst.expressions.GeneratedClass$GenerateEditorForCodeGenStage1.processNext(未知源)
位于org.apache.spark.sql.execution.BufferedRowIterator.hasNext(BufferedRowIterator.java:43)
位于org.apache.spark.sql.execution.whisttagecodegenexec$$anonfun$10$$anon$1.hasNext(whisttagecodegenexec.scala:614)
位于org.apache.spark.sql.execution.SparkPlan$$anonfun$2.apply(SparkPlan.scala:253)
位于org.apache.spark.sql.execution.SparkPlan$$anonfun$2.apply(SparkPlan.scala:247)
位于org.apache.spark.rdd.rdd$$anonfun$mapPartitionsInternal$1$$anonfun$apply$25.apply(rdd.scala:830)
位于org.apache.spark.rdd.rdd$$anonfun$mapPartitionsInternal$1$$anonfun$apply$25.apply(rdd.scala:830)
在org.apache.spark.rdd.MapPartitionsRDD.compute上(MapPartitionsRDD.scala:38)
在org.apache.spark.rdd.rdd.computeOrReadCheckpoint(rdd.scala:324)
位于org.apache.spark.rdd.rdd.iterator(rdd.scala:288)
在org.apache.spark.rdd.MapPartitionsRDD.compute上(MapPartitionsRDD.scala:38)
在org.apache.spark.rdd.rdd.computeOrReadCheckpoint(rdd.scala:324)
位于org.apache.spark.rdd.rdd.iterator(rdd.scala:288)
位于org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87)
位于org.apache.spark.scheduler.Task.run(Task.scala:109)
位于org.apache.spark.executor.executor$TaskRunner.run(executor.scala:345)
位于java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
位于java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
运行(Thread.java:748)
原因:scala.MatchError:{value=138.0,currency=USD}(属于java.util.LinkedHashMap类)
在org.apache.spark.sql.catalyst.CatalystTypeConverters$StructConverter.toCatalystImpl(CatalystTypeConverters.scala:236)上
在org.apache.spark.sql.catalyst.CatalystTypeConverters$StructConverter.toCatalystImpl(CatalystTypeConverters.scala:231)上
位于org.apache.spark.sql.catalyst.CatalystTypeConverters$CatalystTypeConverter.toCatalyst(CatalystTypeConverters.scala:103)
在org.apache.spark.sql.CatalystTypeConverters$$anonfun$createToCatalystConverters$2.apply上(CatalystTypeConverters.scala:379)
... 还有19个
我尝试使用自定义数据类型:
class Price(val value: Double, val currency: String) : Serializable
使用返回该类型的UDF:
private val toPrice = UDF1<String, Price> { s ->
val elements = s.split(" ")
Price(elements[0].toDouble(), elements[1])
}
private val toPrice=UDF1{s->
val元素=s.split(“”)
价格(元素[0]。toDouble(),元素[1])
}
但是我得到了另一个MatchError
,它抱怨Price
类型
如何正确地编写可以返回复杂类型的UDF?它很简单。转到并查找相应的类型
在Spark 2.3中
- 如果将返回类型声明为
,则函数必须返回StructType
org.apache.spark.sql.Row
- 如果返回
函数,则返回类型应为Map
——显然不是您想要的MapType
- TL;DR该函数应该返回一个类为org.apache.spark.sql.Row的对象
Spark提供了两种主要的
UDF
定义变体
udf
使用Scala反射的变体:
def-udf[RT](f:()⇒ RT)(隐式arg0:TypeTag[RT]):UserDefinedFunction
def-udf[RT,A1](f:(A1)⇒ RT)(隐式arg0:TypeTag[RT],arg1:TypeTag[A1]):UserDefinedFunction
def-udf[RT,A1,A2,…,A10](f:(A1,A2,…,A10)⇒ (隐式arg0:TypeTag[RT],arg1:TypeTag[A1],arg2:TypeTag[A2],…,arg10:TypeTag[A10])
case类价格(值:双精度,币种:字符串)
val df=序号(“1美元”)。toDF(“价格”)
val toPrice=udf((s:String)=>scala.util.Try{
s拆分(“”)匹配{
案例数组(价格,货币)=>price(price.toDouble,货币)
}
}.toOption)
df.select(toPrice($“价格”)).show
// +----------+
//|自定义项(价格)|
// +----------+
//|[1.0美元]|
// +----------+
在这个变量中,返回类型是自动编码的
由于它依赖于反射,此变体主要针对Scala用户udf
提供模式定义的变体(此处使用的变体)。此变量的返回类型应与Dataset[Row]
的返回类型相同:
- 正如在另一个答案中指出的,您只能使用中列出的类型(装箱或未装箱的原子类型,
/java.sql.Timestamp
,以及高级集合)java.sql.Date
- 复杂结构(
/structs
)使用StructTypes
表示。不允许与代数数据类型或等效数据类型混合。例如(Scala代码) 不是 或者任何混合变体,比如org.apache.spark.sql.Row
import org.apache.spark.sql.types_
导入org.apache.spark.sql.functions.udf
导入org.apache.spark.sql.Row
val schema=StructType(Seq(
StructField(“值”,双重类型,false),
StructField(“货币”,StringType,false)
))
val toPrice=udf((s:String)=>scala.util.Try{
s拆分(“”)匹配{
案例数组(价格、货币)
private val toPrice = UDF1<String, Price> { s ->
val elements = s.split(" ")
Price(elements[0].toDouble(), elements[1])
}
struct<_1:int,_2:struct<_1:string,_2:struct<_1:double,_2:int>>>
Row(1, Row("foo", Row(-1.0, 42))))
(1, ("foo", (-1.0, 42))))
Row(1, Row("foo", (-1.0, 42))))
def createDataFrame(rows: List[Row], schema: StructType): DataFrame
def createDataFrame[A <: Product](data: Seq[A])(implicit arg0: TypeTag[A]): DataFrame
import org.apache.spark.sql.functions._
df.withColumn("price", struct(
split($"price", " ")(0).cast("double").alias("price"),
split($"price", " ")(1).alias("currency")
))