如何在pyspark中聚合连续行
我有大量的用户数据——数十亿行,我需要总结每个用户在特定状态下花费的时间 假设它是历史web数据,我想计算每个用户在站点上花费的时间。数据仅说明用户是否在场如何在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| +---
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|
+----+----------+