Apache spark 如何根据以前的连续行筛选出行?
我有一个要求,数据帧按col1(时间戳)排序,我需要按col2过滤 任何col2值小于前一行col2值的行,我都需要过滤掉该行。结果应该是col2值单调递增 请注意,这不仅仅是两行 例如,假设4行的col2的值为4,2,3,5。结果应为4,5,因为第二行和第三行都小于4(第一行值) 请注意:Apache spark 如何根据以前的连续行筛选出行?,apache-spark,apache-spark-sql,Apache Spark,Apache Spark Sql,我有一个要求,数据帧按col1(时间戳)排序,我需要按col2过滤 任何col2值小于前一行col2值的行,我都需要过滤掉该行。结果应该是col2值单调递增 请注意,这不仅仅是两行 例如,假设4行的col2的值为4,2,3,5。结果应为4,5,因为第二行和第三行都小于4(第一行值) 请注意: 第2行和第3行因其值小于第1行中的值而被过滤掉,即第4行 第5行的值小于第4行的值,即第6行,因此将其过滤掉 一般来说,有没有一种方法可以根据一行的值与前几行中的值的比较来筛选行?我认为您所追求的是运行
- 第2行和第3行因其值小于第1行中的值而被过滤掉,即第4行
- 第5行的值小于第4行的值,即第6行,因此将其过滤掉
一般来说,有没有一种方法可以根据一行的值与前几行中的值的比较来筛选行?我认为您所追求的是运行最大值()。这总是导致我使用窗口聚合
// I made the input a bit more tricky
val input = Seq(
(1,4), (2,2), (3,3), (4,5), (5, 1), (6, 9), (7, 6)
).toDF("timestamp", "value")
scala> input.show
+---------+-----+
|timestamp|value|
+---------+-----+
| 1| 4|
| 2| 2|
| 3| 3|
| 4| 5|
| 5| 1|
| 6| 9|
| 7| 6|
+---------+-----+
我的目标是实现以下预期结果。如果我错了,请纠正我
val expected = Seq((1,4), (4,5), (6, 9)).toDF("timestamp", "value")
scala> expected.show
+---------+-----+
|timestamp|value|
+---------+-----+
| 1| 4|
| 4| 5|
| 6| 9|
+---------+-----+
“运行”问题的诀窍是在定义窗口规范时使用rangeBetween
import org.apache.spark.sql.expressions.Window
val ts = Window
.orderBy("timestamp")
.rangeBetween(Window.unboundedPreceding, Window.currentRow)
使用WindowSpec,您可以从结果中过滤出想要去除的内容,然后就完成了
val result = input
.withColumn("running_max", max("value") over ts)
.where($"running_max" === $"value")
.select("timestamp", "value")
scala> result.show
18/05/29 22:09:18 WARN WindowExec: No Partition Defined for Window operation! Moving all data to a single partition, this can cause serious performance degradation.
+---------+-----+
|timestamp|value|
+---------+-----+
| 1| 4|
| 4| 5|
| 6| 9|
+---------+-----+
正如您所看到的,它的效率不是很高,因为它只使用一个分区(这会导致单线程执行不佳,因此与在一台机器上运行实验没有太大区别)
我认为我们可以对输入进行分区,计算部分运行的最大值,然后合并部分结果,再次运行运行的最大值计算。只是一个我自己还没有尝试过的想法。检查与运行最大值的相等性应该可以做到:
val input = Seq((1,4), (2,2), (3,3), (4,5), (5, 1), (6, 9), (7, 6)).toDF("timestamp", "value")
input.show()
+---------+-----+
|timestamp|value|
+---------+-----+
| 1| 4|
| 2| 2|
| 3| 3|
| 4| 5|
| 5| 1|
| 6| 9|
| 7| 6|
+---------+-----+
input
.withColumn("max",max($"value").over(Window.orderBy($"timestamp")))
.where($"value"===$"max").drop($"max")
.show()
+---------+-----+
|timestamp|value|
+---------+-----+
| 1| 4|
| 4| 5|
| 6| 9|
+---------+-----+
rangeBetween
是不需要的,如果窗口指定了顺序,它会自动使用“running”Maximum。我想我可能已经看到了Spark版本中需要它的地方,因此我将它作为安全网包括在内。它不会造成伤害,并且让事情变得更容易预测。好吧,但是我建议使用rowsBetween
使它更易于阅读。谢谢Jacek提供了详细的答案。还感谢您回答我关于分区警告的后续问题。运行max是关键。@JacekLaskowski它们基本上是不同的,rowsBetween
是在“行数”中设置一个帧,rangeBetween
使用其他度量来定义一个帧,例如,无论数据是如何采样的,用于计算10米以上的滑动平均值(在这种情况下,行数是动态计算的).
val input = Seq((1,4), (2,2), (3,3), (4,5), (5, 1), (6, 9), (7, 6)).toDF("timestamp", "value")
input.show()
+---------+-----+
|timestamp|value|
+---------+-----+
| 1| 4|
| 2| 2|
| 3| 3|
| 4| 5|
| 5| 1|
| 6| 9|
| 7| 6|
+---------+-----+
input
.withColumn("max",max($"value").over(Window.orderBy($"timestamp")))
.where($"value"===$"max").drop($"max")
.show()
+---------+-----+
|timestamp|value|
+---------+-----+
| 1| 4|
| 4| 5|
| 6| 9|
+---------+-----+