2016-06-09 62 views
5

我有一個簡單的數據幀像這樣:如何將兩個數據框與「通配符」合併?

p  b 
0 a buy 
1 b buy 
2 a sell 
3 b sell 

和查找表是這樣的:

p  b v 
0 a buy 123 
1 a sell 456 
2 a  * 888 
4 b  * 789 

如何我可以合併(連接)兩個dataframes,但尊重列中的「通配符」 b,即預期的結果是:

p  b v 
0 a buy 123 
1 b buy 789 
2 a sell 456 
3 b sell 789 

我能想出這是最好的,但它是相當醜陋,詳細:

data = pd.DataFrame([ 
     ['a', 'buy'], 
     ['b', 'buy'],   
     ['a', 'sell'], 
     ['b', 'sell'],    
    ], columns = ['p', 'b']) 
lookup = pd.DataFrame([ 
     ['a', 'buy', 123], 
     ['a', 'sell', 456], 
     ['a', '*', 888], 
     ['b', '*', 789],   
], columns = ['p','b', 'v']) 

x = data.reset_index() 
y1 = pd.merge(x, lookup, on=['p', 'b'], how='left').set_index('index') 
y2 = pd.merge(x[y1['v'].isnull()], lookup, on=['p'], how='left').set_index('index') 
data['v'] = y1['v'].fillna(y2['v']) 

有沒有更智能的方法?

+0

在上面的預期結果中,爲什麼沒有任何'v'等於888的行? – unutbu

+0

好問題 - 這是因爲通配符只適用於沒有更多特定匹配的情況。 – Matthew

+1

@Matthew如果這是你創建的東西,你需要考慮數據模型。 – Merlin

回答

5

我覺得有點吸塵器清理wildcards第一:

In [11]: wildcards = lookup[lookup["b"] == "*"] 

In [12]: wildcards.pop("b") # ditch the * column, it'll confuse the later merge 

現在你可以用一個update將二者結合起來合併(無需​​):

In [13]: res = df.merge(lookup, how="left") 

In [14]: res 
Out[14]: 
    p  b  v 
0 a buy 123.0 
1 b buy NaN 
2 a sell 456.0 
3 b sell NaN 

In [15]: res.update(df.merge(wildcards, how="left"), overwrite=False) 

In [16]: res 
Out[16]: 
    p  b  v 
0 a buy 123.0 
1 b buy 789.0 
2 a sell 456.0 
3 b sell 789.0 
+1

我喜歡這個解決方案!唯一的呃逆是我有一個情況,有多個列(潛在)通配符。它不是很明顯,我可以如何擴展這個方案以適應這種情況。 – Matthew

+0

@Matthew,不幸的是,'b買888.0'應該是'b買789',但3 upvotes,:-) – Merlin

+0

@Matthew讓我解決這個問題! –

1

我覺得這直觀:

def find_lookup(lookup, p, b): 
    ps = lookup.p == p 
    bs = lookup.b.isin([b, '*']) 
    return lookup.loc[ps & bs].iloc[0].replace('*', b) 

data.apply(lambda x: find_lookup(lookup, x.loc['p'], x.loc['b']), axis=1) 

    p  b v 
0 a buy 123 
1 b buy 789 
2 a sell 456 
3 b sell 789 
+0

嗯,我喜歡你要去的地方。我認爲groupby - > apply可能會更清晰 - 讓我考慮一下吧... – Matthew

1

我找到了其他的r解決方案,受到上述一些想法的啓發(謝謝大家!)。它比我的第一次嘗試整齊,所以我會把它放在這裏,但我相信還有改進的空間。此解決方案假定查找已排序,以便通配符位於表格底部:

x = data.reset_index().merge(lookup, on=['p'], suffixes=["", "_y"]) 
x = x[(x['b'] == x['b_y']) | (x['b_y'] == '*')] 
x = x.groupby('index').first() # see note about sorting lookup! 
x[['p', 'b', 'c', 'v']] 

     p  b v 
index     
0  0 a buy 123 
1  6 b buy 789 
2  4 a sell 456 
3  7 b sell 789 
相關問題