Apache spark 在Spark UDF中操作数据帧

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

我有一个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(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()