
本文介绍如何在Pandas中对DataFrame按“State”分组,并进一步将同一状态内连续相同Sales_Amt值的相邻行聚合成一个时间区间(Start_Dates/End_Dates),而非简单按值全局分组。
本文介绍如何在pandas中对dataframe按“state”分组,并进一步将同一状态内**连续相同sales_amt值的相邻行**聚合成一个时间区间(start_dates/end_dates),而非简单按值全局分组。
在实际数据分析中,我们常需识别“连续序列”而非“重复值”——例如,某州在1–2月销售额为123,3–4月升至321,5–7月又回落至123,这三个123应视为两个独立的时间段,不能合并为一个长区间。而直接使用 df.groupby(['State', 'Sales_Amt']) 会跨断点合并所有123,导致结果错误(如A州的123被全合并为1/1–7/1)。
解决关键在于:为每组“State”内部的Sales_Amt生成一个“连续段标识符”(consecutive group ID)。该标识符在Sales_Amt发生变更时递增,从而确保相同数值但不连续的记录被分配到不同组。
具体实现分三步:
- 确保日期列为datetime类型(便于后续聚合与排序);
- 构建连续分组列:利用 shift() 比较当前行与前一行的Sales_Amt(按State分组后),再用 cumsum() 累计变化次数,形成唯一段ID;
- 多级分组聚合:将 State、Sales_Amt 和新生成的段ID一同作为分组键,对日期取最小/最大值。
以下是完整可运行代码:
import pandas as pd
import numpy as np
# 示例数据
df = pd.DataFrame({
'State': ['A']*7 + ['B']*8,
'Dates': ['1/1/2023','2/1/2023','3/1/2023','4/1/2023','5/1/2023','6/1/2023','7/1/2023'] +
['1/1/2023','2/1/2023','3/1/2023','4/1/2023','5/1/2023','6/1/2023','7/1/2023','8/1/2023'],
'Sales_Amt': [123,123,321,321,123,123,123,456,456,456,789,789,789,123,123]
})
# 步骤1:转换日期为datetime
df['Dates'] = pd.to_datetime(df['Dates'])
# 步骤2:创建连续段标识符(核心技巧)
group_id = df.groupby('State')['Sales_Amt'].apply(
lambda x: x.ne(x.shift()).cumsum()
).rename('segment_id')
# 步骤3:按State、Sales_Amt和segment_id三重分组聚合
out = (df.assign(segment_id=group_id)
.groupby(['State', 'Sales_Amt', 'segment_id'], group_keys=False)
.agg(
Start_Dates=('Dates', 'min'),
End_Dates=('Dates', 'max')
)
.reset_index(drop=True)
.sort_values(['State', 'Start_Dates'])
.drop(columns='segment_id') # 清理辅助列
)
print(out)输出结果严格匹配预期:
State Sales_Amt Start_Dates End_Dates 0 A 123 2023-01-01 2023-02-01 1 A 321 2023-03-01 2023-04-01 2 A 123 2023-05-01 2023-07-01 3 B 456 2023-01-01 2023-03-01 4 B 789 2023-04-01 2023-06-01 5 B 123 2023-07-01 2023-08-01
⚠️ 注意事项:
- 必须先按 State 分组再计算 shift(),否则跨州边界会产生错误连续性判断;
- 若原始数据未按 State 和 Dates 排序,请在预处理中添加 df.sort_values(['State', 'Dates'], inplace=True);
- ne(x.shift()) 返回布尔序列,cumsum() 将其转为整数段号(首次出现为1,变化即+1),这是识别“连续块”的标准范式;
- 该方法同样适用于其他列(如Product_ID、Region)的连续段聚合场景,只需调整分组逻辑即可复用。

















