根据来自另一个数据框的值在多列上对pandas数据框进行子集

时间:2018-11-30 08:06:58

标签: python python-3.x pandas performance dataframe

我有两个数据框,

import pandas as pd
points = pd.DataFrame({'player':['a','b','c','d','e'],'points':[2,5,3,6,1]})
matches = pd.DataFrame({'p1':['a','c','e'], 'p2':['c', 'b', 'd']})

我只想保留数据帧匹配中的那些行,其中p1和p2都具有大于2的点。现在,我首先合并点和p1和player上的匹配项,然后合并结果数据帧和p2和player上的点。之后,在结果数据帧的两点列上应用过滤器。

new_df = pd.merge(matches, points, how = 'left', left_on = 'p1', right_on = 'player')
new_df = pd.merge(new_df, points, how = 'left', left_on = 'p2', right_on = 'player')
new_df = new_df[(new_df.points_x >2) & (new_df.points_y >2)]

这满足了我的需求,但我想知道哪种方法更好,更有效?

2 个答案:

答案 0 :(得分:1)

在这种情况下,我会避免加入并写成这样:

scorers = points.query('points > 2').player
matches.query('p1 in @scorers and p2 in @scorers')

我认为它更具可读性。

在这么小的示例中进行基准测试感觉有点愚蠢,但是在我的机器上,此方法平均运行时间为2.99毫秒,而您的原始方法运行时间为4.45毫秒。有趣的是,这是否可以更好地扩展。

我不知道您是否可以对此代码进行其他微优化,例如将scorers转换为集合。

如果您不喜欢query语法:

scorers = points[points.points > 2].player
matches[matches.p1.isin(scorers) & matches.p2.isin(scorers)]

这也有更好的性能,大约需要1.36毫秒。

答案 1 :(得分:1)

作为替代方案,您可以构建一个将玩家映射到点的系列,然后对pd.Series.map中的每个系列使用matches

s = points.set_index('player')['points']
res = matches.loc[matches.apply(lambda x: x.map(s)).gt(2).all(1)]

print(res)

  p1 p2
1  c  b