2016-06-09 70 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