Pandas:在较小的数据框中有条件地匹配行

时间:2015-11-17 19:37:11

标签: python pandas

(更新:添加desired数据框)

让我首先说我已经相当自信我几年前找到了解决这个问题的方法,但我无法重新找到解决方案。

解决类似问题但未解决我的特定问题的问题包括:

问题

假设我有一个数据框,其中包含许多我正在处理的列:

big = pd.DataFrame({'match_1': [11, 12, 51, 52]})
big
   match_1
0       11
1       12
2       51
3       52

我还有一个较小的数据框,理论上它将一些条件语句映射到所需的值:

# A smaller dataframe that we use to map values into the larger dataframe
small = pd.DataFrame({'is_even': [True, False], 'score': [10, 200]})
small
  is_even  score
0    True     10
1   False    200

此处的目标是使用条件语句将big中的每一行与small中的单行匹配。假设构造small使得big中的每一行始终只有一个匹配。 (如果small中必须有多行匹配,只需选择第一行。)

所需的输出类似于:

desired = pd.DataFrame({'match_1': [11, 12, 51, 52], 'metric': [200, 10, 200, 10]})
desired
 match_1  metric
0       11     200
1       12      10
2       51     200
3       52      10

我很确定语法看起来类似于:

big['score'] = small.loc[small['is_even'] == ( (big['match_1'] / 2) == 0), 'score']

这不起作用,因为small['is_even']是一系列长度为2的系列,而( (big['match_1'] / 2) == 0)是一系列长度为4.我要做的是,为每一行big,根据条件在small中找到匹配的一行。

如果我可以获得一个包含small中与big中每一行匹配的正确行的序列,那么我可以执行以下操作:

`big['score'] = small.loc[matching_rows, 'score']

我的问题是:如何生成序列matching rows

(我认为)不是我想要的东西:

如果bigsmall中的列仅与常量值匹配,则可以直接使用big.merge()big.groupby(),但是,在我的情况下,映射可以是任意复杂的布尔条件,例如:

(big['val1'] > small['threshold']) & (big['val2'] == small['val2']) & (big['val3'] > small['min_val']) & (big['val3'] < small['max_val'])

依赖isin()any()等的解决方案不起作用,因为条件检查可能是任意复杂的。

我当然可以为apply()创建一个更大的DataFrame函数,但同样,我很确定有一个更简单的解决方案。

答案可能归结为“计算一些中间列,直到你可以做一个简单的合并”或“只使用apply(),但我可以发誓,有一种方法可以做我上面所描述的。

2 个答案:

答案 0 :(得分:2)

一种方法是使用merge on_left不是列,而是键向量。通过将small的索引设置为is_even,它变得更简单了:

>>> small.set_index('is_even', inplace=True)
>>> condition = big['match_1'] % 2 == 0
>>> pd.merge(big, small, left_on=condition, right_index=True, how='left')
   match_1  score
0       11    200
1       12     10
2       51    200
3       52     10

答案 1 :(得分:1)

您可以使用True和False索引small,然后直接对其进行.ix次查找。不确定它比中间列/合并更整洁:

In [127]: big = pd.DataFrame({'match_1': [11, 12, 51, 52]})

In [128]: small = pd.DataFrame({'score': [10, 200]}, index=[True, False])

In [129]: big['score'] = small.ix[pd.Index(list(big.match_1 % 2 == 0))].score.values

In [130]: big
Out[130]:
   match_1  score
0       11    200
1       12     10
2       51    200
3       52     10