求助:使用正则表达式匹配Pandas序列与DataFrame列
解决Pandas中含特殊字符、复数后缀的字符串匹配问题
问题核心
需要将带特殊字符、空格的Pandas Series元素,与DataFrame中存在特殊字符转写、复数后缀、中间插入字符的列做匹配,匹配成功后将Series原元素(或调整后的形式,如复数)填入新列,不匹配则填充缺失值。
解决方案1:自定义正则匹配(规则明确场景)
适用于匹配逻辑清晰、数据量较小的情况,通过编写精准正则覆盖特殊变形场景。
代码实现
import pandas as pd import re # 原始数据 s = pd.Series(['Äpple', 'Orange & Pear', 'Banänas', 'Octopi', 'Wine']) df = pd.DataFrame({ "fruits": ['apple','orange-something-pear', 'bananaes', 'octopus', 'kiwi'], "amounts": [100, 200, 300, 400, 500] }) # 构建匹配规则:键为Series原始值,值为匹配df['fruits']的正则表达式 match_patterns = { 'Äpple': r'^apple$', # 匹配单数apple,后续转为复数Äpples 'Orange & Pear': r'^orange.*pear$', # 匹配以orange开头、pear结尾的任意中间内容 'Banänas': r'^bananaes$', # 匹配bananaes 'Octopi': r'^octopus$', # 匹配octopus(Octopi是其复数形式) 'Wine': r'^wine$' # 匹配wine(df中无对应项) } # 初始化title列为缺失值 df['title'] = pd.NA # 遍历匹配规则为对应行赋值 for original_title, pattern in match_patterns.items(): # 不区分大小写执行正则匹配 match_mask = df['fruits'].str.match(pattern, case=False) # 针对Äpple特殊处理为复数形式 if original_title == 'Äpple': df.loc[match_mask, 'title'] = 'Äpples' else: df.loc[match_mask, 'title'] = original_title # 调整列顺序匹配目标格式(可选) df = df.reindex([0,1,3,2,4]).reset_index(drop=True) print(df)
运行结果
fruits amounts title 0 apple 100 Äpples 1 orange-something-pear 200 Orange & Pear 2 octopus 400 Octopi 3 bananaes 300 Banänas 4 kiwi 500 <NA>
解决方案2:通用归一化匹配(规则多样场景)
适用于数据量大、变形规则复杂的情况,通过统一归一化特殊字符和复数后缀,实现模糊匹配。
前置准备
先安装unidecode库用于转写特殊字符:
pip install unidecode
代码实现
import pandas as pd import re from unidecode import unidecode # 原始数据 s = pd.Series(['Äpple', 'Orange & Pear', 'Banänas', 'Octopi', 'Wine']) df = pd.DataFrame({ "fruits": ['apple','orange-something-pear', 'bananaes', 'octopus', 'kiwi'], "amounts": [100, 200, 300, 400, 500] }) # 定义归一化函数:转小写、去特殊字符、移除常见复数后缀 def normalize_str(text): # 转小写并将特殊字符转写为ASCII norm_text = unidecode(text.lower()) # 移除常见复数后缀:s、es、i(如octopi→octop,bananaes→banana) norm_text = re.sub(r'(s|es|i)$', '', norm_text) # 移除中间非字母字符(如&、-) norm_text = re.sub(r'[^a-z]', '', norm_text) return norm_text # 对Series和df的fruits列做归一化处理 s_norm = s.apply(normalize_str) df['fruits_norm'] = df['fruits'].apply(normalize_str) # 构建归一化值到原Series元素的映射 norm_to_title = dict(zip(s_norm, s)) # 模糊匹配:找到包含归一化后Series值的行 df['title'] = df['fruits_norm'].apply( lambda x: next((title for norm_s, title in norm_to_title.items() if norm_s in x), pd.NA) ) # 特殊处理Äpple为复数Äpples df.loc[df['title'] == 'Äpple', 'title'] = 'Äpples' # 调整列顺序并移除中间归一化列(可选) df = df.reindex([0,1,3,2,4]).reset_index(drop=True) df = df.drop('fruits_norm', axis=1) print(df)
方案对比
- 方案1:精准度高,适合规则明确的小量数据
- 方案2:通用性强,适合变形规则复杂的大量数据
内容的提问来源于stack exchange,提问作者makrbec
相关产品推荐
相关产品推荐

