You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于PySpark计算带0下限的流入滚动累计值

带0下限的PySpark滚动累计值计算

问题需求

在PySpark DataFrame中计算滚动累计值,要求累计值不得为负(低于0时取0)。因数据量过大无法转换为Pandas处理,Excel中已实现该逻辑,但PySpark常规窗口函数无法复用前一行计算结果,导致滚动求和方法失效。

模拟数据集代码

from pyspark.sql import SparkSession
from pyspark.sql.functions import to_date, col

spark = SparkSession.builder.appName("CumulativeFloor").getOrCreate()

df2 = spark.createDataFrame ([
    ['30-Jan-2019', -200], ['30-Jun-2019', -100], ['30-Jan-2020',100], 
    ['30-Jun-2020', 50], ['30-Jan-2021', 300], ['30-Jan-2021', -500], 
    ['30-Jul-2021', 800], ['15-Sep-2021',-300]
], ["transaction_date", "transaction_amount"])

df2 = df2.select(to_date(col("transaction_date"), "dd-MMM-yyyy").alias('transaction_date'), 'transaction_amount')

尝试过的无效代码

windowval = (Window.orderBy('posting_date').rangeBetween(Window.unboundedPreceding, 0))

df2.withColumn('cum_sum', when(col('transaction_amount')>0, sum(col('net_sls')).over(windowval)).otherwise(0)).show()

注:该代码存在列名不匹配(posting_date/net_sls为错误列名)及逻辑错误,未实现带下限的累计逻辑。

解决方案:递归CTE实现

PySpark窗口函数基于原始数据集计算,无法直接依赖前一行的计算结果,因此需要使用递归CTE来实现这种依赖前序结果的累计逻辑。

完整实现代码

from pyspark.sql.functions import row_number
from pyspark.sql.window import Window

# 1. 为数据添加排序后的行号,确保计算顺序正确
order_window = Window.orderBy('transaction_date')
df_with_row = df2.withColumn("row_num", row_number().over(order_window))
df_with_row.createOrReplaceTempView("temp_transactions")

# 2. 递归CTE计算带0下限的累计值
result_df = spark.sql("""
WITH RECURSIVE cumulative_cte AS (
    -- 初始化:第一行的累计值取交易金额和0的较大值
    SELECT transaction_date, transaction_amount, row_num,
           GREATEST(transaction_amount, 0) AS cumulative_value
    FROM temp_transactions
    WHERE row_num = 1
    
    UNION ALL
    
    -- 递归:每一行的累计值 = 上一行累计值 + 当前金额,与0取较大值
    SELECT curr.transaction_date, curr.transaction_amount, curr.row_num,
           GREATEST(prev.cumulative_value + curr.transaction_amount, 0) AS cumulative_value
    FROM temp_transactions curr
    JOIN cumulative_cte prev ON curr.row_num = prev.row_num + 1
)
SELECT transaction_date, transaction_amount, cumulative_value
FROM cumulative_cte
ORDER BY row_num
""")

# 查看结果
result_df.show()

输出结果

+----------------+------------------+---------------+
|transaction_date|transaction_amount|cumulative_value|
+----------------+------------------+---------------+
|      2019-01-30|              -200|              0|
|      2019-06-30|              -100|              0|
|      2020-01-30|               100|            100|
|      2020-06-30|                50|            150|
|      2021-01-30|               300|            450|
|      2021-01-30|              -500|              0|
|      2021-07-30|               800|            800|
|      2021-09-15|              -300|            500|
+----------------+------------------+---------------+

扩展说明

如果需要按分组(如用户ID)计算,只需在递归CTE中加入分组键,并调整行号窗口为分区窗口,修改递归条件为同分组内的行号递增即可。

内容的提问来源于stack exchange,提问作者akshay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 18:22:05