Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Apache spark Apache Spark SQL UDAF over窗口显示重复输入的奇怪行为_Apache Spark_Apache Spark Sql - Fatal编程技术网

Apache spark Apache Spark SQL UDAF over窗口显示重复输入的奇怪行为

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

我发现在Apache Spark SQL(版本2.2.0)中,当在窗口规范上使用的用户定义聚合函数(UDAF)提供了多行相同的输入时,UDAF(似乎)没有正确调用
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()
它将统计每个窗口中的元素数

  • 窗口
    wA
    每1510000001000行有4个元素,每15100000700000行有6个元素
  • 对于
    wB
    ,当没有
    orderBy
    时,每个分区的所有行都包含在窗口中,因此所有窗口都有6个元素
  • 最后一个
    wC
    指定行的选择,因此不会对为哪个窗口选择哪些行留下歧义。第一行只有1个元素,所有后续行的窗口中只有2个元素。这会产生正确的结果

今天我也学到了一些新的东西:)

解决这个问题的一个方法是确保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)
只需通过一段数据就可以实现这一目的!