为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

叮当猫咪 2021-03-30 12:13:13
我正在尝试在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 Currency0   20561   EUR1   20562   EUR2   20563   EUR3   20564   USDprint(df2)    Ref         Type    Amount  Comment0   20561       01      150     bla1   INV20562    03      175     bla2   INV20563BG  04      160     bla3   20564       02      180     bla现在,我想创建一个新的数据框(df3),根据发票编号将两者合并。问题在于,发票编号在df2 ['Ref']中并不总是“完全匹配”,而有时却是“部分匹配”。因此,“发票”上的联接不会提供所需的输出,因为它不会复制发票20562和20563的数据,请参见下文:df3 = df1.join(df2.set_index('Ref'), on='Invoice')print(df3)    Invoice Currency    Type    Amount  Comment0   20561   EUR         01       150    bla1   20562   EUR         NaN      NaN    NaN2   20563   EUR         NaN      NaN    NaN3   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    Amount0   20561   EUR         1501   20562   EUR         1752   20563   EUR         1603   20564   USD         180可以以某种更优雅的方式使用str.contains()吗?提前非常感谢您的帮助!
查看完整描述

2 回答

?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

这是使用的一种方式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 Amount

0      EUR   20561    150

1      EUR   20562    175

2      EUR   20563    160

3      USD   20564    180


查看完整回答
反对 回复 2021-04-13
?
慕少森

TA贡献2019条经验 获得超9个赞

这是两个可选的解决方案,都使用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 re

df5 = 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 Amount

0      EUR   20561    150

1      EUR   20562    175

2      EUR   20563    160

3      USD   20564    180

注意:所提供的正则表达式解决方案假定发票号始终为5位数字,并且仅出现此类情况中的第一个。解决方案1更健壮,因为它直接比较字符串。如果需要,可以将正则表达式解决方案改进为更健壮。


查看完整回答
反对 回复 2021-04-13
  • 2 回答
  • 0 关注
  • 211 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信