【问题标题】:Pandas: conditionally matching rows in a smaller data framePandas:有条件地匹配较小数据框中的行
【发布时间】:2016-02-19 07:58:43
【问题描述】:

(更新:添加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(),但我可以发誓,有一种方法可以做我上面描述的事情。

【问题讨论】:

  • 数据框是否在数据库中派生?如果是这样,您的条件匹配(一对多)可以在 SQL 中处理。 pandas 无需在内存中重组。

标签: python pandas


【解决方案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

【讨论】:

    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 2018-05-14
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-21
      • 2021-10-22
      • 2020-11-25
      相关资源
      最近更新 更多