【问题标题】:How to use argmin with groupby in pandas如何在熊猫中使用 argmin 和 groupby
【发布时间】:2013-11-07 23:10:57
【问题描述】:

假设我有一个这样的熊猫数据框:

  cat  val
0   a    1
1   a    6
2   a   12
3   b    2
4   b    5
5   b   11
6   c    4
7   c   22

我想知道,对于每个类别('cat' 的每个值),值最接近给定值的位置是什么,比如 5.5。我可以减去我的目标值并取绝对值,给我这样的东西:

  cat  val  val_delt
0   a    1       4.5
1   a    6       0.5
2   a   12       6.5
3   b    2       3.5
4   b    5       0.5
5   b   11       5.5
6   c    4       1.5
7   c   22      16.5

但我不知道下一步该去哪里。我的第一个想法是将 argmin() 与 groupby() 一起使用,但这会产生错误:

In [375]: df.groupby('cat').val_delt.argmin()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-375-a2c3dbc43c50> in <module>()
----> 1 df.groupby('cat').val_delt.argmin()

TypeError: 'Series' object is not callable

当然,我可以在标准 python 中想出一些可怕的 hacky 东西,我迭代 cat 的所有值,然后选择与该值对应的数据子集,执行 argmin 操作,然后找出在该行的原始数据框。但是必须有一种更优雅的方式来做到这一点。

我想要的输出是这样的:

  cat  val
1   a    6      
4   b    5       
6   c    4  

或至少一些包含相关信息的结构(例如 - {'a':1, 'b':4, 'c':6} )。我不在乎我是否取回索引值或索引位置,但我需要两者之一。我不关心取回该值 - 一旦我拥有索引子集,我总是可以稍后获得它。

【问题讨论】:

  • 这个问题很有用,有很多用例。谢谢!

标签: python pandas


【解决方案1】:

argmin() 不是 agg 函数,您可以使用 apply 来获取每个组的最近索引:

txt = """  cat  val
0   a    1
1   a    6
2   a   12
3   b    2
4   b    5
5   b   11
6   c    4
7   c   22"""

import io

df = pd.read_csv(io.BytesIO(txt), delim_whitespace=True, index_col=0)
df["val_delt"] = (df.val - 5.5).abs()
idx = df.groupby("cat").apply(lambda df:df.val_delt.argmin())
df.ix[idx, :]

输出:

cat  val  val_delt
1   a    6       0.5
4   b    5       0.5
6   c    4       1.5

【讨论】:

    【解决方案2】:

    只需添加到 HYRY 答案,您就可以使用 idxmin。 示例:

    import io
    txt = """  cat  val
    0   a    1
    1   a    6
    2   a   12
    3   b    2
    4   b    5
    5   b   11
    6   c    4
    7   c   22"""
    df = pd.read_csv(io.BytesIO(txt.encode()), delim_whitespace=True, index_col=0)
    df["val_delt"] = (df.val - 5.5).abs()
    idx = df.groupby("cat").apply(lambda df:df.val_delt.idxmin())
    df.ix[idx, :]
    

    【讨论】:

      【解决方案3】:

      你不需要申请。

      idxmin 就足够了。只需要确保你已经设置了你想要的最小值。

      >>> df['val_delt'] = (df.val - 5.5).abs()
      >>> df.set_index('val').groupby('cat').idxmin()
           val_delt
      cat          
      a           6
      b           5
      c           4
      

      【讨论】:

        【解决方案4】:

        这里的所有答案都有些正确,但没有一个以简洁、美观和 Pythonic 的方式做到这一点。我在这里留下了一个明确的方法来做到这一点。

        >>> indx = df.groupby('cat')['val_delt'].idxmin()
        >>> df.loc[indx]
        
          cat  val  val_delt
        1   a    6       0.5
        4   b    5       0.5
        6   c    4       1.5
        

        【讨论】:

        【解决方案5】:

        您可以将df.groupby('cat').val_delt.argmin() 替换为df.sort_values(['cat', 'val_delt']).groupby('cat').head(1)。本质上,这是按两列对 DataFrame 进行排序(cat,然后是val_delt)。

        代码

        df = pd.DataFrame([['a', 1], ['a', 6], ['a', 12], ['b', 2], ['b', 5], ['b', 11], ['c', 4], ['c', 22]], columns=['cat', 'val'])
        df['val_delt'] = (df.val - 5.5).abs()
        df.sort_values(['cat', 'val_delt']).groupby('cat').head(1)
        

        结果

          cat  val  val_delt
        1   a    6       0.5
        4   b    5       0.5
        6   c    4       1.5
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-10
          • 2018-01-06
          • 1970-01-01
          • 2021-06-16
          • 2013-10-24
          • 2013-06-06
          • 2019-01-18
          • 2020-09-08
          相关资源
          最近更新 更多