本文介绍如何基于 DataFrame 中某一列(如 Sales_Amt)对连续相同值进行分组(而非全局去重分组),同时保留状态列(如 State)的区分,最终生成每个连续段的起止日期区间。
本文介绍如何基于 dataframe 中某一列(如 `sales_amt`)对**连续相同值**进行分组(而非全局去重分组),同时保留状态列(如 `state`)的区分,最终生成每个连续段的起止日期区间。
在 Pandas 数据分析中,常规的 groupby(['State', 'Sales_Amt']) 会将所有同值记录归为一组(例如所有 Sales_Amt=123 的行,无论是否连续),但这无法满足“仅合并相邻相同值”的业务需求——比如时间序列中需识别销售金额的连续稳定期。要实现真正的“连续分组”(consecutive grouping),关键在于构造一个能标识值变化边界的辅助分组键。
核心思路是:对每组 State 内部,检测 Sales_Amt 是否相比上一行发生改变;若改变,则开启新组。这可通过 shift() 与 cumsum() 链式操作高效实现:
import pandas as pd
import numpy as np
# 确保日期列为 datetime 类型(便于后续聚合)
df['Dates'] = pd.to_datetime(df['Dates'])
# 构造连续分组标识列:当 Sales_Amt 变化时,cumsum 增加,形成新组号
group_id = df.groupby('State')['Sales_Amt'].apply(
lambda x: x.ne(x.shift()).cumsum()
).rename('group_id')
# 在原 DataFrame 中添加该标识(或直接用于 groupby)
df_with_group = df.assign(group_id=group_id)
# 按 State、Sales_Amt 和连续组号三重分组,聚合起止日期
result = (df_with_group
.groupby(['State', 'Sales_Amt', 'group_id'], group_keys=False)
.agg(
Start_Dates=('Dates', 'min'),
End_Dates=('Dates', 'max')
)
.sort_values(['State', 'Start_Dates'])
.reset_index(drop=True)
.drop(columns='group_id') # 清理临时列
)
✅ 输出效果:
每条记录代表一个 State 下 Sales_Amt 的一段连续不变区间,Start_Dates 与 End_Dates 即该区间的首尾日期。例如 State=A, Sales_Amt=123 出现两次,分别对应 1/1–2/1 和 5/1–7/1 两个独立连续段。
⚠️ 注意事项:
- 必须先确保 Dates 列为 datetime64 类型,否则 min()/max() 聚合可能出错或结果不可靠;
- groupby(...).apply(...) 中使用 lambda 是为清晰表达逻辑,也可用向量化写法(如答案中所示的布尔索引 + cumsum),性能更优;
- 若原始数据未按 State 和时间排序,务必在构造 group_id 前执行 df.sort_values(['State', 'Dates'], inplace=True),否则连续性判断将失效;
- 此方法天然支持多列连续分组(如同时按 Sales_Amt 和 Region 连续变化),只需扩展 ne() 和 shift() 的比较逻辑即可。
该技巧广泛应用于日志分析、状态追踪、指标波动区间识别等场景,是 Pandas 高级分组能力的典型实践。










