如何按规则匹配子串并批量修改Pandas DataFrame列值?
问题:基于规则忽略大小写批量替换Pandas DataFrame字符串
现有数据
1. stores DataFrame
| country | store_name |
|---|---|
| FR | my new tmp |
| ES | this Tmp is new |
| FR | walmart |
| ES | Target |
| FR | TMP |
可复现代码:
import pandas as pd data_stores = [['FR', 'my new tmp'], ['ES', 'this Tmp is new'], ['FR', 'walmart'], ['ES', 'Target'], ['FR', 'TMP']] stores = pd.DataFrame(data_stores, columns=['country', 'store_name'])
2. replacements 规则DataFrame
| country | original | replacement |
|---|---|---|
| ES | TMP | STORE |
| FR | TMP | STORE |
| FR | WALMART | IGNORE |
可复现代码(修正原代码列名错误):
data_replace = [['ES', 'TMP','STORE'], ['FR', 'TMP','STORE'], ['FR', 'WALMART','IGNORE']] replacements = pd.DataFrame(data_replace, columns=['country', 'original', 'replacement'])
需求说明
忽略大小写匹配replacements中original列的内容:
- 若
store_name完全匹配original(忽略大小写),则替换为对应的replacement - 若
store_name包含original子串(忽略大小写),则替换该子串为replacement - 替换规则需按
country对应匹配,最终期望结果:
| country | store_name |
|---|---|
| FR | my new STORE |
| ES | this STORE is new |
| FR | IGNORE |
| ES | Target |
| FR | STORE |
当前代码问题
你提供的循环代码存在以下问题:
for index, row in replacements.iterrows(): stores['store_name'] = stores['store_name'].str.upper().replace(row["original"].upper(), row["replacement"])
- 大小写丢失:每次循环把整个
store_name列转成大写,破坏原字符串的正确大小写(如Target变成TARGET) - 规则范围错误:未按
country过滤替换行,ES的规则会错误应用到FR的行(虽结果碰巧符合,但存在逻辑漏洞) - 效率低下:
iterrows循环在处理大DataFrame时性能极差
优化解决方案
以下方案按国家分组处理规则,区分全匹配和子串替换,同时保留原字符串的正确大小写:
import pandas as pd # 构建每个国家的全匹配规则(忽略大小写) full_match_map = replacements.groupby('country').apply( lambda x: {row['original'].upper(): row['replacement'] for _, row in x.iterrows()} ).to_dict() # 构建每个国家的子串替换规则(忽略大小写正则) substring_replace_map = replacements.groupby('country').apply( lambda x: {f'(?i){row["original"]}': row['replacement'] for _, row in x.iterrows()} ).to_dict() def apply_replacement(row): country = row['country'] text = row['store_name'] text_upper = text.upper() # 优先处理全匹配替换 if country in full_match_map: if text_upper in full_match_map[country]: return full_match_map[country][text_upper] # 处理子串替换 if country in substring_replace_map: for pattern, repl in substring_replace_map[country].items(): text = pd.Series([text]).str.replace(pattern, repl, regex=True).iloc[0] return text # 应用替换规则 stores['store_name'] = stores.apply(apply_replacement, axis=1) # 输出结果 print(stores)
执行后输出与期望结果完全一致:
country store_name 0 FR my new STORE 1 ES this STORE is new 2 FR IGNORE 3 ES Target 4 FR STORE
优化点说明
- 按国家分组:确保规则仅应用到对应国家的行,避免跨区域错误替换
- 区分全匹配/子串:优先处理全匹配场景(如
walmart→IGNORE),再处理子串替换(如tmp→STORE) - 保留原大小写:用正则
(?i)标记实现忽略大小写匹配,无需转大写破坏原字符串格式 - 效率提升:避免
iterrows循环整个DataFrame,改用分组映射和批量处理,大数据场景下性能更优
内容的提问来源于stack exchange,提问作者Alain
相关产品推荐
相关产品推荐

