Apache spark Apache Spark SQL UDAF over窗口显示重复输入的奇怪行为
我发现在Apache Spark SQL(版本2.2.0)中,当在窗口规范上使用的用户定义聚合函数(UDAF)提供了多行相同的输入时,UDAF(似乎)没有正确调用Apache spark Apache Spark SQL UDAF over窗口显示重复输入的奇怪行为,apache-spark,apache-spark-sql,Apache Spark,Apache Spark Sql,我发现在Apache Spark SQL(版本2.2.0)中,当在窗口规范上使用的用户定义聚合函数(UDAF)提供了多行相同的输入时,UDAF(似乎)没有正确调用evaluate方法 我已经能够在Java和Scala中本地和集群上重现这种行为。下面的代码显示了一个示例,其中,如果行在前一行的1秒之内,则将其标记为false class ExampleUDAF(val timeLimit: Long) extends UserDefinedAggregateFunction { def det
evaluate
方法
我已经能够在Java和Scala中本地和集群上重现这种行为。下面的代码显示了一个示例,其中,如果行在前一行的1秒之内,则将其标记为false
class ExampleUDAF(val timeLimit: Long) extends UserDefinedAggregateFunction {
def deterministic: Boolean = true
def inputSchema: StructType = StructType(Array(StructField("unix_time", LongType)))
def dataType: DataType = BooleanType
def bufferSchema = StructType(Array(
StructField("previousKeepTime", LongType),
StructField("keepRow", BooleanType)
))
def initialize(buffer: MutableAggregationBuffer) = {
buffer(0) = 0L
buffer(1) = false
}
def update(buffer: MutableAggregationBuffer, input: Row) = {
if (buffer(0) == 0L) {
buffer(0) = input.getLong(0)
buffer(1) = true
} else {
val timeDiff = input.getLong(0) - buffer.getLong(0)
if (timeDiff < timeLimit) {
buffer(1) = false
} else {
buffer(0) = input.getLong(0)
buffer(1) = true
}
}
}
def merge(buffer1: MutableAggregationBuffer, buffer2: Row) = {} // Not implemented
def evaluate(buffer: Row): Boolean = buffer.getBoolean(1)
}
val timeLimit = 1000 // 1 second
val udaf = new ExampleUDAF(timeLimit)
val window = Window
.orderBy(column("unix_time"))
.partitionBy(column("category"))
val df = spark.createDataFrame(Arrays.asList(
Row(1510000001000L, "a", true),
Row(1510000001000L, "a", false),
Row(1510000001000L, "a", false),
Row(1510000001000L, "a", false),
Row(1510000700000L, "a", true),
Row(1510000700000L, "a", false)
), new StructType().add("unix_time", LongType).add("category", StringType).add("expected_result", BooleanType))
df.withColumn("actual_result", udaf(column("unix_time")).over(window)).show
我正确理解Spark在窗口规范上使用时的UDAF行为?如果没有,谁能提供这方面的见解。如果我对UDAF在windows上的行为的理解是正确的,这可能是Spark中的错误吗?多谢各位 UDAF的一个问题是,它没有指定要在哪些行上使用
rowsBetween()
运行窗口。如果没有rowsbeween()
规范,则对于每一行,窗口函数将在当前行之前和之后获取所有行,包括当前行(在给定类别中)。因此,所有行的实际结果
基本上只考虑示例中最后两行的DataFrame
,unix\u时间=15100000700000,这将有效地返回所有行的false
使用窗口
这样的声明:
Window.partitionBy(col("category")).orderBy(col("unix_time")).rowsBetween(-1L, 0L)
+-------------+--------+---------------+-------------+
| unix_time|category|expected_result|actual_result|
+-------------+--------+---------------+-------------+
|1510000001000| a| false| true|
|1510000001000| a| false| false|
|1510000001000| a| false| false|
|1510000001000| a| true| false|
|1510000700000| a| true| true|
|1510000700000| a| false| false|
+-------------+--------+---------------+-------------+
您总是只查看前一行和当前行。前一行先取。这将创建正确的输出。但是,由于具有相同unix\u时间的行的顺序不是唯一的,因此无法预测具有相同unix\u时间的行中哪一行将具有值true
结果可能如下所示:
Window.partitionBy(col("category")).orderBy(col("unix_time")).rowsBetween(-1L, 0L)
+-------------+--------+---------------+-------------+
| unix_time|category|expected_result|actual_result|
+-------------+--------+---------------+-------------+
|1510000001000| a| false| true|
|1510000001000| a| false| false|
|1510000001000| a| false| false|
|1510000001000| a| true| false|
|1510000700000| a| true| true|
|1510000700000| a| false| false|
+-------------+--------+---------------+-------------+
更新
进一步研究后,似乎在提供了
orderBy
列时,所有元素都位于当前行+当前行之前。并不是所有的分区元素都像我之前说的那样。此外,如果orderBy列
“包含重复值”窗口中的每个重复行将包含所有重复值。通过执行以下操作,您可以清楚地看到它:
val wA = Window.partitionBy(col("category")).orderBy(col("unix_time"))
val wB = Window.partitionBy(col("category"))
val wC = Window.partitionBy(col("category")).orderBy(col("unix_time")).rowsBetween(-1L, 0L)
df.withColumn("countRows", count(col("unix_time")).over(wA)).show()
df.withColumn("countRows", count(col("unix_time")).over(wB)).show()
df.withColumn("countRows", count(col("unix_time")).over(wC)).show()
它将统计每个窗口中的元素数
- 窗口
每1510000001000行有4个元素,每15100000700000行有6个元素wA
- 对于
,当没有wB
时,每个分区的所有行都包含在窗口中,因此所有窗口都有6个元素orderBy
- 最后一个
指定行的选择,因此不会对为哪个窗口选择哪些行留下歧义。第一行只有1个元素,所有后续行的窗口中只有2个元素。这会产生正确的结果wC
今天我也学到了一些新的东西:)解决这个问题的一个方法是确保Spark UDAFs的所有输入都是唯一的,即没有重复的。其他方法是第一个计算
行数(
)(按unix\u时间排序),然后将此行号
用作窗口规范中的排序列,而不是unix\u时间
。问题似乎是顺序没有明确定义。你能解释一下为什么df.withColumn(“rnb”,row\u number()。over(Window.orderBy($“unix\u time”))))。withColumn(“实际结果”,udaf($“unix\u time”)。over(Window.orderBy($“rnb”)。show
给出了预期的结果吗?@RaphaelRoth问得好。尝试这三个窗口val wA=Window.partitionBy(col(“category”).orderBy(col(“unix_time”)
val wB=Window.partitionBy(col(“category”)val wC=Window.partitionBy(col(“category”).orderBy(col(“unix_time”)df.withColumn(“countRows”,count(col(“unix_时间”)).over(wB)).show()
df.withColumn(“countRows”,count(col(“unix_时间”)).over(wC)).show()
@RaphaelRoth似乎如果在orderBy
列中有非唯一值,它会将它们全部放在每个非唯一行的窗口中。我不知道!很高兴知道!此外,如果没有orderBy,它将获取给定类别的所有值。如果指定了orderBy,则它将占用前面的所有行。也很高兴知道。我会更新我的答案!但是仍然在(-1L,0L)之间提供.rowsBetween(<1L,0L)
只需通过一段数据就可以实现这一目的!