如何优化Pandas中非连续重复section字段的分组重命名实现?
需求与问题
我管理着一个项目组合,每个项目都有独立的Excel表格用于跟踪时间、成本等数据。当前处理的DataFrame中,section字段存在非连续重复的情况,需要将同一section的不同连续分组进行编号区分(例如lift、lift (2)、lift (3))。我已经编写代码实现了需求,但希望得到更简洁优雅的优化方案。
原始数据
time cost section error other 0 1 500 lift E1 1 2 100 lift E2 2 1 400 wait E3 3 4 200 lift D4 E4 4 5 500 lift E5 5 2 322 crash E3 6 3 500 lift E1 7 3 200 wait D5 E2
期望结果
time cost section error other 0 1 500 lift E1 1 2 100 lift E2 2 1 400 wait E3 3 4 200 lift (2) D4 E4 4 5 500 lift (2) E5 5 2 322 crash E3 6 3 500 lift (3) E1 7 3 200 wait (2) D5 E2
当前实现代码
df = pd.DataFrame({ 'time': ['1', '2', '1', '4', '5', '2', '3', '3'], 'cost': ['500', '100', '400', '200', '500', '322','500','200'], 'section': ['lift', 'lift', 'wait', 'lift', 'lift', 'crash', 'lift','wait'], 'error': ['', '', '', 'D4', '','','','D5'], 'other': ['E1', 'E2', 'E3', 'E4', 'E5','E3','E1','E2']} ) print(df) # 检查当前行section是否与上一行相同 df['match'] = df.section.eq(df.section.shift()) # 若不同,将section值写入新列 df.loc[df['match'] == False, 'match2'] = df['section'] # 统计每个section的非连续出现次数 df.loc[df['match'] == False, 'match3'] = df.groupby(['match2']).cumcount()+1 # 根据次数添加后缀 df.loc[df['match3'] == 1, 'match4'] = df['section'] df.loc[df['match3'] == 2, 'match4'] = df['section'] + ' (2)' df.loc[df['match3'] == 3, 'match4'] = df['section'] + ' (3)' df.loc[df['match3'] == 4, 'match4'] = df['section'] + ' (4)' df.loc[df['match3'] == 5, 'match4'] = df['section'] + ' (5)' df.loc[df['match3'] == 6, 'match4'] = df['section'] + ' (6)' df['match4'] = df['match4'].ffill() df['section'] = df['match4'] df.drop(['match', 'match2', 'match3', 'match4'], inplace=True, axis=1 ) print(df)
当前代码输出
time cost section error other 0 1 500 lift E1 1 2 100 lift E2 2 1 400 wait E3 3 4 200 lift D4 E4 4 5 500 lift E5 5 2 322 crash E3 6 3 500 lift E1 7 3 200 wait D5 E2 time cost section error other 0 1 500 lift E1 1 2 100 lift E2 2 1 400 wait E3 3 4 200 lift (2) D4 E4 4 5 500 lift (2) E5 5 2 322 crash E3 6 3 500 lift (3) E1 7 3 200 wait (2) D5 E2
优化方案
通过识别连续分组+分组内计数+批量生成后缀的方式大幅简化代码,避免多个中间列和重复的条件赋值:
import pandas as pd df = pd.DataFrame({ 'time': ['1', '2', '1', '4', '5', '2', '3', '3'], 'cost': ['500', '100', '400', '200', '500', '322','500','200'], 'section': ['lift', 'lift', 'wait', 'lift', 'lift', 'crash', 'lift','wait'], 'error': ['', '', '', 'D4', '','','','D5'], 'other': ['E1', 'E2', 'E3', 'E4', 'E5','E3','E1','E2']} ) # 1. 标记连续分组的起始点:当前行与上一行section不同时为True group_starts = df['section'] != df['section'].shift() # 2. 为每个连续分组分配唯一ID(全局分组编号) df['group_id'] = group_starts.cumsum() # 3. 按原始section分组,统计每个section下的连续分组出现次数 df['section_count'] = df.groupby('section')['group_id'].transform(lambda x: x.rank(method='dense').astype(int)) # 4. 生成新的section名称:仅当计数>1时添加后缀 df['section'] = df.apply( lambda row: f"{row['section']} ({row['section_count']})" if row['section_count'] > 1 else row['section'], axis=1 ) # 清理中间列 df.drop(['group_id', 'section_count'], axis=1, inplace=True) print(df)
优化说明
- 步骤1-2:通过
cumsum()快速生成连续分组的唯一ID,避免手动标记和填充; - 步骤3:用
rank(method='dense')直接统计每个section下的连续分组序号,替代多次分组和条件赋值; - 步骤4:用
apply批量处理后缀生成,无需为每个计数写单独的条件语句,扩展性更强(支持任意次数的重复); - 整体代码更紧凑,逻辑清晰,中间列可一次性清理,减少冗余操作。
内容的提问来源于stack exchange,提问作者beltalowda
相关产品推荐
相关产品推荐

