我有一个要求,即数据框按col1(时间戳)排序,我需要按col2过滤。
任何行,其中col2值小于前一行的col2值,我需要过滤掉该行。结果应该是单调增加col2值。
请注意,这不仅仅是两行。
例如,假设4行的col2值为4,2,3,5。结果应为4,5,因为第2行和第3行都小于4(第一行值)。
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|
+---------+-----+
请注意:
一般来说,有没有办法根据一行的值与前一行中的值进行比较来过滤行?
答案 0 :(得分:1)
我认为你所追求的是最大值(在running total之后)。这总是让我使用窗口聚合。
// 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)
使用窗口规范,您可以过滤掉想要从结果中删除的内容,然后就完成了。
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|
+---------+-----+
正如您所看到的那样效率不高,因为它只使用一个分区(导致单线程执行不佳,因此与在一台机器上运行实验没有多大差别)。
我认为我们可以对输入进行分区,然后部分计算运行最大值,然后将部分结果联合起来并再次运行运行最大值计算。只是一个想法,我没有尝试过自己。
答案 1 :(得分:1)
检查与运行最大值的相等性应该可以解决问题:
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|
+---------+-----+