我正在尝试在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()可以以某种更优雅的方式使用吗?提前非常感谢您的帮助!
答案 0 :(得分:1)
这是使用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
答案 1 :(得分:1)
这里有两个替代解决方案,都使用了熊猫的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更健壮,因为它直接比较字符串。 如果需要,可以将regex解决方案改进为更强大。