【问题标题】:Pyspark : Custom window functionPyspark:自定义窗口函数
【发布时间】:2017-03-17 04:31:54
【问题描述】:

我目前正在尝试在 PySpark 数据帧中提取一系列连续出现的事件并对它们进行排序/排序,如下所示(为方便起见,我已按 user_idtimestamp 对初始数据帧进行排序):

df_ini
+-------+--------------------+------------+
|user_id|     timestamp      |  actions   |
+-------+--------------------+------------+
| 217498|           100000001|    'A'     |
| 217498|           100000025|    'A'     |
| 217498|           100000124|    'A'     |
| 217498|           100000152|    'B'     |
| 217498|           100000165|    'C'     |
| 217498|           100000177|    'C'     |
| 217498|           100000182|    'A'     |
| 217498|           100000197|    'B'     |
| 217498|           100000210|    'B'     |
| 854123|           100000005|    'A'     |
| 854123|           100000007|    'A'     |
| etc.

到:

expected df_transformed
+-------+------------+------------+------------+
|user_id|  actions   | nb_of_occ  |    order   |
+-------+------------+------------+------------+
| 217498|    'A'     |      3     |     1      |
| 217498|    'B'     |      1     |     2      |
| 217498|    'C'     |      2     |     3      |
| 217498|    'A'     |      1     |     4      |
| 217498|    'B'     |      2     |     5      |
| 854123|    'A'     |      2     |     1      |
| etc.

我的猜测是我必须使用一个智能窗口函数,通过 user_id 和操作对表进行分区但只有当这些操作在时间上是连续的时!我不知道该怎么做...

如果有人在 PySpark 中遇到这种类型的转换,我会很高兴得到提示!

干杯

【问题讨论】:

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


    【解决方案1】:

    这是一种非常常见的模式,可以通过几个步骤使用窗口函数来表达。首先导入所需的函数:

    from pyspark.sql.functions import sum as sum_, lag, col, coalesce, lit
    from pyspark.sql.window import Window
    

    接下来定义一个窗口:

    w = Window.partitionBy("user_id").orderBy("timestamp")
    

    标记每个组的第一行:

    is_first = coalesce(
      (lag("actions", 1).over(w) != col("actions")).cast("bigint"),
      lit(1)
    )
    

    定义order

    order = sum_("is_first").over(w)
    

    并将所有部分与聚合组合在一起:

    (df
        .withColumn("is_first", is_first)
        .withColumn("order", order)
        .groupBy("user_id", "actions", "order")
        .count())
    

    如果您将df 定义为:

    df = sc.parallelize([
        (217498, 100000001, 'A'), (217498, 100000025, 'A'), (217498, 100000124, 'A'),
        (217498, 100000152, 'B'), (217498, 100000165, 'C'), (217498, 100000177, 'C'),
        (217498, 100000182, 'A'), (217498, 100000197, 'B'), (217498, 100000210, 'B'),
        (854123, 100000005, 'A'), (854123, 100000007, 'A')
    ]).toDF(["user_id", "timestamp", "actions"])
    

    并按user_idorder 排序结果,您将得到:

    +-------+-------+-----+-----+ 
    |user_id|actions|order|count|
    +-------+-------+-----+-----+
    | 217498|      A|    1|    3|
    | 217498|      B|    2|    1|
    | 217498|      C|    3|    2|
    | 217498|      A|    4|    1|
    | 217498|      B|    5|    2|
    | 854123|      A|    1|    2|
    +-------+-------+-----+-----+
    

    【讨论】:

    • 为什么需要一个 coalesce() ?
    • 是否可以将列动态传递给正在定义的窗口?我正在尝试这样做,但是面临 WindowSpec 类型错误。stackoverflow.com/questions/58746514/…
    【解决方案2】:

    恐怕无法使用标准数据框窗口函数。但是您仍然可以使用旧的 RDD API groupByKey() 来实现该转换:

    >>> from itertools import groupby
    >>> 
    >>> def recalculate(records):
    ...     actions = [r.actions for r in sorted(records[1], key=lambda r: r.timestamp)]
    ...     groups = [list(g) for k, g in groupby(actions)]
    ...     return [(records[0], g[0], len(g), i+1) for i, g in enumerate(groups)]
    ... 
    >>> df_ini.rdd.map(lambda row: (row.user_id, row)) \
    ...     .groupByKey().flatMap(recalculate) \
    ...     .toDF(['user_id', 'actions', 'nf_of_occ', 'order']).show()
    +-------+-------+---------+-----+
    |user_id|actions|nf_of_occ|order|
    +-------+-------+---------+-----+
    | 217498|      A|        3|    1|
    | 217498|      B|        1|    2|
    | 217498|      C|        2|    3|
    | 217498|      A|        1|    4|
    | 217498|      B|        2|    5|
    | 854123|      A|        2|    1|
    +-------+-------+---------+-----+
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-16
      • 2019-09-21
      • 2018-03-14
      • 2023-02-23
      • 2020-05-26
      • 1970-01-01
      • 2016-05-10
      • 2022-01-25
      相关资源
      最近更新 更多