簡體   English   中英

Pandas:加入部分字符串匹配,如 Excel VLOOKUP

[英]Pandas: join on partial string match, like 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() 可以以更優雅的方式使用嗎? 非常感謝您的幫助!

這是使用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

這里有兩個替代解決方案,都使用 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_m4df_m5都將打印

  Currency Invoice Amount
0      EUR   20561    150
1      EUR   20562    175
2      EUR   20563    160
3      USD   20564    180

注意:提供的正則表達式解決方案假定發票號碼始終為 5 位數字,並且只采用第一個此類事件。 解決方案 1 更健壯,因為它直接比較字符串。 如果需要,可以改進正則表達式解決方案以使其更加健壯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM