使用pandas按顺序匹配多列字符串填充State州列的实现方案
解决方案
你之前代码出现覆盖问题的核心原因是:赋值时没有过滤State列已经存在有效值的行,直接对所有匹配到的行统一赋值,才会把原有合法值覆盖。
实现逻辑
严格按照State > Location > Origin的优先级,仅对State列为空的行执行后续匹配填充,从高优先级列取到有效值后就不再处理该行,避免覆盖:
- 先清洗原始
State列,仅保留在给定州名列表内的有效值,无效值统一转为空值 - 对
State为空的行,从Location列提取匹配的州名填充 - 对仍为空的行,最后从
Origin列提取匹配的州名填充
代码实现
方法1:通用逻辑(易理解,适合中小数据集)
import pandas as pd import numpy as np # 测试数据和州名列表可直接复用你提供的版本 df = pd.DataFrame({'State': [np.nan, np.nan, np.nan, np.nan, np.nan, 'California', np.nan, np.nan], 'Location': ['Seattle, Washington', 'California', 'INPUT ERROR', 'Portland, Oregon', 'INPUT ERROR', 'San Bernardino', 'ERROR', 'Seattle'], 'Origin': ['Portland, Oregon', 'San Francisco', 'RANDOM ERROR', 'INPUT ERROR', 'Las Vegas, Nevada', 'Nevada, Barstow', 'Portland', 'Washington, Tacoma']}) states = ['Alaska', 'Arizona', 'California', 'Colorado', 'Hawaii', 'Idaho', 'Montana', 'Nevada', 'New Mexico', 'Oregon', 'Utah', 'Washington', 'Wyoming'] # 定义提取州名的辅助函数 def extract_valid_state(text, state_list): if pd.isna(text): return np.nan # 按逗号拆分字符串后逐个匹配,返回第一个命中的州名 text_parts = [part.strip() for part in str(text).split(',')] for part in text_parts: if part in state_list: return part return np.nan # 1. 清洗原State列,保留合法值 df['State'] = df['State'].apply(lambda x: x if x in states else np.nan) # 2. 仅填充State为空的行:从Location提取 empty_mask = df['State'].isna() df.loc[empty_mask, 'State'] = df.loc[empty_mask, 'Location'].apply(lambda x: extract_valid_state(x, states)) # 3. 填充剩余空行:从Origin提取 empty_mask = df['State'].isna() df.loc[empty_mask, 'State'] = df.loc[empty_mask, 'Origin'].apply(lambda x: extract_valid_state(x, states))
运行后最终State列结果:
| 索引 | State |
|---|---|
| 0 | Washington |
| 1 | California |
| 2 | Oregon |
| 3 | Oregon |
| 4 | Nevada |
| 5 | California |
| 6 | Oregon |
| 7 | Washington |
可以看到索引5行原有的California没有被覆盖,完全符合要求。
方法2:向量化操作(性能更高,适合百万级以上大数据集)
用正则匹配+combine_first按优先级取第一个非空值,避免apply循环的性能损耗:
# 生成州名正则匹配模式 state_reg = '|'.join(states) # 分别从Location、Origin列提取第一个匹配的州名 df['loc_state'] = df['Location'].str.extract(f'({state_reg})', expand=False) df['origin_state'] = df['Origin'].str.extract(f'({state_reg})', expand=False) # 按优先级合并:原State > Location提取值 > Origin提取值 df['State'] = df['State'].combine_first(df['loc_state']).combine_first(df['origin_state']) # 可选:删除临时生成的辅助列 df.drop(['loc_state', 'origin_state'], axis=1, inplace=True)
内容的提问来源于stack exchange,提问作者jcf
相关产品推荐
相关产品推荐

