熊猫:加入部分字符串匹配,例如Excel VLOOKUP

我正在尝试在Python中执行与Excel中的VLOOKUP非常相似的操作。在StackOverflow上有很多与此相关的问题,但是它们都与本用例略有不同。希望任何人都可以引导我朝着正确的方向前进。我有以下两个熊猫数据框:


df1 = pd.DataFrame({'Invoice': ['20561', '20562', '20563', '20564'],

                    'Currency': ['EUR', 'EUR', 'EUR', 'USD']})

df2 = pd.DataFrame({'Ref': ['20561', 'INV20562', 'INV20563BG', '20564'],

                    'Type': ['01', '03', '04', '02'],

                    'Amount': ['150', '175', '160', '180'],

                    'Comment': ['bla', 'bla', 'bla', 'bla']})


print(df1)

    Invoice Currency

0   20561   EUR

1   20562   EUR

2   20563   EUR

3   20564   USD


print(df2)

    Ref         Type    Amount  Comment

0   20561       01      150     bla

1   INV20562    03      175     bla

2   INV20563BG  04      160     bla

3   20564       02      180     bla

现在,我想创建一个新的数据框(df3),根据发票编号将两者合并。问题在于,发票编号在df2 ['Ref']中并不总是“完全匹配”,而有时却是“部分匹配”。因此,“发票”上的联接不会提供所需的输出,因为它不会复制发票20562和20563的数据,请参见下文:


df3 = df1.join(df2.set_index('Ref'), on='Invoice')


print(df3)

    Invoice Currency    Type    Amount  Comment

0   20561   EUR         01       150    bla

1   20562   EUR         NaN      NaN    NaN

2   20563   EUR         NaN      NaN    NaN

3   20564   USD         02       180    bla

有没有办法参加部分比赛?我知道如何用正则表达式“清理” df2 ['Ref'],但这不是我要的解决方案。使用for循环,我可以走很长一段路,但这不是Pythonic。


df4 = df1.copy()

for i, row in df1.iterrows():

    tmp = df2[df2['Ref'].str.contains(row['Invoice'])]

    df4.loc[i, 'Amount'] = tmp['Amount'].values[0]


print(df4)

Invoice     Currency    Amount

0   20561   EUR         150

1   20562   EUR         175

2   20563   EUR         160

3   20564   USD         180

可以以某种更优雅的方式使用str.contains()吗?提前非常感谢您的帮助!


叮当猫咪
浏览 212回答 2
2回答

江户川乱折腾

这是使用的一种方式pd.Series.apply,这只是一个薄薄的循环。您正在寻找“部分字符串合并”,我不确定它是否以矢量化形式存在。df4 = df1.copy()def get_amount(x):    return df2.loc[df2['Ref'].str.contains(x), 'Amount'].iloc[0]df4['Amount'] = df4['Invoice'].apply(get_amount)print(df4)  Currency Invoice Amount0      EUR   20561    1501      EUR   20562    1752      EUR   20563    1603      USD   20564    180

慕少森

这是两个可选的解决方案,都使用Pandas的merge。# Solution 1 (checking directly if 'Invoice' string is in the 'Ref' string)df4 = df2.copy()df4['Invoice'] = [val for idx, val in enumerate(df1['Invoice']) if val in df2['Ref'][idx]]df_m4 = df1.merge(df4[['Amount', 'Invoice']], on='Invoice')# Solution 2 (regex)import redf5 = df2.copy()df5['Invoice'] = [re.findall(r'(\d{5})', s)[0] for s in df2['Ref']]df_m5 = df1.merge(df5[['Amount', 'Invoice']], on='Invoice')双方df_m4并df_m5会打印  Currency Invoice Amount0      EUR   20561    1501      EUR   20562    1752      EUR   20563    1603      USD   20564    180注意:所提供的正则表达式解决方案假定发票号始终为5位数字,并且仅出现此类情况中的第一个。解决方案1更健壮,因为它直接比较字符串。如果需要,可以将正则表达式解决方案改进为更健壮。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python