如何在pandas DataFrame中基于其他列子串创建新列并解决匹配与告警问题
问题原因说明
- 子串匹配错误:原代码
any(i in x for i in 'Urea')是遍历'Urea'的单个字符做匹配,只要Substance列值包含U/r/e/a任意一个字符就会触发匹配,而非匹配完整的"Urea"子串,所以会出现大量误判。 - SettingWithCopyWarning告警:你的
test对象是从其他DataFrame切片生成的派生对象,pandas无法确认它是原数据的视图还是独立副本,直接赋值存在误改原数据的风险。
最优实现方案
推荐使用numpy.select实现规则匹配,扩展性强、运行效率远高于apply,规则调整只需修改条件和结果列表即可:
import pandas as pd import numpy as np # 解决告警:如果test是切片生成的,先转为独立副本 test = test.copy() # 按优先级定义匹配条件,靠前的规则优先匹配 match_conditions = [ test['Substance'].str.contains('Urea'), test['Substance'].str.contains('CAN'), test['Substance'].str.contains('DAP') ] # 条件对应返回的短名称,顺序和上面的条件一一对应 match_results = [ 'Urea', 'Calcium ammonium nitrate (CAN)', 'Di-ammonium phosphate (DAP)' ] # 未匹配到任何规则的默认返回(NPK) test['Short Name'] = np.select(match_conditions, match_results, default='(NPK)')
如果需要忽略大小写匹配,可在str.contains中添加case=False参数,例如test['Substance'].str.contains('Urea', case=False)。
apply版本实现(适合小数据量场景)
如果更习惯用apply逻辑,可参考以下写法:
test = test.copy() def map_short_name(substance): if 'Urea' in substance: return 'Urea' elif 'CAN' in substance: return 'Calcium ammonium nitrate (CAN)' elif 'DAP' in substance: return 'Di-ammonium phosphate (DAP)' return '(NPK)' test['Short Name'] = test['Substance'].apply(map_short_name)
两种方案运行后的结果一致,你提供的示例数据最终输出如下:
| Substance | value | Short Name |
|---|---|---|
| (NPK) 20/10/6 | 0.2 | (NPK) |
| (NPK) Guayacan 10/20/30 | 0.4 | (NPK) |
| 46%N / O%P2O5 (Urea) | 0.6 | Urea |
| 46%N / O%P2O5 (Urea) | 0.8 | Urea |
| (NPK) DAP Diammonphosphat; 18/46/0 | 0.9 | Di-ammonium phosphate (DAP) |
内容的提问来源于stack exchange,提问作者switchback
相关产品推荐
相关产品推荐

