Apache spark 带条件的Pypark窗口函数

Apache spark 带条件的Pypark窗口函数,apache-spark,pyspark,apache-spark-sql,Apache Spark,Pyspark,Apache Spark Sql,假设我有一个事件的数据帧,每行之间有时差,主要规则是,如果只有事件在前一个或下一个事件的5分钟内,则一次访问被计算在内: +--------+-------------------+--------+ |userid | eventtime | timeDiff| +--------+-------------------+--------+ |37397e29 | 2017-06-04 03:00:00 | 60| |37397e29 | 2017-06-04 03:01:00 | 60| |

假设我有一个事件的数据帧,每行之间有时差,主要规则是,如果只有事件在前一个或下一个事件的5分钟内,则一次访问被计算在内:

+--------+-------------------+--------+
|userid | eventtime | timeDiff|
+--------+-------------------+--------+
|37397e29 | 2017-06-04 03:00:00 | 60|
|37397e29 | 2017-06-04 03:01:00 | 60|
|37397e29 | 2017-06-04 03:02:00 | 60|
|37397e29 | 2017-06-04 03:03:00 | 180|
|37397e29 | 2017-06-04 03:06:00 | 60|
|37397e29 | 2017-06-04 03:07:00 | 420|
|37397e29 | 2017-06-04 03:14:00 | 60|
|37397e29 | 2017-06-04 03:15:00 | 1140|
|37397e29 | 2017-06-04 03:34:00 | 540|
|37397e29 | 2017-06-04 03:53:00 | 540|
+--------+----------------- -+--------+
挑战是根据最新事件时间的开始时间和结束时间分组,条件是在5分钟内。输出应如下表所示:

+--------+-------------------+--------------------+-----------+
|用户ID |开始时间|结束时间|事件|
+--------+-------------------+--------------------+-----------+
|37397e29 | 2017-06-04 03:00:00 | 2017-06-04 03:07:00 | 6|
|37397e29 | 2017-06-04 03:14:00 | 2017-06-04 03:15:00 | 2|
+--------+-------------------+--------------------+-----------+
到目前为止,我已经使用了窗口滞后函数和一些条件,但是,我不知道从这里可以走到哪里:

%spark.pyspark
从pyspark.sql导入函数为F
从pyspark.sql导入窗口作为W
从pyspark.sql.functions导入col
windowSpec=W.partitionBy(结果_-poi[“用户ID”]、结果_-poi[“唯一参考编号”])。orderBy(结果_-poi[“事件时间”])
windowSpecDesc=W.partitionBy(结果_-poi[“userid”]、结果_-poi[“唯一_-reference\u-number”]).orderBy(结果_-poi[“eventtime”].desc())
#窗口位于当前行和下一行之间。e、 g:下午3:00和3:03
nextEventTime=F.lag(col(“eventtime”),-1).over(windowSpec)
#窗口位于当前行和下一行之间。e、 g:下午3:00和3:03
previousEventTime=F.lag(col(“eventtime”),1).over(windowSpec)
differenceventtime=nexteventime-col(“事件时间”)
nextTimeDiff=F.coalesce((F.unix_时间戳(nextEventTime))
-F.unix_时间戳('eventtime')),F.lit(0))
previousTimeDiff=F.coalesce((F.unix_时间戳('eventtime')-F.unix_时间戳(previousEventTime)),F.lit(0))
#检查下一个POI是否等于当前POI,且时差是否小于5分钟。
验证=F.聚结((nextTimeDiff<300)|(previousTimeDiff<300)),F.发光(假))
#将True更改为1
visitCheck=F.coalesce((验证==True).cast(“int”),F.lit(1))
结果点带列(“访问检查”,访问检查)。带列(“下一个TTIMEDIFF”,下一个TTIMEDIFF)。选择(“用户ID”、“事件时间”、“下一个TTIMEDIFF”、“访问检查”)。订购人(“事件时间”)
我的问题:这是一种可行的方法吗?如果是,我如何“前进”并查看满足5分钟条件的最大事件时间。据我所知,遍历Spark SQL列的值,这可能吗?是不是太贵了?。有没有其他方法可以达到这个结果

由@Aku建议的解决方案结果

+--------+--------+---------------------+---------------------+------+
|用户ID |子组|开始时间|结束时间|事件|
+--------+--------+--------+------------+---------------------+------+
|37397e29 | 0 | 2017-06-04 03:00:00.0 | 2017-06-04 03:06:00.0 | 5|
|37397e29 | 1 | 2017-06-04 03:07:00.0 | 2017-06-04 03:14:00.0 | 2|
|37397e29 | 2 | 2017-06-04 03:15:00.0 | 2017-06-04 03:15:00.0 | 1|
|37397e29 | 3 | 2017-06-04 03:34:00.0 | 2017-06-04 03:43:00.0 | 2|
+------------------------------------+-----------------------+-------+

它没有给出预期的结果。3:07-3:14和03:34-03:43被计算为5分钟内的范围,不应该是这样。此外,3:07应该是第一行的结束时间,因为它在前一行3:06的5分钟内。

方法可以根据时间线标准对数据帧进行分组

您可以创建一个数据帧,其中的行打破了5分钟的时间线。 这些行是对记录和数据进行分组的标准 该行将为每个组设置开始时间和结束时间


然后找到每个组的计数和最大时间戳(endtime)。

因此,如果我正确理解这一点,您基本上希望在TimeDiff>300时结束每个组?对于滚动窗口功能,这似乎相对简单:

首先是一些进口商品

from pyspark.sql.window import Window
import pyspark.sql.functions as func
然后设置windows,我假设您将按用户ID进行分区

w = Window.partitionBy("userid").orderBy("eventtime")
然后通过首先标记每个组的第一个成员,然后对列求和,找出每个观察值属于哪个亚组

indicator = (TimeDiff > 300).cast("integer")
subgroup = func.sum(indicator).over(w).alias("subgroup")
然后是一些聚合函数,您应该完成

DF = DF.select("*", subgroup)\
.groupBy("subgroup")\
.agg(
    func.min("eventtime").alias("start_time"),
    func.max("eventtime").alias("end_time"),
    func.count(func.lit(1)).alias("events")
)

您需要一个额外的窗口函数和一个
groupby
来实现这一点。 我们想要的是,timeDiff大于300的每一行都是一个组的结束和一个新组的开始。Aku的解决方案应该有效,只有指标标志着一个团队的开始而不是结束。要改变这一点,您必须将累计总和增加到n-1,而不是n(n是您的当前行):

w=Window.partitionBy(“userid”).orderBy(“eventtime”)
DF=DF.withColumn(“指示器”),(DF.timeDiff>300.cast(“int”))
DF=DF.带列(“子组”,函数和(“指标”)。在(w)上-函数列(“指标”))
DF=DF.groupBy(“子组”).agg(
func.min(“事件时间”).alias(“开始时间”),
函数最大值(“事件时间”).别名(“结束时间”),
函数计数(“*”)别名(“事件”)
)
+--------+-------------------+-------------------+------+
|子组|开始时间|结束时间|事件|
+--------+-------------------+-------------------+------+
|       0|2017-06-04 03:00:00|2017-06-04 03:07:00|     6|
|       1|2017-06-04 03:14:00|2017-06-04 03:15:00|     2|
|