在数据分析工作中,原始表格经常把说明性文字和数值挤在同一个字段里,例如“订单A123数量45元”“退回B9件”。如果要把其中的数字抽出来,再按指定顺序拼成“单号-数量”这样的新字符串,靠Excel手动分列效率极低。Pandas提供了基于正则表达式的字符串方法,可以在不写循环的情况下向量化完成提取与组合。

使用str.extract按捕获组提取数字
Pandas的Series.str.extract方法接受一个正则表达式,并且要求表达式中包含圆括号表示的捕获组。该方法会对每一行字符串进行匹配,把每个捕获组的内容作为一列返回。如果只想拿第一个出现的连续数字,可以用(d+)作为模式。这里d代表数字字符,加号表示至少一个。当某行没有匹配到时,对应位置会是NaN,因此后续组合前通常需要用fillna填补。
下面的例子展示从混合文本中抽取订单数字和数量数字两个分组,并合并为新字符串:
import pandas as pd
df = pd.DataFrame({
'raw': ['订单A123数量45', '退B9件', 'C888共12']
})
# 两个捕获组:订单数字、数量数字
ext = df['raw'].str.extract(r'([A-Za-z]+?)(d+).*?(d+)')
# ext列名默认为0,1,2,分别对应字母前缀、订单号、数量
ext.columns = ['prefix', 'order_no', 'qty']
ext['order_no'] = ext['order_no'].fillna('0')
ext['qty'] = ext['qty'].fillna('0')
df['new_code'] = ext['prefix'] + ext['order_no'] + '-' + ext['qty']
print(df)
这段代码中,正则([A-Za-z]+?)(d+).*?(d+)先用非贪婪字母捕获前缀,再抓订单号,最后用.*?跳过中间文字后抓数量。这种写法在结构相对固定时很方便,但如果一行存在多个相似数字块,extract只取首次匹配,可能漏掉后面的内容。此时要考虑extractall或者调整表达式。
extract与extractall在组合场景下的差异
str.extractall会返回多级索引的DataFrame,把所有不重叠的匹配全部列出,每一行原始数据可能对应多行结果。它适合从一个字段里掏出所有数字再自行挑选的场景。比如文本是“收入100支出50结余30”,用(d+)配合extractall会得到三行数字,之后可用groupby和agg拼成“100-50-30”。
对比来看,extract代码更短、结果宽表直观,但灵活度低;extractall信息全、便于统计,却要处理多重索引。实际项目中,若确认每行只需前两个数字,extract足够。若数字个数不固定,可先extractall展开,再用pivot或apply组合。下面示例用extractall把不定数量数字连成字符串:
import pandas as pd
df = pd.DataFrame({'txt': ['收入100支出50', 'A1B2C3', '仅文字']})
all_num = df['txt'].str.extractall(r'(d+)')
# all_num索引为(原行号, 匹配序号)
joined = all_num.groupby(level=0)[0].apply(lambda s: '-'.join(s))
df['nums'] = joined.reset_index(level=1, drop=True)
print(df)
上例中groupby(level=0)按原行聚合,apply里用join把数字用短横连接。对于没有数字的行,joined里不会有对应键,赋值后即为NaN,可再fillna成空串。这种组合方式比循环快很多,也避免了Python层逐个判断。
利用向量化运算拼接提取结果
提取出数字列以后,Pandas支持用加号直接对字符串Series相加,也可以使用str.cat方法。两者都向量化执行,比apply加Python字符串快。要注意参与相加的列必须都是字符串类型,若提取结果是对象型但含NaN,需先astype(str)或fillna。组合时还可插入固定分隔符,如冒号、下划线,满足下游系统编码规则。
当新字符串格式复杂,比如“NO{订单}-Q{数量}”,可用str.cat配合格式化,或先用加号拼好模板。下面的片段演示把订单与数量嵌入固定文案:
import pandas as pd
df = pd.DataFrame({'order': ['123', '9', '888'], 'qty': ['45', '0', '12']})
df['order'] = df['order'].fillna('0')
df['qty'] = df['qty'].fillna('0')
df['label'] = 'NO' + df['order'] + '-Q' + df['qty']
# 等价于使用str.cat
# df['label'] = df['order'].str.cat(df['qty'], sep='-', na_rep='0')
print(df)
这种写法把正则提取和字符串组合分成清晰两步,易于调试。若后期规则变化,例如要加日期前缀,只需修改拼接那一行。从维护角度看,比把正则和格式揉在一个大表达式里更稳妥。掌握上述方法后,面对各种脏数据中的数字抽取与重排需求,都能用少量代码解决。