我有一个设备的行,并且我希望将依次发生的所有相同事件分组。
我也想用pyspark做到这一点
因此,给出以下内容:
+--------------------+-------+
| datetime | event |
+--------------------+-------+
| 12-02-18T08:20:00 | 1 |
| 12-02-18T08:25:00 | 1 |
| 12-02-18T08:30:00 | 1 |
| 12-02-18T09:00:00 | 2 |
| 12-02-18T09:05:00 | 2 |
| 12-02-18T09:10:00 | 1 |
| 12-02-18T09:15:00 | 1 |
+--------------------+-------+
我想得出以下结论:
+-------------------+-------------------+-------+
| start_time | end_time | event |
+-------------------+-------------------+-------+
| 12-02-18T08:20:00 | 12-02-18T09:00:00 | 1 |
| 12-02-18T09:00:00 | 12-02-18T09:10:00 | 2 |
| 12-02-18T09:10:00 | null | 1 |
+-------------------+-------------------+-------+
不会有重叠事件,因此不需要考虑。我曾教过使用UDF进行此操作,但想知道是否有人知道一种更优雅/更有效的方法。
答案 0 :(得分:2)
使用Florian提供的方法(窗口函数),可以通过在事件中获取具有已更改事件的行,然后在Scala上获取下一个更改的日期来实现:
val df = List(
("12-02-18T08:20:00", 1),
("12-02-18T08:25:00", 1),
("12-02-18T08:30:00", 1),
("12-02-18T09:00:00", 2),
("12-02-18T09:05:00", 2),
("12-02-18T09:10:00", 1),
("12-02-18T09:15:00", 1)
).toDF("datetime", "event")
df.show(false)
val w = Window.orderBy("datetime")
val changedRowsOnlyDF = df.withColumn("changed", $"event" =!= lag($"event", 1, 0).over(w))
.where($"changed")
val result = changedRowsOnlyDF
.withColumn("end_time", lead($"datetime", 1).over(w))
.drop("changed")
.withColumnRenamed("datetime", "start_time")
result.show(false)
输出:
+-----------------+-----+-----------------+
|start_time |event|end_time |
+-----------------+-----+-----------------+
|12-02-18T08:20:00|1 |12-02-18T09:00:00|
|12-02-18T09:00:00|2 |12-02-18T09:10:00|
|12-02-18T09:10:00|1 |null |
+-----------------+-----+-----------------+
免责声明:这种方法可用于少量数据,并通过消息通知Spark:
WARN org.apache.spark.sql.execution.window.WindowExec:未为窗口操作定义分区!将所有数据移至单个分区,这可能会导致严重的性能下降。
答案 1 :(得分:1)
您可以使用UDF
而不是使用Window
来查找事件的过渡,并从中创建一个新列以用于分组(请参见this answer)。然后,我们可以汇总找到每个事件的最小和最大时间。下面给出一个可行的示例,希望对您有所帮助!
import pyspark.sql.functions as F
from pyspark.sql import Window
# SAMPLE DATA
df = spark.sparkContext.parallelize([
('2018-07-20T01:00:00.000Z','1'),
('2018-07-20T02:00:00.000Z','1'),
('2018-07-20T03:00:00.000Z','2'),
('2018-07-20T04:00:00.000Z','2'),
('2018-07-20T05:00:00.000Z','1')
]).toDF(("datetime","event" ))
# CALCULATE START AND END TIMES
w = Window.orderBy('datetime')
df_result = (df
.withColumn("changed", (F.col('event') != F.lag('event', 1, 0).over(w)).cast('int'))
.withColumn("group_id", F.sum("changed").over(w)).drop("changed")
.groupBy('group_id','event').agg(
F.min('datetime').alias('start'),
F.max('datetime').alias('end'))
.drop('group_id'))
df_result.show()
输出:
+-----+--------------------+--------------------+
|event| start| end|
+-----+--------------------+--------------------+
| 1|2018-07-20T01:00:...|2018-07-20T02:00:...|
| 2|2018-07-20T03:00:...|2018-07-20T04:00:...|
| 1|2018-07-20T05:00:...|2018-07-20T05:00:...|
+-----+--------------------+--------------------+