Apache spark 在Spark UDF中操作数据帧
我有一个UDF,它从数据帧中过滤和选择值,但它遇到了“对象不可序列化”错误。详情如下 假设我有一个数据帧df1,它的列具有名称(“ID”、“Y1”、“Y2”、“Y3”、“Y4”、“Y5”、“Y6”、“Y7”、“Y8”、“Y9”、“Y10”)。我希望根据另一个数据帧df2中匹配的“ID”和“Value”对“Y”列的子集求和。我尝试了以下方法:Apache spark 在Spark UDF中操作数据帧,apache-spark,dataframe,apache-spark-sql,spark-dataframe,Apache Spark,Dataframe,Apache Spark Sql,Spark Dataframe,我有一个UDF,它从数据帧中过滤和选择值,但它遇到了“对象不可序列化”错误。详情如下 假设我有一个数据帧df1,它的列具有名称(“ID”、“Y1”、“Y2”、“Y3”、“Y4”、“Y5”、“Y6”、“Y7”、“Y8”、“Y9”、“Y10”)。我希望根据另一个数据帧df2中匹配的“ID”和“Value”对“Y”列的子集求和。我尝试了以下方法: val y_list = ("Y1", "Y2", "Y3", "Y4", "Y5", "Y6", "Y7", "Y8", "Y9", "Y10").map
val y_list = ("Y1", "Y2", "Y3", "Y4", "Y5", "Y6", "Y7", "Y8", "Y9", "Y10").map(c => col(c))
def udf_test(ID: String, value: Int): Double = {
df1.filter($"ID" === ID).select(y_list:_*).first.toSeq.toList.take(value).foldLeft(0.0)(_+_)
}
sqlContext.udf.register("udf_test", udf_test _)
val df_result = df2.withColumn("Result", callUDF("udf_test", $"ID", $"Value"))
这给了我形式上的错误:
java.io.NotSerializableException: org.apache.spark.sql.Column
Serialization stack:
- object not serializable (class: org.apache.spark.sql.Column, value: Y1)
我查了一下,发现Spark列是不可序列化的。我想知道:
1) 有没有办法操纵UDF中的数据帧
2) 如果没有,实现上述操作类型的最佳方法是什么?我的真实情况比这更复杂。它要求我根据大数据帧中的某些列从多个小数据帧中选择值,然后将值计算回大数据帧
我使用的是Spark 1.6.3。谢谢 不能在UDF中使用数据集操作。UDF只能对现有列进行手动填充并生成一个结果列。它不能过滤数据集或进行聚合,但可以在过滤器内部使用。UDAF还可以聚合值 相反,您可以使用
.as[SomeCaseClass]
从DataFrame生成数据集,并在filter、map和reduce中使用普通的强类型函数
编辑:如果要将bigDF与smallDFs列表中的每个小DF合并,可以执行以下操作:
import org.apache.spark.sql.functions._
val bigDF = // some processing
val smallDFs = Seq(someSmallDF1, someSmallDF2)
val joined = smallDFs.foldLeft(bigDF)((acc, df) => acc.join(broadcast(df), "join_column"))
broadcast
是一个向小型DF添加广播提示的函数,因此小型DF将使用更有效的广播连接,而不是排序合并连接1)不,您只能在UDF中使用普通scala代码
2) 如果您正确解释了代码,您可以通过以下方式实现目标:
df2
.join(
df1.select($"ID",y_list.foldLeft(lit(0))(_ + _).as("Result")),Seq("ID")
)
Dataset
在Spark 1.6.3中是实验性的,所以我不建议使用them@RaphaelRoth是的,这只是一个关于新功能的建议。谢谢你的反馈。我决定将我的小型DFs转换为地图来解决这个问题。在我的案例中,加入不是很理想,因为确切的逻辑比我描述的简单案例要复杂得多。谢谢你的到来1)。至于2),我决定将我的小型DFs转换为地图来解决这个问题。在我的案例中,连接不是很理想,因为确切的逻辑比我描述的简单案例复杂得多。
import org.apache.spark.sql.functions._
val events = Seq (
(1,1,2,3,4),
(2,1,2,3,4),
(3,1,2,3,4),
(4,1,2,3,4),
(5,1,2,3,4)).toDF("ID","amt1","amt2","amt3","amt4")
var prev_amt5=0
var i=1
def getamt5value(ID:Int,amt1:Int,amt2:Int,amt3:Int,amt4:Int) : Int = {
if(i==1){
i=i+1
prev_amt5=0
}else{
i=i+1
}
if (ID == 0)
{
if(amt1==0)
{
val cur_amt5= 1
prev_amt5=cur_amt5
cur_amt5
}else{
val cur_amt5=1*(amt2+amt3)
prev_amt5=cur_amt5
cur_amt5
}
}else if (amt4==0 || (prev_amt5==0 & amt1==0)){
val cur_amt5=0
prev_amt5=cur_amt5
cur_amt5
}else{
val cur_amt5=prev_amt5 + amt2 + amt3 + amt4
prev_amt5=cur_amt5
cur_amt5
}
}
val getamt5 = udf {(ID:Int,amt1:Int,amt2:Int,amt3:Int,amt4:Int) =>
getamt5value(ID,amt1,amt2,amt3,amt4)
}
myDF.withColumn("amnt5", getamt5(myDF.col("ID"),myDF.col("amt1"),myDF.col("amt2"),myDF.col("amt3"),myDF.col("amt4"))).show()