
本文介绍在Pandas中处理“按名称分组、跨多行存储”的不规则表格数据时,如何精准识别并删除重复的组(如第二个'M'组),同时保留其全部关联行(含空值行),避免传统drop_duplicates()失效问题。
本文介绍在pandas中处理“按名称分组、跨多行存储”的不规则表格数据时,如何精准识别并删除重复的组(如第二个'm'组),同时保留其全部关联行(含空值行),避免传统`drop_duplicates()`失效问题。
在实际数据清洗中,常遇到类似“逻辑分组”结构:同一实体(如姓名)的数据分散在连续多行,主键仅出现在首行,其余行为空或None,且各字段内容(如Mas、Sce)需整体视为一个数据单元。此时直接对Name列调用df.drop_duplicates(subset='Name')会失败——因为它仅检查当前行的Name值,而忽略后续空值行属于该组的事实,导致只保留首行、丢失组内其他行。
正确思路是:将整个逻辑组(从某非空Name开始,到下一个非空Name前一行结束)视为一个整体,并标记所有属于“重复组”的行予以剔除。
以下是推荐实现方案(使用向量化操作,高效且无循环):
import pandas as pd
import numpy as np
# 构造原始数据
d = {'Name': ['M', None, None, 'T', 'R', 'G', 'M', '', ''],
'Mas': ['( (87)', '(91)', '(97) )', '(77)', '(60)', '(95)', '( (50)', '(50)', '(99) )'],
'Sce': ['83', '', '', '76', '32', '20', '89', '', '']}
df = pd.DataFrame(d)
df['Name'] = df['Name'].ffill() # 向前填充,使每行都携带所属组名(关键预处理)
# 步骤1:统一空值为NaN,便于后续逻辑判断
df["Name"] = np.where(df["Name"].isin(["", None]), np.nan, df["Name"])
# 步骤2:生成分组标识 —— 每个非空Name开启新组,用cumsum标记组号
group_id = df["Name"].notna().cumsum()
# 步骤3:标记每个组是否为首次出现(keep='first'语义)
is_first_occurrence = ~group_id.duplicated(keep='first')
# 步骤4:将组级标记广播至每行(同组所有行共享同一布尔值)
mask = is_first_occurrence.groupby(group_id).transform('first')
# 步骤5:过滤,仅保留首次出现的组的所有行
result = df[mask].copy()
print(result)输出结果:
Name Mas Sce 0 M ( (87) 83 1 NaN (91) 2 NaN (97) ) 3 T (77) 76 4 R (60) 32 5 G (95) 20
✅ 关键点说明:
- df['Name'].ffill() 是前提,确保空行继承上方有效Name,使逻辑组可被识别;
- group_id = df["Name"].notna().cumsum() 将连续非空Name视为独立组(即使值相同),是核心分组机制;
- group_id.duplicated(keep='first') 判断组ID是否重复,再取反得到“首次组”掩码;
- transform('first') 将组级布尔值广播到组内每一行,保证整组被整体保留或丢弃;
- 该方法天然兼容任意长度的组(2行、5行或更多),且不依赖正则解析括号内容,鲁棒性强。
⚠️ 注意事项:
- 原始数据必须满足“非空Name严格标记新组起始”的格式,否则cumsum分组会错位;
- 若存在中间插入的孤立非空Name(如误标),建议先做基础校验(如df['Name'].str.len() > 0);
- 如需进一步提取Mas中的数值(如87, 91, 97),应单独使用正则(df['Mas'].str.extractall(r'(\d+)')),切勿在去重逻辑中耦合解析。
此方案兼顾准确性与性能,适用于日志解析、报表OCR后处理、嵌套文本表格等典型场景。

















