如何在Pandas DataFrame中按列值分组,拆分非连续重复值组
问题:Pandas 按连续列值分组并拆分非连续重复组
需要对Pandas DataFrame按State和Sales_Amt分组,但仅合并连续出现的相同值组;如果后续出现与之前分组值相同但非连续的情况,需拆分为新组。
原始数据
State Dates Sales_Amt A 1/1/2023 123 A 2/1/2023 123 A 3/1/2023 321 A 4/1/2023 321 A 5/1/2023 123 A 6/1/2023 123 A 7/1/2023 123 B 1/1/2023 456 B 2/1/2023 456 B 3/1/2023 456 B 4/1/2023 789 B 5/1/2023 789 B 6/1/2023 789 B 7/1/2023 123 B 8/1/2023 123
期望结果
State Start_Dates End_Dates Sales_Amt A 1/1/2023 2/1/2023 123 A 3/1/2023 4/1/2023 321 A 5/1/2023 7/1/2023 123 B 1/1/2023 3/1/2023 456 B 4/1/2023 6/1/2023 789 B 7/1/2023 8/1/2023 123
尝试的代码(无效)
df = (df.groupby(['State','Sales_Amt'],group_keys=True) .agg(Start_Dates=('Dates', np.min), End_Dates=('Dates', np.max)) .sort_values(['State','Start_Dates','End_Dates'],ascending=True) .reset_index() )
解决方案
问题原因
直接按State和Sales_Amt分组会将同一State下所有相同Sales_Amt的行合并,忽略了“连续”的要求,导致A组中两次出现的123被合并成一个区间,不符合需求。
实现步骤
- 转换日期格式:将
Dates列转为datetime类型,确保日期计算准确(可选,但推荐)。 - 生成连续分组标识:通过对比当前行与上一行的
State和Sales_Amt,标记新的分组,用累加值生成唯一组ID。 - 分组聚合:基于
State、Sales_Amt和组ID分组,提取每个组的起始和结束日期。
完整代码
import pandas as pd import numpy as np # 加载原始数据(示例) data = { 'State': ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B'], '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] } df = pd.DataFrame(data) # 转换Dates为datetime类型 df['Dates'] = pd.to_datetime(df['Dates'], format='%m/%d/%Y') # 创建连续分组标识:当State或Sales_Amt与上一行不同时,生成新组 df['group_id'] = (df[['State', 'Sales_Amt']] != df[['State', 'Sales_Amt']].shift()).any(axis=1).cumsum() # 分组聚合并整理结果 result = (df.groupby(['State', 'Sales_Amt', 'group_id']) .agg(Start_Dates=('Dates', 'min'), End_Dates=('Dates', 'max')) .reset_index() .drop('group_id', axis=1) .sort_values(['State', 'Start_Dates']) .reset_index(drop=True)) # 转换回原日期字符串格式(可选) result['Start_Dates'] = result['Start_Dates'].dt.strftime('%m/%d/%Y') result['End_Dates'] = result['End_Dates'].dt.strftime('%m/%d/%Y') print(result)
输出结果
State Start_Dates End_Dates Sales_Amt 0 A 1/1/2023 2/1/2023 123 1 A 3/1/2023 4/1/2023 321 2 A 5/1/2023 7/1/2023 123 3 B 1/1/2023 3/1/2023 456 4 B 4/1/2023 6/1/2023 789 5 B 7/1/2023 8/1/2023 123
内容的提问来源于stack exchange,提问作者eeem
相关产品推荐
相关产品推荐

