Scala 将UDF函数应用于Spark窗口,其中输入参数是范围内所有列值的列表
我想在窗口中的每一行上建立一个移动平均线。比方说-10排。但是如果可用行少于10行,我想在结果行->新建列中插入一个0。 因此,我将尝试在聚合窗口中使用UDF,该窗口具有输入参数List()(或任何超类),其中包含所有行的值 下面是一个不起作用的代码示例:Scala 将UDF函数应用于Spark窗口,其中输入参数是范围内所有列值的列表,scala,apache-spark,Scala,Apache Spark,我想在窗口中的每一行上建立一个移动平均线。比方说-10排。但是如果可用行少于10行,我想在结果行->新建列中插入一个0。 因此,我将尝试在聚合窗口中使用UDF,该窗口具有输入参数List()(或任何超类),其中包含所有行的值 下面是一个不起作用的代码示例: val w = Window.partitionBy("id").rowsBetween(-10, +0) dfRetail2.withColumn("test", udftestf(dfRetail2("salesMth")).over(w
val w = Window.partitionBy("id").rowsBetween(-10, +0)
dfRetail2.withColumn("test", udftestf(dfRetail2("salesMth")).over(w))
预期输出:如果没有更多行可用,则列出(1,2,3,4)
,并将其作为udf函数的输入参数。udf函数应返回计算值,如果可用行数少于10行,则返回0
上述代码终止:
表达式“UDF(salesMth#152L)”在窗口函数中不受支持
您可以在不需要UDF/UDAF的情况下,根据您的具体情况使用Spark的内置窗口功能以及when/other
。为简单起见,在以下使用虚拟数据的示例中,滑动窗口大小减小为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|
// +---+------+------------+----------+
import org.apache.spark.sql.functions_
导入org.apache.spark.sql.expressions.Window
导入spark.implicits_
val df=(1到2).flatMap(i=>序列表(8)(j=>(i,i*10.0+j)))。
toDF(“id”、“金额”)
val slidingWin=4
val winSpec=Window.partitionBy($“id”).rowsBetween(-(slidingWin-1),0)
df。
withColumn(“滑动计数”,计数($“金额”).over(winSpec))。
withColumn(“slidingAvg”,当($“slidingCount”
根据评论部分的评论,我将通过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
defmovingavg(n:Int)=udf{(ls:Seq[Double])=>
val(平均,计数)=ls.takeRight(n.foldLeft((0.0,1)){
案例((a,i,next)=>(a+(next-a)/i,i+1)
}
if(count您可以针对您的特定情况使用Spark的内置窗口函数以及when/other
,而无需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|
// +---+------+------------+----------+
import org.apache.spark.sql.functions_
导入org.apache.spark.sql.expressions.Window
导入spark.implicits_
val df=(1到2).flatMap(i=>序列表(8)(j=>(i,i*10.0+j)))。
toDF(“id”、“金额”)
val slidingWin=4
val winSpec=Window.partitionBy($“id”).rowsBetween(-(slidingWin-1),0)
df。
withColumn(“滑动计数”,计数($“金额”).over(winSpec))。
withColumn(“slidingAvg”,当($“slidingCount”
根据评论部分的评论,我将通过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
defmovingavg(n:Int)=udf{(ls:Seq[Double])=>
val(平均,计数)=ls.takeRight(n.foldLeft((0.0,1)){
案例((a,i,next)=>(a+(next-a)/i,i+1)
}
if(count)在浏览网络以获取答案时,我发现这是:“用户定义的聚合函数UDAF”,而不是UDF。可能就是这样。在浏览网络以获取答案时,我发现这是:“用户定义的聚合函数UDAF”而不是UDF。这可能是它。清晰明了的scala代码。成功了。谢谢!一个问题。我想它也适用于UDAF,但需要更多的代码。这个解决方案是否意味着我可以只使用UDF,而不需要使用UDAF?@Sven,实际上没有必要使用UDF或UDAF。请查看我的简化解决方案。T非常感谢。我必须承认,我的0示例太简单了(因为它超出了所问问题的范围)。我的评估有点复杂,所以我宁愿使用udf解决方案。我可以重新措辞吗?因此,使用udf解决方案,我是否可以使用UDAF。UDAF更复杂,因为您必须创建一个新的(案例)每个函数类。还是有一个我需要它们的用例?只是好奇而已。@Sven,请看我的扩展答案。清晰明了的scala代码。成功了。非常感谢!一个问题。我想它也适用于UDAF,但需要更多的代码。这个解决方案是否意味着我可以只使用UDF,而不需要费心使用它UDAF?@Sven,实际上不需要UDF或UDAF。请查看我的简化解决方案。非常感谢。我不得不承认我的0示例太简单了(因为它超出了所问问题的范围)。我的评估有点m