【问题标题】:How to use lag and rangeBetween functions on timestamp values?如何在时间戳值上使用 lag 和 rangeBetween 函数?
【发布时间】:2018-01-13 20:38:04
【问题描述】:

我的数据如下所示:

userid,eventtime,location_point
4e191908,2017-06-04 03:00:00,18685891
4e191908,2017-06-04 03:04:00,18685891
3136afcb,2017-06-04 03:03:00,18382821
661212dd,2017-06-04 03:06:00,80831484
40e8a7c3,2017-06-04 03:12:00,18825769

如果在同一location_point 的 5 分钟窗口内有 2 个或更多userid,我想添加一个新的布尔列,该列标记为 true。我有一个想法,使用lag 函数在由userid 分区的窗口上查找,范围介于当前时间戳和接下来的 5 分钟之间:

from pyspark.sql import functions as F
from pyspark.sql import Window as W
from pyspark.sql.functions import col

days = lambda i: i * 60*5 

windowSpec = W.partitionBy(col("userid")).orderBy(col("eventtime").cast("timestamp").cast("long")).rangeBetween(0, days(5))

lastURN = F.lag(col("location_point"), 1).over(windowSpec)
visitCheck = (last_location_point == output.location_pont)
output.withColumn("visit_check", visitCheck).select("userid","eventtime", "location_pont", "visit_check")

当我使用 RangeBetween 函数时,这段代码给了我一个分析异常:

AnalysisException: u'Window Frame RANGE BETWEEN CURRENT ROW AND 1500 FOLLOWING 必须匹配所需的帧 ROWS BETWEEN 1 PRECEDING 和 1 上一个;

你知道解决这个问题的方法吗?

【问题讨论】:

    标签: apache-spark pyspark apache-spark-sql window-functions


    【解决方案1】:

    鉴于您的数据:

    让我们添加一个以秒为单位的时间戳列:

    df = df.withColumn('timestamp',df_taf.eventtime.astype('Timestamp').cast("long"))
    df.show()
    
    +--------+-------------------+--------------+----------+
    |  userid|          eventtime|location_point| timestamp|  
    +--------+-------------------+--------------+----------+
    |4e191908|2017-06-04 03:00:00|      18685891|1496545200|
    |4e191908|2017-06-04 03:04:00|      18685891|1496545440|
    |3136afcb|2017-06-04 03:03:00|      18382821|1496545380|
    |661212dd|2017-06-04 03:06:00|      80831484|1496545560|
    |40e8a7c3|2017-06-04 03:12:00|      18825769|1496545920|
    |4e191908|2017-06-04 03:11:30|      18685891|1496545890|
    +--------+-------------------+--------------+----------+  
    

    现在,让我们定义一个窗口函数,按 location_point 分区,按时间戳排序,范围在 -300 秒和当前时间之间。我们可以计算这个窗口中的元素数量,并将这些数据放在名为“occurrences in_5_min”的列中:

    w = Window.partitionBy('location_point').orderBy('timestamp').rangeBetween(-60*5,0)
    df = df.withColumn('occurrences_in_5_min',F.count('timestamp').over(w))
    df.show()
    
    +--------+-------------------+--------------+----------+--------------------+
    |  userid|          eventtime|location_point| timestamp|occurrences_in_5_min|
    +--------+-------------------+--------------+----------+--------------------+
    |40e8a7c3|2017-06-04 03:12:00|      18825769|1496545920|                   1|
    |3136afcb|2017-06-04 03:03:00|      18382821|1496545380|                   1|
    |661212dd|2017-06-04 03:06:00|      80831484|1496545560|                   1|
    |4e191908|2017-06-04 03:00:00|      18685891|1496545200|                   1|
    |4e191908|2017-06-04 03:04:00|      18685891|1496545440|                   2|
    |4e191908|2017-06-04 03:11:30|      18685891|1496545890|                   1|
    +--------+-------------------+--------------+----------+--------------------+
    

    现在,如果在特定位置的最后 5 分钟内出现次数严格超过 1,您可以使用 True 添加所需的列:

    add_bool = udf(lambda col : True if col>1 else False, BooleanType())
    df = df.withColumn('already_occured',add_bool('occurrences_in_5_min'))
    df.show()
    
    +--------+-------------------+--------------+----------+--------------------+---------------+
    |  userid|          eventtime|location_point| timestamp|occurrences_in_5_min|already_occured|
    +--------+-------------------+--------------+----------+--------------------+---------------+
    |40e8a7c3|2017-06-04 03:12:00|      18825769|1496545920|                   1|          false|
    |3136afcb|2017-06-04 03:03:00|      18382821|1496545380|                   1|          false|
    |661212dd|2017-06-04 03:06:00|      80831484|1496545560|                   1|          false|
    |4e191908|2017-06-04 03:00:00|      18685891|1496545200|                   1|          false|
    |4e191908|2017-06-04 03:04:00|      18685891|1496545440|                   2|           true|
    |4e191908|2017-06-04 03:11:30|      18685891|1496545890|                   1|          false|
    +--------+-------------------+--------------+----------+--------------------+---------------+
    

    【讨论】:

      【解决方案2】:

      rangeBetween 对像lag 这样的非聚合函数没有意义。 lag 总是需要一个特定的行,用 offset 参数表示,所以指定 frame 是没有意义的。

      要获得时间序列的窗口,您可以使用 window 与标准聚合进行分组:

      from pyspark.sql.functions import window,  countDistinct
      
      
      (df
          .groupBy("location_point", window("eventtime", "5 minutes"))
          .agg( countDistinct("userid")))
      

      您可以添加更多参数来修改幻灯片持续时间。

      如果你按location分区,你可以尝试类似的窗口函数:

      windowSpec = (W.partitionBy(col("location"))
        .orderBy(col("eventtime").cast("timestamp").cast("long"))
        .rangeBetween(0, days(5)))
      
      
      df.withColumn("id_count", countDistinct("userid").over(windowSpec))
      

      【讨论】:

      • 感谢您指出rangeBetween 基本上仅适用于聚合函数。我以前从来没有这样想过!
      猜你喜欢
      • 1970-01-01
      • 2020-05-04
      • 2014-05-01
      • 1970-01-01
      • 2021-04-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-18
      • 1970-01-01
      相关资源
      最近更新 更多