Apache spark 计算pyspark中每组成对连续行之间的时间差

Apache spark 计算pyspark中每组成对连续行之间的时间差,apache-spark,pyspark,apache-spark-sql,pyspark-sql,Apache Spark,Pyspark,Apache Spark Sql,Pyspark Sql,我想计算每个用户每个SeqID花费的时间。我有一个这样的数据帧。 但是,对于每个用户,Action\u A和Action\u B这两个操作的时间是分开的。 每个用户、每个seqID的总时间将是所有这些对的总和 对于第一个用户,它是5+3[(2019-12-10 10:00:00-2019-12-10 10:05:00)+(2019-12-10 10:20:00-2019-12-10:23:00)] 因此,第一个用户在Seqid1上理想地花费了8分钟(而不是23分钟) 同样,用户2花费了1+5=

我想计算每个用户每个SeqID花费的时间。我有一个这样的数据帧。 但是,对于每个用户,
Action\u A和Action\u B这两个操作的时间是分开的。
每个用户、每个seqID的总时间将是所有这些对的总和

对于第一个用户,它是
5+3[(2019-12-10 10:00:00-2019-12-10 10:05:00)+(2019-12-10 10:20:00-2019-12-10:23:00)]

因此,第一个用户在Seqid1上理想地花费了
8分钟(而不是
23分钟)

同样,用户2花费了
1+5=6分钟

如何使用pyspark计算此值

data = [(("ID1", 15, "2019-12-10 10:00:00", "Action_A")), 
        (("ID1", 15, "2019-12-10 10:05:00", "Action_B")),
        (("ID1", 15, "2019-12-10 10:20:00", "Action_A")),
        (("ID1", 15, "2019-12-10 10:23:00", "Action_B")),
        (("ID2", 23, "2019-12-10 11:10:00", "Action_A")),
        (("ID2", 23, "2019-12-10 11:11:00", "Action_B")),
        (("ID2", 23, "2019-12-10 11:30:00", "Action_A")),
        (("ID2", 23, "2019-12-10 11:35:00", "Action_B"))]
df = spark.createDataFrame(data, ["ID", "SeqID", "Timestamp", "Action"])
df.show()

+---+-----+-------------------+--------+
| ID|SeqID|          Timestamp|  Action|
+---+-----+-------------------+--------+
|ID1|   15|2019-12-10 10:00:00|Action_A|
|ID1|   15|2019-12-10 10:05:00|Action_B|
|ID1|   15|2019-12-10 10:20:00|Action_A|
|ID1|   15|2019-12-10 10:23:00|Action_B|
|ID2|   23|2019-12-10 11:10:00|Action_A|
|ID2|   23|2019-12-10 11:11:00|Action_B|
|ID2|   23|2019-12-10 11:30:00|Action_A|
|ID2|   23|2019-12-10 11:35:00|Action_B|
+---+-----+-------------------+--------+
一旦我有了每对的数据,我就可以对整个组进行求和(ID,SeqID)

预期输出(也可以是秒)


以下是使用(Spark>=2.4)的可能解决方案:

步骤:

  • 收集每个组
    ID
    SeqID
    的所有时间戳到数组中,并按升序排序
  • 使用lambda函数
    (x,i)=>Double
    对数组应用变换。其中,
    x
    是实际元素,
    i
    是其索引。对于数组中的每个时间戳,我们计算下一个时间戳的差异。我们乘以
    (i+1)%2
    ,以使每2对中只有2个差异(第一个是第二个,第三个是第四个,…),因为总是有2个动作
  • 最后,我们将转换的结果数组聚合为所有元素的和 输出:

    +---+-----+--------+
    |ID |SeqID|Dur_Mins|
    +---+-----+--------+
    |ID1|15   |8.0     |
    |ID2|23   |6.0     |
    +---+-----+--------+
    
    使用
    flatMapValues
    rdd

    使用
    数据
    变量

    df = spark.createDataFrame(data, ["id", "seq_id", "ts", "action"]). \
        withColumn('ts', func.col('ts').cast('timestamp'))
    
    # func to calculate the duration | applied on each row
    def getDur(groupedrows):
        """
        """
    
        res = []
    
        for row in groupedrows:
            if row.action == 'Action_A':
                frst_ts = row.ts
                dur = 0
            elif row.action == 'Action_B':
                dur = (row.ts - frst_ts).total_seconds()
    
            res.append([val for val in row] + [float(dur)])
    
        return res
    
    # run the rules on the base df | row by row
    # grouped on ID, SeqID - sorted on timestamp
    dur_rdd = df.rdd. \
        groupBy(lambda k: (k.id, k.seq_id)). \
        flatMapValues(lambda r: getDur(sorted(r, key=lambda ok: ok.ts))). \
        values()
    
    # specify final schema
    dur_schema = df.schema. \
        add('dur', 'float')
    
    # convert to DataFrame
    dur_sdf = spark.createDataFrame(dur_rdd, dur_schema)
    
    dur_sdf.orderBy('id', 'seq_id', 'ts').show()
    
    +---+------+-------------------+--------+-----+
    | id|seq_id|                 ts|  action|  dur|
    +---+------+-------------------+--------+-----+
    |ID1|    15|2019-12-10 10:00:00|Action_A|  0.0|
    |ID1|    15|2019-12-10 10:05:00|Action_B|300.0|
    |ID1|    15|2019-12-10 10:20:00|Action_A|  0.0|
    |ID1|    15|2019-12-10 10:23:00|Action_B|180.0|
    |ID2|    23|2019-12-10 11:10:00|Action_A|  0.0|
    |ID2|    23|2019-12-10 11:11:00|Action_B| 60.0|
    |ID2|    23|2019-12-10 11:30:00|Action_A|  0.0|
    |ID2|    23|2019-12-10 11:35:00|Action_B|300.0|
    +---+------+-------------------+--------+-----+
    
    # Your required data
    dur_sdf.groupBy('id', 'seq_id'). \
        agg((func.sum('dur') / func.lit(60)).alias('dur_mins')). \
        show()
    
    +---+------+--------+
    | id|seq_id|dur_mins|
    +---+------+--------+
    |ID1|    15|     8.0|
    |ID2|    23|     6.0|
    +---+------+--------+
    

    这符合您描述的数据,但请检查它是否符合您的所有情况。

    您可以将操作列拆分为两个新列,即操作A和操作B,然后根据Seq_ID计算两个值之间的差值,然后按聚合进行分组。您是在建议pivot吗?但是,对于相同的属性名称,有多个值,pivot将无法处理它。对于初学者,您可以获得每个ID/操作的最小和最大时间戳,然后计算每个操作的差异。然后,每个ID的简单总和汇总将给出每个ID的持续时间。我不确定每个操作的最小值和最大值在您无法使用的情况下有多大帮助(spark<2.4)
    +---+-----+--------+
    |ID |SeqID|Dur_Mins|
    +---+-----+--------+
    |ID1|15   |8.0     |
    |ID2|23   |6.0     |
    +---+-----+--------+
    
    df = spark.createDataFrame(data, ["id", "seq_id", "ts", "action"]). \
        withColumn('ts', func.col('ts').cast('timestamp'))
    
    # func to calculate the duration | applied on each row
    def getDur(groupedrows):
        """
        """
    
        res = []
    
        for row in groupedrows:
            if row.action == 'Action_A':
                frst_ts = row.ts
                dur = 0
            elif row.action == 'Action_B':
                dur = (row.ts - frst_ts).total_seconds()
    
            res.append([val for val in row] + [float(dur)])
    
        return res
    
    # run the rules on the base df | row by row
    # grouped on ID, SeqID - sorted on timestamp
    dur_rdd = df.rdd. \
        groupBy(lambda k: (k.id, k.seq_id)). \
        flatMapValues(lambda r: getDur(sorted(r, key=lambda ok: ok.ts))). \
        values()
    
    # specify final schema
    dur_schema = df.schema. \
        add('dur', 'float')
    
    # convert to DataFrame
    dur_sdf = spark.createDataFrame(dur_rdd, dur_schema)
    
    dur_sdf.orderBy('id', 'seq_id', 'ts').show()
    
    +---+------+-------------------+--------+-----+
    | id|seq_id|                 ts|  action|  dur|
    +---+------+-------------------+--------+-----+
    |ID1|    15|2019-12-10 10:00:00|Action_A|  0.0|
    |ID1|    15|2019-12-10 10:05:00|Action_B|300.0|
    |ID1|    15|2019-12-10 10:20:00|Action_A|  0.0|
    |ID1|    15|2019-12-10 10:23:00|Action_B|180.0|
    |ID2|    23|2019-12-10 11:10:00|Action_A|  0.0|
    |ID2|    23|2019-12-10 11:11:00|Action_B| 60.0|
    |ID2|    23|2019-12-10 11:30:00|Action_A|  0.0|
    |ID2|    23|2019-12-10 11:35:00|Action_B|300.0|
    +---+------+-------------------+--------+-----+
    
    # Your required data
    dur_sdf.groupBy('id', 'seq_id'). \
        agg((func.sum('dur') / func.lit(60)).alias('dur_mins')). \
        show()
    
    +---+------+--------+
    | id|seq_id|dur_mins|
    +---+------+--------+
    |ID1|    15|     8.0|
    |ID2|    23|     6.0|
    +---+------+--------+