折叠火花柱时避免中间计算

时间:2018-02-21 20:38:44

标签: scala apache-spark fold

我正在寻找创建一个需要一些数字序列和一个火花列的函数,然后高效计算一个列表中匹配最多的新列,默认为0没有匹配。

例如,取序列[500,100,1]。

天真的方法是为序列手动编写以下内容:

val manualAdjustment = (c: Column) => 
    when(c.isNull, -1)
      .when(c > 500, 500)
      .when(c > 100, 100)
      .when(c > 1, 1)
      .otherwise(0)

当我们提供一个列时,例如lit(1),我们会得到简洁明了的计划:

CASE WHEN (1 IS NULL) THEN -1 WHEN (1 > 500) THEN 500 WHEN (1 > 100) THEN 100 WHEN (1 > 1) THEN 1 ELSE 0 END.

如果给定一个列和一个有序整数的任意序列,而不是手动写出每个序列的先前代码,那么组成同一个计划的函数会更好。

这导致我编写以下函数:

val makeRange: (Column, Seq[Int]) => Column = (col: Column, range: Seq[Int]) => {
  val whenFunction = (c: Column, condition: Column, value: Int) => 
       c.when(condition, value)
  val reduced: Column => Column = 
       range.map(i => (column: Column) => whenFunction(column, column > i, i))
            .reduce(_ compose _)
  reduced(when(col.isNull, -1)).otherwise(0)
}

此函数将每个int映射到基于列值大于int的when条件,因此至少在理论上它执行与上面manualAdjustment函数相同的操作。但是,makeRange(lit(1), Seq(500, 100, 1))的输出是:

CASE WHEN (1 IS NULL) THEN -1 WHEN (CASE WHEN (1 IS NULL) THEN -1 END > 500) THEN 500 WHEN (CASE WHEN (1 IS NULL) THEN -1 WHEN (CASE WHEN (1 IS NULL) THEN -1 END > 500) THEN 500 END > 100) THEN 100 WHEN (CASE WHEN (1 IS NULL) THEN -1 WHEN (CASE WHEN (1 IS NULL) THEN -1 END > 500) THEN 500 WHEN (CASE WHEN (1 IS NULL) THEN -1 WHEN (CASE WHEN (1 IS NULL) THEN -1 END > 500) THEN 500 END > 100) THEN 100 END > 1) THEN 1 ELSE 0 END

这个计划至少比另一个计划更冗长,但似乎效率低得多,因为它(貌似)必须一遍又一遍地计算相同的条件。我的理论是,当条件需要评估前一步骤时,计算何时条件并进一步应用。在折叠中,这需要一遍又一遍地计算相同的条件。

有关为何发生这种情况的任何见解,或者如何编写类似于makeRange的函数,该函数创建的计划与manualAdjustment一样简单?

1 个答案:

答案 0 :(得分:1)

这似乎与您的manualAdjustment创建了相同的计划:

val makeRange = (c: Column, range:Seq[Int]) =>
  range.foldLeft(when(c.isNull, -1))((acc: Column, curr: Int) => acc.when(c>curr,curr)).otherwise(0)

== Physical Plan ==
LocalTableScan [CASE WHEN (i IS NULL) THEN -1 WHEN (i > 500) THEN 500 WHEN (i > 100) THEN 100 WHEN (i > 1) THEN 1 ELSE 0 END#10]