
本文介绍如何在 Pandas DataFrame 中,基于 State 分组后,进一步将连续相同 Sales_Amt 值的行合并为一个时间区间(即识别“连续块”),而非简单按值全局聚合,从而正确生成 Start_Dates 和 End_Dates。
本文介绍如何在 pandas dataframe 中,基于 `state` 分组后,进一步将**连续相同 `sales_amt` 值的行合并为一个时间区间**(即识别“连续块”),而非简单按值全局聚合,从而正确生成 `start_dates` 和 `end_dates`。
在实际数据分析中,常需对时序数据按业务维度(如 State)分组,并进一步识别同一指标(如 Sales_Amt)的连续稳定期——例如某地区销售额连续多月保持 123,随后变为 321,再变回 123,这三个阶段应视为三个独立区间,而非合并为一个大组。这正是典型的“连续值分组”(consecutive grouping)问题,仅靠 groupby(['State', 'Sales_Amt']) 无法解决,因为它会跨断点合并所有相同值,忽略顺序与连续性。
核心思路是:为每条记录构造一个辅助分组标识(grouper),该标识在 State 内部随 Sales_Amt 的变化而递增——即当当前行的 Sales_Amt 不等于前一行(同 State)的 Sales_Amt 时,开启新组。Pandas 提供了简洁高效的实现方式:
import pandas as pd
import numpy as np
# 确保日期列为 datetime 类型(关键!否则 min/max 可能出错)
df['Dates'] = pd.to_datetime(df['Dates'])
# 构造连续分组标识:在每个 State 内,检测 Sales_Amt 是否发生突变
group_series = df.groupby('State')['Sales_Amt'].apply(
lambda x: x.ne(x.shift()).cumsum()
).rename('g')
# 将辅助分组列加入原 DataFrame(推荐用 assign 避免链式赋值警告)
df = df.assign(g=group_series)
# 多级分组:State + Sales_Amt + 连续组号 → 聚合起止日期
result = (df.groupby(['State', 'Sales_Amt', 'g'], group_keys=False)
.agg(
Start_Dates=('Dates', 'min'),
End_Dates=('Dates', 'max')
)
.sort_values(['State', 'Start_Dates'])
.reset_index(drop=True)
.drop(columns='g') # 清理临时列
)
✅ 关键说明:
- x.ne(x.shift()) 生成布尔序列,标记每次 Sales_Amt 变化的位置(首行为 True);
- .cumsum() 将其转换为递增整数标签,相同标签即代表连续相同值的块;
- groupby(..., group_keys=False) 可避免索引冗余,提升可读性;
- sort_values(...) 确保结果按 State 和时间自然排序,符合业务逻辑。
最终输出严格匹配目标格式,每个 State 下的 Sales_Amt 变化均被精确切分为独立时间段,且起止日期为对应连续块内的最早与最晚 Dates。此方法鲁棒性强,适用于任意长度、多状态、含重复值的时序分组场景,是 Pandas 高级分组技巧中的经典实践。










