Pandas实现按分组累计统计唯一日期数(分组中断时重置)
需求说明
需要在pandas DataFrame中新增一列Daily_Count,实现以下逻辑:
- 按
Code和Item的连续序列分组(当Code或Item的连续值被中断时,视为全新分组) - 对每个分组内的唯一日期进行累计计数,同一日期的所有行对应相同计数值
- 新分组启动时计数从1重新开始
当前尝试代码
import pandas as pd df = pd.DataFrame({"Date":['2023-03-01', '2023-03-01', '2023-03-01', '2023-03-04', '2023-03-06', '2023-03-06', '2023-03-07', '2023-03-08', '2023-03-09','2023-03-01', '2023-03-02', '2023-03-03', '2023-03-03', '2023-03-03','2023-03-03', '2023-03-04', '2023-03-05', '2023-03-06'], "Code":['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y'], "Item":['A', 'A', 'A', 'B', 'B', 'B', 'B', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'], }) df['Date'] = pd.to_datetime(df['Date']) df['Daily_Count'] = df.groupby(['Code', 'Item', df['Date'].dt.date]).cumcount()
解决方案
当前代码无法识别Code/Item的连续中断,且cumcount()是对组内行数计数,而非对唯一日期累计。以下是符合需求的实现:
import pandas as pd df = pd.DataFrame({"Date":['2023-03-01', '2023-03-01', '2023-03-01', '2023-03-04', '2023-03-06', '2023-03-06', '2023-03-07', '2023-03-08', '2023-03-09','2023-03-01', '2023-03-02', '2023-03-03', '2023-03-03', '2023-03-03','2023-03-03', '2023-03-04', '2023-03-05', '2023-03-06'], "Code":['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y'], "Item":['A', 'A', 'A', 'B', 'B', 'B', 'B', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'], }) df['Date'] = pd.to_datetime(df['Date']) # 1. 标记连续的Code+Item分组:只要Code或Item与前一行不同,就视为新分组 df['group_id'] = (df[['Code', 'Item']] != df[['Code', 'Item']].shift()).any(axis=1).cumsum() # 2. 对每个分组内的唯一日期做密集排名,同一日期得到相同计数 date_rank = df.groupby('group_id')['Date'].rank(method='dense', ascending=True).astype(int) # 3. 赋值到目标列,清理临时列 df['Daily_Count'] = date_rank df.drop('group_id', axis=1, inplace=True) print(df)
代码解释
- 生成连续分组ID:通过对比当前行与前一行的
Code和Item,用cumsum()生成连续分组ID,精准识别Code/Item中断后的新分组。 - 唯一日期累计计数:
rank(method='dense')会对每个分组内的日期按顺序排名,同一日期的排名值相同,正好满足累计计数需求。 - 收尾处理:将排名转为整数后赋值给目标列,删除临时分组ID列。
输出结果
Date Code Item Daily_Count 0 2023-03-01 X A 1 1 2023-03-01 X A 1 2 2023-03-01 X A 1 3 2023-03-04 X B 1 4 2023-03-06 X B 2 5 2023-03-06 X B 2 6 2023-03-07 X B 3 7 2023-03-08 X A 1 8 2023-03-09 X A 2 9 2023-03-01 Y A 1 10 2023-03-02 Y A 2 11 2023-03-03 Y A 3 12 2023-03-03 Y A 3 13 2023-03-03 Y A 3 14 2023-03-03 Y A 3 15 2023-03-04 Y A 4 16 2023-03-05 Y A 5 17 2023-03-06 Y A 6
内容的提问来源于stack exchange,提问作者jonboy
相关产品推荐
相关产品推荐

