如何使用orElse组成的部分函数作为spark中的udf

时间:2016-10-07 20:48:19

标签: scala apache-spark

正如问题所述,我想使用由orElse组成的部分函数作为spark中的udf。这是一个可以在spark shell中运行的例子:

val df = sc.parallelize(1 to 15).toDF("num")
df.show

//Testing out a normal udf - this works
val gt5: (Int => String) = num => (num > 5).toString
val gt5Udf = udf(gt5)
df.withColumn("gt5", gt5Udf(col("num"))).show

//Now create a udf of a partial function composed with orElse
val baseline: PartialFunction[Int, String] = { case _ => "baseline" }
val ge3: PartialFunction[Int, String] = { case x if x >= 3 => ">=3" }
val ge7: PartialFunction[Int, String] = { case x if x >= 7 => ">=7" }
val ge12: PartialFunction[Int, String] = { case x if x >= 12 => ">=12" }

val composed: PartialFunction[Int, String] = ge12 orElse ge7 orElse ge3 orElse baseline
val composedUdf = udf(composed)

//This fails (but this is what I'd like to do)
df.withColumn("pf", composedUdf(col("num"))).show

//Use a partial function not composed with orElse - this works
val baselineUdf = udf(baseline)
df.withColumn("pf", baselineUdf(col("num"))).show 

我目前在具有以下配置的三节点独立群集上运行此功能:

  • spark:1.6.0
  • hdfs:2.4.1
  • scala:2.10.5

我在这个答案中找到了我认为的线索:Why Scala can serialize Function but not PartialFunction?

所以我试过了:

scala> composed.isInstanceOf[Serializable]
res: Boolean = false

scala> composedUdf.isInstanceOf[Serializable]
res: Boolean = true

scala> baseline.isInstanceOf[Serializable]
res: Boolean = true

scala> baselineUdf.isInstanceOf[Serializable]
res: Boolean = true

我在这里变得模糊,但似乎用orElse组成部分函数会删除序列化?

我认为最具信息性的错误是:

org.apache.spark.SparkException: Task not serializable
...
Caused by: java.io.NotSerializableException: scala.PartialFunction$OrElse
...

我该如何解决?还是我离开基地?

提前感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

如果将其抬起并将其包裹在另一个功能中,它应该可以工作。

val composed: Int => Option[String] = 
  x => (ge12 orElse ge7 orElse ge3 orElse baseline).lift.apply(x)

答案 1 :(得分:1)

虽然这并没有直接解决您的问题,但我想建议并使用SQL函数替代解决方案。

首先,您必须导入所需的功能:

import org.apache.spark.sql.functions.{when, lit}

和一些implicits为了简洁起见:

import sqlContext.implicits._

接下来,您可以表达与代码中相同的条件:

val baseline = lit("baseline")
val ge3 = when($"num" >= 3,  ">=3")
val ge7 = when($"num" >= 7, ">=7")
val ge12 = when($"num" >= 12, ">=12")

val composed = ge12 otherwise (ge7 otherwise (ge3 otherwise baseline))

在这种形式下,它稍微不那么优雅,但您可以毫不费力地使用标准集合API(foldLeft / foldRight)和unlike UDFs来构建这样的表达式,结果可以是由Catalyst Optimizer优化。