Pandas正则替换疑问:为何否定式正则表达式不生效?
正则否定式不生效的原因及解决方案
问题背景
原始DataFrame定义:
import pandas as pd df = pd.DataFrame(['0123_GRP_LE_BNS', 'ABC_GRP_BNS', 'DEF_GRP', '456A_GRP_SSA'], columns=['P'])
输出如下:
P 0 0123_GRP_LE_BNS 1 ABC_GRP_BNS 2 DEF_GRP 3 456A_GRP_SSA
需求:
- 若字符串包含
GRP_LE,则保留到GRP_LE,移除后续所有字符; - 若
GRP后不是_LE,则保留到GRP,移除后续所有字符。
期望输出:
0 0123_GRP_LE 1 ABC_GRP 2 DEF_GRP 3 456A_GRP
尝试以下正则替换后结果不符合预期:
df['P'].replace({r'(.*_GRP)[^_LE].*':r'\1', r'(.*GRP_LE)_.*':r'\1'}, regex=True)
输出结果:
0 0123_GRP_LE 1 ABC_GRP_BNS 2 DEF_GRP 3 456A_GRP_SSA Name: P, dtype: object
原因分析
你写的正则r'(.*_GRP)[^_LE].*'中的[^_LE]是单个字符的否定字符类,它的作用是匹配任意一个不是_、L、E的字符。
但目标字符串ABC_GRP_BNS中,GRP后面紧跟的是_,这个_属于[^_LE]排除的字符(字符类明确排除了_),所以[^_LE]无法匹配这个_,导致整个正则表达式不匹配该字符串,自然不会执行替换操作。
你真正需要的是判断GRP后面是否不是_LE这个完整序列,这需要用负向预查(零宽断言),而非单个字符的否定。
正确解决方案
方案1:分规则处理(先匹配GRP_LE,再匹配GRP后非_LE的情况)
使用负向预查(?!_LE)来判断GRP后面是否不是_LE:
df['P'].replace({ r'(.*GRP_LE)_.*': r'\1', # 保留GRP_LE,移除后续字符 r'(.*GRP)(?!_LE).*': r'\1' # GRP后不是_LE时,保留GRP,移除后续字符 }, regex=True)
方案2:单正则匹配所有情况
通过捕获组同时处理两种场景:
df['P'].replace(r'(.*GRP)(_LE)?.*', r'\1\2', regex=True)
两种方案运行后都能得到期望输出:
0 0123_GRP_LE 1 ABC_GRP 2 DEF_GRP 3 456A_GRP Name: P, dtype: object
内容的提问来源于stack exchange,提问作者techie11
相关产品推荐
相关产品推荐

