如何用Pandas基于两列子串匹配为df1添加Address列?
问题描述
现有两个Pandas DataFrame:
DataFrame df1
Rep 0 ec21b_AI_154OH 1 m2010_AI_066UW 2 20wh1_DS_416FC
DataFrame df2
Address FirstPart SecondPart 0 address13 m2010 066UW 1 address22 2020e 999GV 2 address26 2020c 513DT 3 address35 evd18 874GO 4 address36 ep21b 986CG 5 address493 20wh1 416FC 6 address628 ec21b 154OH
需求是在df1中添加Address列,最终结果如下:
Rep Address 0 ec21b_AI_154OH address628 1 m2010_AI_066UW address13 2 20wh1_DS_416FC address493
目前已通过循环实现该需求,代码如下:
for First, Second, Add in zip(list(df2['FirstPart']),list(df2['SecondPart']),list(df2['Address'])): condition = df1['Rep'].str.contains(First) & df1['Rep'].str.contains(Second) df1.loc[condition,"Address"] = Add
注:无需以_作为分隔符,Rep字段可能存在其他分隔符。现询问是否有更优的实现方式?
更优实现方式
循环遍历的方式在数据量较大时效率较低,推荐使用Pandas的矢量化操作来实现,以下是几种可行方案:
方案一:正则匹配结合apply关联
先为df2构造包含双条件的正则匹配键,再通过apply完成关联:
import pandas as pd # 生成同时包含FirstPart和SecondPart的正则表达式 df2['match_pattern'] = df2.apply(lambda x: f'(?=.*{x["FirstPart"]})(?=.*{x["SecondPart"]})', axis=1) # 为每个Rep匹配对应的Address df1['Address'] = df1['Rep'].apply( lambda rep: df2.loc[df2['match_pattern'].str.match(rep), 'Address'].iloc[0] if not df2.loc[df2['match_pattern'].str.match(rep), 'Address'].empty else None )
方案二:利用numpy广播批量判断匹配
借助numpy的广播机制一次性完成所有匹配判断,避免逐行循环:
import pandas as pd import numpy as np # 将数据转为numpy数组,方便广播运算 rep_arr = df1['Rep'].values[:, np.newaxis] first_arr = df2['FirstPart'].values second_arr = df2['SecondPart'].values # 批量判断每个Rep是否同时包含对应的FirstPart和SecondPart match_mask = np.logical_and( np.char.find(rep_arr, first_arr) != -1, np.char.find(rep_arr, second_arr) != -1 ) # 根据匹配结果映射对应的Address df1['Address'] = df2['Address'].values[match_mask.argmax(axis=1)]
方案三:直接通过in关键字做包含判断
这是更简洁的矢量化写法,直接判断子串是否存在:
df1['Address'] = df1['Rep'].apply( lambda x: df2.loc[ (df2['FirstPart'].apply(lambda fp: fp in x)) & (df2['SecondPart'].apply(lambda sp: sp in x)), 'Address' ].iloc[0] )
这些方案都利用了Pandas和numpy的矢量化特性,相比显式循环,在数据量较大时能显著提升运行效率。
内容的提问来源于stack exchange,提问作者TRK
相关产品推荐
相关产品推荐

