如何在pyspark中聚合连续行

如何在pyspark中聚合连续行,pyspark,pyspark-sql,Pyspark,Pyspark Sql,我有大量的用户数据——数十亿行,我需要总结每个用户在特定状态下花费的时间 假设它是历史web数据,我想计算每个用户在站点上花费的时间。数据仅说明用户是否在场 df = spark.createDataFrame([("A", 1), ("A", 2), ("A", 3),("B", 4 ),("B", 5 ),("A", 6 ),("A", 7 ),("A", 8 )], ["user","timestamp"]) +----+---------+ |user|timestamp| +---

我有大量的用户数据——数十亿行,我需要总结每个用户在特定状态下花费的时间

假设它是历史web数据,我想计算每个用户在站点上花费的时间。数据仅说明用户是否在场

df = spark.createDataFrame([("A", 1), ("A", 2), ("A",  3),("B", 4 ),("B", 5 ),("A", 6 ),("A", 7 ),("A", 8 )], ["user","timestamp"])

+----+---------+
|user|timestamp|
+----+---------+
|   A|        1|
|   A|        2|
|   A|        3|
|   B|        4|
|   B|        5|
|   A|        6|
|   A|        7|
|   A|        8|
+----+---------+
正确的答案是这样的,因为我是对每个连续段的总数求和

+----+---------+
|user|   ttl   |
+----+---------+
|   A|        4|
|   B|        1|
+----+---------+
我试着做了一个最大-最小和群比,但结果a段是8-1,给出了错误的答案

在sqlite中,我能够通过创建一个分区号,然后找到差异并求和来得到答案。我用这个创建了分区

SELECT
COUNT(*) FILTER (WHERE a.user <>
  ( SELECT b.user
    FROM foobar AS b
    WHERE a.timestamp > b.timestamp
    ORDER BY b.timestamp DESC
    LIMIT 1
  ))  
    OVER (ORDER BY timestamp) c,
user,
timestamp
FROM foobar a;
然后,sql中的最后一个优先函数就可以轻松完成

有没有关于如何在pyspark中扩展和实现此功能的想法?我似乎找不到足够的替代品来代替伯爵。。。sqlite提供了

我们可以做到:

创建数据帧 为每一行分配一个行号,按时间戳排序。使用列虚拟,以便我们可以使用窗口函数row_number。 我们想在这里的每个用户组中创建一个子组。 1对于每个用户组,计算当前行的行数与前一行的行数之差。因此,任何大于1的差值都表示存在一个新的连续组。这会产生不同的结果,请注意,每组中的第一行的值为-1

2然后,我们将null分配给diff==1的每一行。此结果列为diff2

3接下来,我们使用最后一个函数使用diff2列中的最后一个非null值用diff2==null填充行。这将导致子组ID

这是我们要为每个用户组创建的子组

w2 = Window.partitionBy('user').orderBy('timestamp')
df = df.withColumn('diff', df['row_number'] - F.lag('row_number').over(w2)).fillna(-1)
df = df.withColumn('diff2', F.when(df['diff']==1, None).otherwise(F.abs(df['diff'])))
df = df.withColumn('subgroupid', F.last(F.col('diff2'), True).over(w2))
df.show()

+----+---------+-----+----------+----+-----+----------+
|user|timestamp|dummy|row_number|diff|diff2|subgroupid|
+----+---------+-----+----------+----+-----+----------+
|   B|        4|    1|         4|  -1|    1|         1|
|   B|        5|    1|         5|   1| null|         1|
|   A|        1|    1|         1|  -1|    1|         1|
|   A|        2|    1|         2|   1| null|         1|
|   A|        3|    1|         3|   1| null|         1|
|   A|        6|    1|         6|   3|    3|         3|
|   A|        7|    1|         7|   1| null|         3|
|   A|        8|    1|         8|   1| null|         3|
+----+---------+-----+----------+----+-----+----------+

现在,我们根据用户和子ID进行分组,以计算每个用户在每个连续时间间隔上花费的时间。 最后,我们按用户分组,仅汇总每个用户花费的总时间

s = "(max('timestamp') - min('timestamp'))"
df = df.groupBy(['user', 'subgroupid']).agg(eval(s))
s = s.replace("'","")
df = df.groupBy('user').sum(s).select('user', F.col("sum(" + s + ")").alias('total_time'))
df.show()

+----+----------+
|user|total_time|
+----+----------+
|   B|         1|
|   A|         4|
+----+----------+



感觉你应该可以在这里使用窗口功能。。
df = df.withColumn('dummy', F.lit(1))
w1 = Window.partitionBy('dummy').orderBy('timestamp')
df = df.withColumn('row_number', F.row_number().over(w1))
df.show()

+----+---------+-----+----------+
|user|timestamp|dummy|row_number|
+----+---------+-----+----------+
|   A|        1|    1|         1|
|   A|        2|    1|         2|
|   A|        3|    1|         3|
|   B|        4|    1|         4|
|   B|        5|    1|         5|
|   A|        6|    1|         6|
|   A|        7|    1|         7|
|   A|        8|    1|         8|
+----+---------+-----+----------+

w2 = Window.partitionBy('user').orderBy('timestamp')
df = df.withColumn('diff', df['row_number'] - F.lag('row_number').over(w2)).fillna(-1)
df = df.withColumn('diff2', F.when(df['diff']==1, None).otherwise(F.abs(df['diff'])))
df = df.withColumn('subgroupid', F.last(F.col('diff2'), True).over(w2))
df.show()

+----+---------+-----+----------+----+-----+----------+
|user|timestamp|dummy|row_number|diff|diff2|subgroupid|
+----+---------+-----+----------+----+-----+----------+
|   B|        4|    1|         4|  -1|    1|         1|
|   B|        5|    1|         5|   1| null|         1|
|   A|        1|    1|         1|  -1|    1|         1|
|   A|        2|    1|         2|   1| null|         1|
|   A|        3|    1|         3|   1| null|         1|
|   A|        6|    1|         6|   3|    3|         3|
|   A|        7|    1|         7|   1| null|         3|
|   A|        8|    1|         8|   1| null|         3|
+----+---------+-----+----------+----+-----+----------+

s = "(max('timestamp') - min('timestamp'))"
df = df.groupBy(['user', 'subgroupid']).agg(eval(s))
s = s.replace("'","")
df = df.groupBy('user').sum(s).select('user', F.col("sum(" + s + ")").alias('total_time'))
df.show()

+----+----------+
|user|total_time|
+----+----------+
|   B|         1|
|   A|         4|
+----+----------+