基于条件的多列pyspark分组累积和函数

时间:2017-09-16 00:41:59

标签: python pandas apache-spark pyspark spark-dataframe

我需要创建一个event_id,基本上是在多个列(v_id,d_id,ip,l_id)上进行计数器分组,并在delta> gt时递增它。 40来得到 像这样的输出

v_id d_id ip l_id delta event_id  last_event_flag
1     20   30 40   1    1           N 
1     20   30 40   2    1           N
1     20   30 40   3    1           N
1     20   30 40   4    1           Y
1     20   20 40   1    1           Y
1     30   30 40   2    1           N
1     30   30 40   3    1           N
1     30   30 40   4    1           N
1     30   30 40   5    1           Y

我能够使用pandas数据框实现这一目标

df['event_id'] = (df.delta >=40.0).groupby([df.l_id,df.v_id,d_id,ip]).cumsum() + 1
df.append(df['event_id'], ignore_index=True

但在更大的数据上执行时会看到内存错误。

如何在pyspark中做类似的事情。

2 个答案:

答案 0 :(得分:1)

在pyspark中,您可以使用window函数执行此操作:

首先让我们创建数据帧。请注意,您也可以直接从csv:

将其作为数据框加载
df = spark.createDataFrame(
    sc.parallelize(
        [[1,20,30,40,1,1],
        [1,20,30,40,2,1],
        [1,20,30,40,3,1],
        [1,20,30,40,4,1],
        [1,20,30,40,45,2],
        [1,20,30,40,1,2],
        [1,30,30,40,2,1],
        [1,30,30,40,3,1],
        [1,30,30,40,4,1],
        [1,30,30,40,5,1]]
    ), 
    ["v_id","d_id","ip","l_id","delta","event_id"]
)

你的表中有一个隐式排序,我们需要创建一个单调递增的id,这样我们就不会最终改变它了:

import pyspark.sql.functions as psf
df = df.withColumn(
    "rn", 
    psf.monotonically_increasing_id()
)
    +----+----+---+----+-----+--------+----------+
    |v_id|d_id| ip|l_id|delta|event_id|        rn|
    +----+----+---+----+-----+--------+----------+
    |   1|  20| 30|  40|    1|       1|         0|
    |   1|  20| 30|  40|    2|       1|         1|
    |   1|  20| 30|  40|    3|       1|         2|
    |   1|  20| 30|  40|    4|       1|         3|
    |   1|  20| 30|  40|   45|       2|         4|
    |   1|  20| 30|  40|    1|       2|8589934592|
    |   1|  30| 30|  40|    2|       1|8589934593|
    |   1|  30| 30|  40|    3|       1|8589934594|
    |   1|  30| 30|  40|    4|       1|8589934595|
    |   1|  30| 30|  40|    5|       1|8589934596|
    +----+----+---+----+-----+--------+----------+

现在计算event_idlast_event_flag

from pyspark.sql import Window
w1 = Window.partitionBy("v_id", "d_id", "l_id", "ip").orderBy("rn")
w2 = Window.partitionBy("v_id", "d_id", "l_id", "ip").orderBy(psf.desc("rn"))
df.withColumn(
    "event_id", 
    psf.sum((df.delta >= 40).cast("int")).over(w1) + 1
).withColumn(
    "last_event_flag", 
    psf.row_number().over(w2) == 1
).drop("rn")

    +----+----+---+----+-----+--------+---------------+
    |v_id|d_id| ip|l_id|delta|event_id|last_event_flag|
    +----+----+---+----+-----+--------+---------------+
    |   1|  20| 30|  40|    1|       1|          false|
    |   1|  20| 30|  40|    2|       1|          false|
    |   1|  20| 30|  40|    3|       1|          false|
    |   1|  20| 30|  40|    4|       1|          false|
    |   1|  20| 30|  40|   45|       2|          false|
    |   1|  20| 30|  40|    1|       2|           true|
    |   1|  30| 30|  40|    2|       1|          false|
    |   1|  30| 30|  40|    3|       1|          false|
    |   1|  30| 30|  40|    4|       1|          false|
    |   1|  30| 30|  40|    5|       1|           true|
    +----+----+---+----+-----+--------+---------------+

答案 1 :(得分:0)

也许你应该在运行groupby之前计算df = df [df.delta> = 40] - 我不确定这是否重要。

此外,您可以查看chunksize以基于csv的块执行计算以获得内存效率。因此,您可以将数据拆分为10000行的块,然后运行计算以避免内存错误。

https://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html

How to read a 6 GB csv file with pandas