避免折叠火花列中的中间计算



我正在创建一个函数,该函数采用一些数字和火花列,然后有效地计算一个新列,其最高数字从该列中匹配列表,如果不匹配,则默认为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的条件时的条件时,因此至少在理论上,它执行与上面的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一样简单?

这似乎创建了与您的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]

最新更新