Spark:UDF执行多次


9

我有一个带有以下代码的数据框:

def test(lat: Double, lon: Double) = {
  println(s"testing ${lat / lon}")
  Map("one" -> "one", "two" -> "two")
}

val testUDF = udf(test _)

df.withColumn("test", testUDF(col("lat"), col("lon")))
  .withColumn("test1", col("test.one"))
  .withColumn("test2", col("test.two"))

现在检查日志,我发现每行UDF执行3次。如果我从“ test.three”列中添加“ test3”,则将再次执行UDF。

有人可以解释我为什么吗?

是否可以正确避免这种情况(即使添加了“测试”,也无需缓存数据框,即使这可行)?


你什么意思?您要调用测试函数三次。这就是为什么它被执行了三次。不知道为什么要使用UDF。为什么不只是将Map设为val?
user4601931

这只是显示火花行为的一个示例。对我来说,“ test”是一个包含结构的新列,然后访问该结构的任何部分都不应再次执行UDF。我怎么了
Rolintocour

我尝试打印模式,“ test”的数据类型Map不是Struct。现在,如果UDF返回的是诸如Test(一个String,两个:String)之类的case类,则test它实际上不是Struct,而是返回的UDF的执行次数总是很多。
Rolintocour


缓存应根据以下答案进行工作:stackoverflow.com/a/40962714/1138523
Raphael Roth,

Answers:


5

如果要避免多次调用udf(这很有用,尤其是在udf是您工作的瓶颈的情况下),可以按以下步骤进行:

val testUDF = udf(test _).asNondeterministic()

基本上,您告诉Spark您的函数不是确定性的,现在Spark确保只调用一次,因为多次调用它并不安全(每次调用可能返回不同的结果)。

另请注意,此技巧并非免费,通过这样做,您会对优化器施加一些约束,例如,其副作用是Spark优化器不会通过不确定的表达式推送过滤器,因此您需要负责优化过滤条件在查询中的位置。


真好!这个问题的答案也属于这里:stackoverflow.com/questions/40320563/...
拉斐尔·罗斯

就我而言,asNondeterministic强制UDF仅执行一次。使用该explode(array(myUdf($"id")))解决方案,它仍然可以执行两次。
Rolintocour
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.