
本文介绍如何在 Pandas 中结合字符串模式匹配与 groupby 逻辑,精准筛选满足「同一分组内同时存在两类特定字符串」的数据子集,适用于文档号、编码类字段的复合业务规则过滤。
本文介绍如何在 pandes 中结合字符串模式匹配与 `groupby` 逻辑,精准筛选满足「同一分组内同时存在两类特定字符串」的数据子集,适用于文档号、编码类字段的复合业务规则过滤。
在实际数据分析中,常需对分组(如按客户号、产品线、文件编号 fn)施加跨行逻辑约束——例如:“每个 fn 组内,必须至少包含一个以 'EP' 开头且不以 'W' 结尾的 docn,同时也必须至少包含一个以 'EP' 开头且以 'W' 结尾的 docn”。这类需求无法通过简单布尔索引或单行条件完成,必须借助 groupby 的聚合判断 + transform 的广播能力实现高效向量化处理。
核心思路是:
- 分别构造两个布尔序列,标记每行是否满足子条件(如 docn 是否匹配 ^EP.*W$ 或 ^EP.*[^W]$);
- 按 fn 分组后,对每个布尔序列使用 .transform(any) —— 这会将每组内是否存在 True 的结果“广播”回原 DataFrame 的每一行;
- 组合两个广播后的布尔序列进行行级筛选,即可保留所有属于“合格分组”的原始记录。
以下是完整可运行代码示例:
import pandas as pd
# 构建示例数据
df = pd.DataFrame(data=[
['840', '007658', 'EP010A', 'wrwrwr'],
['841', '007658', 'EP019410A', 'wwtert'],
['842', '007658', 'EP0129W', 'erterte'],
['843', '007658', 'EP0629W', 'ertetet'],
['992', '007675', 'EP0275A', 'ete'],
['993', '007675', 'EP0375A', 'ertre'],
['994', '007675', 'EP02091', 'ert'],
['117', '007690', 'EP02212A', 'sf'],
['118', '007690', 'EP00212A', 'sf'],
['118', '007690', 'EP02281W', 'dg'],
['118', '007690', 'EP07281W', 'dg'],
['118', '007690', 'EP0281W', 'sdf'],
['118', '007690', 'EP02281W', 'ert'],
['118', '007690', 'EP0612A', 'dfg'],
['11', '0076', 'US0612A', 'dfg'],
['12', '0076', 'CA0612A', 'dfg']
], columns=['i', 'fn', 'docn', 'ad'])
# ✅ 关键步骤:构建分组级条件并广播
cond_has_ep_not_w = df['docn'].str.contains(r'^EP.*[^W]$', na=False).groupby(df['fn']).transform('any')
cond_has_ep_w = df['docn'].str.contains(r'^EP.*W$', na=False).groupby(df['fn']).transform('any')
# 筛选:仅保留所属分组同时满足两个条件的所有行
result = df[cond_has_ep_not_w & cond_has_ep_w].copy()
print(result)? 正则说明:
- r'^EP.*W$':严格匹配以 EP 开头、以 W 结尾的字符串(中间任意字符);
- r'^EP.*[^W]$':匹配以 EP 开头、且末字符不是 W 的字符串(注意 [^W] 表示“非 W 字符”,需确保字符串长度 ≥ 3,否则可能误判空值或单字符;更稳健写法可用 r'^EP.*(?
- na=False 参数防止 NaN 导致布尔运算异常。
⚠️ 注意事项:
- 避免使用 apply(lambda ...) + any() 在 groupby 上循环,该方式效率极低(Python 层循环),而 transform(any) 是底层优化的向量化操作;
- 若 docn 列含缺失值(NaN),str.contains() 默认返回 NaN,需显式传入 na=False,否则 transform(any) 可能返回 False(因 any([True, NaN]) 在 pandas 中为 True,但行为依赖版本,显式控制更安全);
- 此方法保留原始行结构与全部列,符合“获取符合条件分组的全部原始记录”的典型需求;若只需分组键(如 fn 列表),可改用 df.groupby('fn').filter(...).index.unique()。
总结而言,transform(any) 是连接“分组聚合判断”与“行级筛选”的关键桥梁。掌握这一模式,可高效解决诸如“某客户既有活跃订单又有退订记录”“某设备既上报正常日志又上报错误码”等典型的分组双条件业务场景。










