Pandas loc按条件赋值str.extract提取结果异常及报错问题
问题原因
- 核心错误是对
pandas.Series.str.extract()的返回值理解有误:默认参数下,无论正则里写多少个捕获组,str.extract()返回的都是DataFrame类型,哪怕只有1个捕获组,得到的也是单列DataFrame,而非一维Series。 - 当你用
loc选中部分行、给单列col2赋值时,pandas期望接收一维的可迭代对象(Series/数组/列表),传入二维DataFrame会触发两类异常:- 若目标列是数值类型,pandas会尝试按列名、索引做对齐:你传入的全量DataFrame列名是默认的
0,和目标列名col2不匹配,选中行匹配不到有效值,全部被填充为NaN。 - 若目标列是字符串类型,pandas会直接检测到赋值类型不兼容,直接抛出
ValueError: Incompatible indexer with DataFrame错误。
- 若目标列是数值类型,pandas会尝试按列名、索引做对齐:你传入的全量DataFrame列名是默认的
正确实现方案
推荐两种无副作用的写法,都能得到预期结果:
写法1:开启expand=False参数(最简洁)
给str.extract传入expand=False,提取单捕获组时会直接返回Series类型,从根源上避免类型不匹配问题,结合fillna保留非匹配行的原有值:
import pandas as pd d = {'col1': ['62', '4 < s', '5<77', '< 10 '], 'col2': [3, 4, None, 9]} df = pd.DataFrame(data=d) # 提取匹配值,非匹配位置用原col2的值填充 df['col2'] = df['col1'].str.extract(r'<\s*(\d+)', expand=False).fillna(df['col2'])
如果偏好loc的显式筛选写法,可以这么写,索引自动对齐不会出错:
mask = df['col1'].str.contains(r'<\s*\d+') df.loc[mask, 'col2'] = df.loc[mask, 'col1'].str.extract(r'<\s*(\d+)', expand=False)
写法2:手动取DataFrame的列转Series
如果忘了expand=False参数,也可以手动取出str.extract返回的单列DataFrame的第0列,得到Series后再赋值:
# 全量赋值版本 df['col2'] = df['col1'].str.extract(r'<\s*(\d+)')[0].fillna(df['col2']) # loc版本 mask = df['col1'].str.contains(r'<\s*\d+') df.loc[mask, 'col2'] = df.loc[mask, 'col1'].str.extract(r'<\s*(\d+)')[0]
两种写法运行后得到的结果完全一致:
col1 col2 0 62 3 1 4 < s 4 2 5<77 77 3 < 10 10
内容的提问来源于stack exchange,提问作者butterflyeffect
相关产品推荐
相关产品推荐

