【问题标题】:Pandas sort by group aggregate and columnPandas 按组聚合和列排序
【发布时间】:2013-02-03 04:22:54
【问题描述】:

给定以下数据框

In [31]: rand = np.random.RandomState(1)
         df = pd.DataFrame({'A': ['foo', 'bar', 'baz'] * 2,
                            'B': rand.randn(6),
                            'C': rand.rand(6) > .5})

In [32]: df
Out[32]:      A         B      C
         0  foo  1.624345  False
         1  bar -0.611756   True
         2  baz -0.528172  False
         3  foo -1.072969   True
         4  bar  0.865408  False
         5  baz -2.301539   True 

我想按B 的总和然后按C 中的值(未汇总)对它进行分组(A)。所以基本上得到A组的顺序与

In [28]: df.groupby('A').sum().sort('B')
Out[28]:             B  C
         A               
         baz -2.829710  1
         bar  0.253651  1
         foo  0.551377  1

然后通过 True/False,最终看起来像这样:

In [30]: df.ix[[5, 2, 1, 4, 3, 0]]
Out[30]: A         B      C
    5  baz -2.301539   True
    2  baz -0.528172  False
    1  bar -0.611756   True
    4  bar  0.865408  False
    3  foo -1.072969   True
    0  foo  1.624345  False

如何做到这一点?

【问题讨论】:

    标签: python sorting group-by dataframe pandas


    【解决方案1】:

    按 A 分组:

    In [0]: grp = df.groupby('A')
    

    在每个组中,对 B 求和并使用变换广播值。然后按 B 排序:

    In [1]: grp[['B']].transform(sum).sort('B')
    Out[1]:
              B
    2 -2.829710
    5 -2.829710
    1  0.253651
    4  0.253651
    0  0.551377
    3  0.551377
    

    通过从上面传递索引来索引原始 df。这将通过 B 值的总和对 A 值重新排序:

    In [2]: sort1 = df.ix[grp[['B']].transform(sum).sort('B').index]
    
    In [3]: sort1
    Out[3]:
         A         B      C
    2  baz -0.528172  False
    5  baz -2.301539   True
    1  bar -0.611756   True
    4  bar  0.865408  False
    0  foo  1.624345  False
    3  foo -1.072969   True
    

    最后,使用sort=False 选项对“A”组中的“C”值进行排序,以保留第 1 步中的 A 排序顺序:

    In [4]: f = lambda x: x.sort('C', ascending=False)
    
    In [5]: sort2 = sort1.groupby('A', sort=False).apply(f)
    
    In [6]: sort2
    Out[6]:
             A         B      C
    A
    baz 5  baz -2.301539   True
        2  baz -0.528172  False
    bar 1  bar -0.611756   True
        4  bar  0.865408  False
    foo 3  foo -1.072969   True
        0  foo  1.624345  False
    

    使用reset_indexdrop=True 清理df 索引:

    In [7]: sort2.reset_index(0, drop=True)
    Out[7]:
         A         B      C
    5  baz -2.301539   True
    2  baz -0.528172  False
    1  bar -0.611756   True
    4  bar  0.865408  False
    3  foo -1.072969   True
    0  foo  1.624345  False
    

    【讨论】:

    • 另外,我假设groupbysort=False 标志会返回一个任意的,不一定是排序的顺序(我想我出于某种原因将它们与python 字典相关联)。但是这个答案意味着该标志可以保证保留数据帧行的原始顺序?
    • 我 99% 确定它会保留组首次出现时的顺序。我没有任何代码来支持这一点,但一些快速测试证实了这种直觉。
    • 感谢@Zelazny7 的回答。这正是我想要的。但是,似乎在最新的 pandas 包中,要实现相同的Out[7],应该将inplace=True 添加到Input[7] 中的参数中。
    • 添加更多信息:sort() 现已弃用。建议使用 DataFrame.sort_values()
    【解决方案2】:

    这里有一个更简洁的方法...

    df['a_bsum'] = df.groupby('A')['B'].transform(sum)
    df.sort(['a_bsum','C'], ascending=[True, False]).drop('a_bsum', axis=1)
    

    第一行使用分组和向数据框添加一列。第二行执行排序,然后删除多余的列。

    结果:

        A       B           C
    5   baz     -2.301539   True
    2   baz     -0.528172   False
    1   bar     -0.611756   True
    4   bar      0.865408   False
    3   foo     -1.072969   True
    0   foo      1.624345   False
    

    注意:sort 已弃用,请改用sort_values

    【讨论】:

      【解决方案3】:

      一种方法是插入一个带有总和的虚拟列以进行排序:

      In [10]: sum_B_over_A = df.groupby('A').sum().B
      
      In [11]: sum_B_over_A
      Out[11]: 
      A
      bar    0.253652
      baz   -2.829711
      foo    0.551376
      Name: B
      
      in [12]: df['sum_B_over_A'] = df.A.apply(sum_B_over_A.get_value)
      
      In [13]: df
      Out[13]: 
           A         B      C  sum_B_over_A
      0  foo  1.624345  False      0.551376
      1  bar -0.611756   True      0.253652
      2  baz -0.528172  False     -2.829711
      3  foo -1.072969   True      0.551376
      4  bar  0.865408  False      0.253652
      5  baz -2.301539   True     -2.829711
      
      In [14]: df.sort(['sum_B_over_A', 'A', 'B'])
      Out[14]: 
           A         B      C   sum_B_over_A
      5  baz -2.301539   True      -2.829711
      2  baz -0.528172  False      -2.829711
      1  bar -0.611756   True       0.253652
      4  bar  0.865408  False       0.253652
      3  foo -1.072969   True       0.551376
      0  foo  1.624345  False       0.551376
      

      也许你会删除虚拟行:

      In [15]: df.sort(['sum_B_over_A', 'A', 'B']).drop('sum_B_over_A', axis=1)
      Out[15]: 
           A         B      C
      5  baz -2.301539   True
      2  baz -0.528172  False
      1  bar -0.611756   True
      4  bar  0.865408  False
      3  foo -1.072969   True
      0  foo  1.624345  False
      

      【讨论】:

      • 确定我在这里看到了一些巧妙的方法(基本上允许使用键进行排序),但我似乎找不到它。
      • 很高兴知道有更好的方法来做df.A.map(dict(zip(sum_B_over_A.index, sum_B_over_A))) :)(应该是get_value,不是吗?)。也不知道按列下降,非常感谢。 (尽管出于某种原因,我更喜欢没有虚拟列的版本)
      • @BirdJaguarIV 哎呀错字:)。是的,使用假人似乎很愚蠢(我本来可以更聪明地使用我的应用 [12] 来完成它,而且它可能更有效,但我决定我不想成为那个人阅读它...)。就像我说的那样,我认为有一种聪明的方法可以进行这种复杂的排序:s
      • 您没有按 C 列排序。
      • @MarkByers 您可以将“C”附加到要排序的列列表中,所以它是:df.sort(['sum_B_over_A', 'A', 'B', 'C'])... 我真的应该添加指向sort 文档的链接。
      【解决方案4】:

      这个问题很难理解。但是,按 A 分组并按 B 求和,然后按降序对值进行排序。 A 列的排序顺序取决于 B。然后,您可以使用过滤来创建一个新的数据框过滤器,按 A 值对数据框进行排序。

      rand = np.random.RandomState(1)
      df = pd.DataFrame({'A': ['foo', 'bar', 'baz'] * 2,
                              'B': rand.randn(6),
                              'C': rand.rand(6) > .5})
      grouped=df.groupby('A')['B'].sum().sort_values(ascending=False)
      print(grouped)
      print(grouped.index.get_level_values(0))
      

      输出:

      A
      foo    0.551377
      bar    0.253651
      baz   -2.829710
      

      【讨论】:

        猜你喜欢
        • 2014-11-17
        • 2014-02-07
        • 1970-01-01
        • 2017-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-07
        • 2021-01-06
        相关资源
        最近更新 更多