我想在窗口的每一行上建立移动平均线。假设有-10行。但是如果少于10行可用,我想在结果行->新列中插入0。 因此,我想实现的目标是在具有输入参数List()(或任何超类)的聚合窗口中使用UDF,该参数具有所有可用行的值。
这是一个不起作用的代码示例:
val w = Window.partitionBy("id").rowsBetween(-10, +0)
dfRetail2.withColumn("test", udftestf(dfRetail2("salesMth")).over(w))
Expected output: List( 1,2,3,4)
(如果没有更多行可用),并将其作为udf函数的输入参数。 udf函数应返回一个计算值,如果可用行少于10行,则应返回0。
以上代码终止:Expression 'UDF(salesMth#152L)' not supported within a window function.;;
答案 0 :(得分:0)
您可以将Spark的内置Window函数与when/otherwise
一起用于特定条件,而无需使用UDF / UDAF。为简单起见,在下面的示例中,使用伪数据将滑动窗口的大小减小为4:
import org.apache.spark.sql.functions._
import org.apache.spark.sql.expressions.Window
import spark.implicits._
val df = (1 to 2).flatMap(i => Seq.tabulate(8)(j => (i, i * 10.0 + j))).
toDF("id", "amount")
val slidingWin = 4
val winSpec = Window.partitionBy($"id").rowsBetween(-(slidingWin - 1), 0)
df.
withColumn("slidingCount", count($"amount").over(winSpec)).
withColumn("slidingAvg", when($"slidingCount" < slidingWin, 0.0).
otherwise(avg($"amount").over(winSpec))
).show
// +---+------+------------+----------+
// | id|amount|slidingCount|slidingAvg|
// +---+------+------------+----------+
// | 1| 10.0| 1| 0.0|
// | 1| 11.0| 2| 0.0|
// | 1| 12.0| 3| 0.0|
// | 1| 13.0| 4| 11.5|
// | 1| 14.0| 4| 12.5|
// | 1| 15.0| 4| 13.5|
// | 1| 16.0| 4| 14.5|
// | 1| 17.0| 4| 15.5|
// | 2| 20.0| 1| 0.0|
// | 2| 21.0| 2| 0.0|
// | 2| 22.0| 3| 0.0|
// | 2| 23.0| 4| 21.5|
// | 2| 24.0| 4| 22.5|
// | 2| 25.0| 4| 23.5|
// | 2| 26.0| 4| 24.5|
// | 2| 27.0| 4| 25.5|
// +---+------+------------+----------+
在评论部分的每句话中,我包括以下通过UDF提供的解决方案:
def movingAvg(n: Int) = udf{ (ls: Seq[Double]) =>
val (avg, count) = ls.takeRight(n).foldLeft((0.0, 1)){
case ((a, i), next) => (a + (next-a)/i, i + 1)
}
if (count <= n) 0.0 else avg // Expand/Modify this for specific requirement
}
// To apply the UDF:
df.
withColumn("average", movingAvg(slidingWin)(collect_list($"amount").over(winSpec))).
show
请注意,与sum
或count
不同的是,collect_list
会忽略rowsBetween()
并生成分区的数据,这些数据可能很大,要传递给UDF(因此需要takeRight()
)。如果计算出的窗口sum
和count
足以满足您的特定要求,请考虑将它们传递给UDF。
通常,特别是如果手头的数据已经是DataFrame格式,则使用内置的DataFrame API来利用Spark的执行引擎优化要比使用用户定义的UDF / UDAF更好,并且其性能和扩展性更好。您可能对阅读article re:DataFrame / Dataset API相对于UDF / UDAF的优势感兴趣。