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

PySpark按ID分组统计status为0的连续累计天数实现方法

PySpark实现连续状态天数统计

问题说明

现有存储客户每日状态的PySpark DataFrame,结构如下:

+---+------+-----------+
|id |status|date       |
+---+------+-----------+
|1  |1     |01-01-2022 |
|1  |0     |02-01-2022 |
|1  |0     |03-01-2022 |
|1  |0     |04-01-2022 |
|1  |1     |05-01-2022 |
|1  |0     |06-01-2022 |
|2  |1     |01-01-2022 |
|2  |0     |02-01-2022 |
|2  |0     |03-01-2022 |
|2  |1     |04-01-2022 |
|2  |0     |05-01-2022 |
+---+------+-----------+

需要统计每个客户连续处于status=0状态的天数,期望输出如下:

+---+------+-----------+------------+
|id |status|date       |count_status|
+---+------+-----------+------------+
|1  |1     |01-01-2022 | 0          |
|1  |0     |02-01-2022 | 1          |
|1  |0     |03-01-2022 | 2          |
|1  |0     |04-01-2022 | 3          |
|1  |1     |05-01-2022 | 0          |
|1  |0     |06-01-2022 | 1          |
|2  |1     |01-01-2022 | 0          |
|2  |0     |02-01-2022 | 1          |
|2  |0     |03-01-2022 | 2          |
|2  |1     |04-01-2022 | 0          |
|2  |0     |05-01-2022 | 1          |
+---+------+-----------+-----------+

pandas环境下已通过以下逻辑实现,需要改写为PySpark版本:

df['count_status'] = np.where(df['status'] == 0, 
                             df.groupby(['id', 
                                        (df['status'] != df['status'].shift(1)).cumsum()]).cumcount()+1,
                                  0)

实现代码

核心逻辑和pandas实现完全对齐,通过窗口函数实现偏移取值、累计求和、分组计数三个步骤:

from pyspark.sql import Window
import pyspark.sql.functions as F

# 1. 定义基础窗口:按客户ID分区,按日期升序排列
base_win = Window.partitionBy("id").orderBy("date")

# 2. 标记状态变更点:取上一行status,和当前行不一致则标记为1
df = df.withColumn("prev_status", F.lag("status").over(base_win))
df = df.withColumn(
    "is_change",
    F.when(
        F.col("prev_status").isNull() | (F.col("status") != F.col("prev_status")),
        1
    ).otherwise(0)
)

# 3. 对变更标记累计求和,生成每个连续状态段的唯一ID
df = df.withColumn("segment_id", F.sum("is_change").over(base_win))

# 4. 按客户+连续状态段分组,计算段内序号,status为0时取序号否则为0
segment_win = Window.partitionBy("id", "segment_id").orderBy("date")
df = df.withColumn(
    "count_status",
    F.when(F.col("status") == 0, F.row_number().over(segment_win)).otherwise(0)
)

# 5. 删除中间计算列,得到最终结果
df = df.drop("prev_status", "is_change", "segment_id")

执行后输出和期望结果完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 23:12:19