(更新:添加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
?
如果big
和small
中的列仅与常量值匹配,则可以直接使用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()
,但我可以发誓,有一种方法可以做我上面所描述的。
答案 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