在具有特定状态的行上分配时间段(列值)

时间:2017-05-23 00:25:13

标签: apache-spark pyspark apache-spark-sql spark-dataframe pyspark-sql

我有一个包含日志的Pyspark数据框,每一行对应于记录时系统的状态,以及一个组号。我想找出每个群体处于不健康状态的时间段的长度。

例如,如果这是我的表:

TIMESTAMP | STATUS_CODE | GROUP_NUMBER
--------------------------------------
02:03:11  | healthy     | 000001
02:03:04  | healthy     | 000001
02:03:03  | unhealthy   | 000001
02:03:00  | unhealthy   | 000001
02:02:58  | healthy     | 000008
02:02:57  | healthy     | 000008
02:02:55  | unhealthy   | 000001
02:02:54  | healthy     | 000001
02:02:50  | healthy     | 000007
02:02:48  | healthy     | 000004

我想要返回组000001,其不健康的时间段为9秒(从02:02:55到02:03:04)。

其他群体也可能有不健康的时间段,我也希望将其归还。

由于连续行具有相同状态的可能性,并且由于不同组的行被散布,我正在努力寻找一种有效地执行此操作的方法。

我无法将Pyspark数据帧转换为Pandas数据帧,因为它太大了。

如何有效地确定这些时间段的长度?

非常感谢!

2 个答案:

答案 0 :(得分:1)

使用spark-sql解决方案的pyspark看起来像这样。

首先,我们创建示例数据集。除了数据集之外,我们还在组上生成row_number字段分区,并按时间戳排序。然后我们将生成的数据帧注册为表table1

from pyspark.sql.window import Window
from pyspark.sql.functions import row_number
from pyspark.sql.functions import unix_timestamp

df = spark.createDataFrame([
('2017-01-01 02:03:11','healthy','000001'),
('2017-01-01 02:03:04','healthy','000001'),
('2017-01-01 02:03:03','unhealthy','000001'),
('2017-01-01 02:03:00','unhealthy','000001'),
('2017-01-01 02:02:58','healthy','000008'),
('2017-01-01 02:02:57','healthy','000008'),
('2017-01-01 02:02:55','unhealthy','000001'),
('2017-01-01 02:02:54','healthy','000001'),
('2017-01-01 02:02:50','healthy','000007'),
('2017-01-01 02:02:48','healthy','000004')
],['timestamp','state','group_id'])

df = df.withColumn('rownum', row_number().over(Window.partitionBy(df.group_id).orderBy(unix_timestamp(df.timestamp))))

df.registerTempTable("table1")

将数据框注册为表(table1)。可以使用spark-sql

按如下方式计算所需数据
>>> spark.sql("""
... SELECT t1.group_id,sum((t2.timestamp_value - t1.timestamp_value)) as duration
... FROM
... (SELECT unix_timestamp(timestamp) as timestamp_value,group_id,rownum FROM table1 WHERE state = 'unhealthy') t1
... LEFT JOIN
... (SELECT unix_timestamp(timestamp) as timestamp_value,group_id,rownum FROM table1) t2
... ON t1.group_id = t2.group_id
... AND t1.rownum = t2.rownum - 1
... group by t1.group_id
... """).show()
+--------+--------+
|group_id|duration|
+--------+--------+
|  000001|       9|
+--------+--------+

示例日期集仅包含group_id 00001的不健康数据。但是这个解决方案适用于其他具有不健康状态的group_ids。

答案 1 :(得分:0)

一种直接的方式(可能不是最佳的)是:

  1. 使用GROUP_NUMBER作为密钥K
  2. 映射到[K,V]
  3. 使用repartitionAndSortWithinPartitions,这样您就可以获得同一分区中每个组的所有数据,并按TIMESTAMP排序。详细解释如何工作在这个答案:Pyspark: Using repartitionAndSortWithinPartitions with multiple sort Critiria
  4. 最后使用mapPartitions在单个分区中获取排序数据的迭代器,这样您就可以轻松找到所需的答案。 (mapPartitions的解释:How does the pyspark mapPartitions function work?