Spark - 使用 groupBy 减少组合数量

Spark - Reducing number of combinations using a groupBy

假设我有以下形式的数据集:

data = sc.parallelize([('customer_1', 'contract_1', 15000, 100), 
                       ('customer_1', 'contract_1', 20000, 200),
                       ('customer_2', 'contract_2', 30000, 100), 
                       ('customer_1', 'contract_1', 7500, 500)], 2)

其中:

我需要做的是添加一个额外的列,对于每一行,包含具有相同客户 ID、相同合同 ID 且时间戳等于或大于时间戳的所有行的合同价值总和当前行。

所以,对于之前的数据集,结果应该是:

customer_1 contract_1 15000 300  # 300 = 100+200
customer_1 contract_1 20000 200  # 200
customer_2 contract_2 30000 100  # 100
customer_1 contract_1  7500 800  # 800 = 100+200+500

如果没有时间戳检查,可以设置一个由客户 ID 和合同 ID 组成的密钥,按密钥减少,然后进行连接,但如果有时间戳比较,我找不到简单的方法这样做的方式。

我完成这项工作的第一种方法是以这种方式使用笛卡尔运算:

combinations = data.cartesian(data)
               .filter(lambda a: a[0][0] == a[1][0] and  
                                 a[0][1] == a[1][1] and 
                                 a[1][2] >= a[0][2])
agg = combinations.map(lambda a: (a[0], a[1][3])).reduceByKey(lambda x,y: x+y)

结果还可以,但恐怕对我管理的数据量(超过 100 万行)应用笛卡尔坐标效率很低。事实上,在这里应用笛卡尔运算会产生很多根本没有意义的组合(根据定义,组合不同客户或合同的行是没有意义的),这些组合后来被过滤器删除。

我的理想情况是使用客户 ID 和合同 ID 作为键执行 groupBy,然后遍历结果 groupBy,并对每一行应用笛卡尔积。这将大大减少生成的组合数量。但是,我没有找到任何方法来做到这一点。更何况,这可能吗?如果是这样,如何?关于如何实现我的要求,您还有其他 recommendation/idea 吗?

感谢您的帮助!

这是一个要求 window 函数的问题:

import sys
from pyspark.sql.window import Window
from pyspark.sql.functions import sum

df = data.toDF(["customer_id", "contract_id", "timestamp", "value"])
w = (Window()
    .partitionBy("customer_id", "contract_id")
    .orderBy("timestamp")
    # Current row and future values
    .rangeBetween(0, sys.maxsize)) # or .rowsBetween(0, sys.maxsize)

result = df.withColumn("future_value", sum("value").over(w))
result.show()

## +-----------+-----------+---------+-----+------------+
## |customer_id|contract_id|timestamp|value|future_value|
## +-----------+-----------+---------+-----+------------+
## | customer_1| contract_1|     7500|  500|         800|
## | customer_1| contract_1|    15000|  100|         300|
## | customer_1| contract_1|    20000|  200|         200|
## | customer_2| contract_2|    30000|  100|         100|
## +-----------+-----------+---------+-----+------------+