大pandas按组合和列进行sorting

给定以下dataframe

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]: ABC 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 

我想按组合( A )按B总和,然后按C (不汇总)的值sorting。 所以基本上得到了A组的顺序

 In [28]: df.groupby('A').sum().sort('B') Out[28]: BC 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]: ABC 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 

如何才能做到这一点?

Groupby答:

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

在每个组中,对B进行求和并使用变换来广播这些值。 然后按Bsorting:

 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值进行重新sorting:

 In [2]: sort1 = df.ix[grp[['B']].transform(sum).sort('B').index] In [3]: sort1 Out[3]: ABC 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”值进行sort=False以保留步骤1中的Asorting顺序:

 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]: ABC 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 

通过使用drop=True reset_index清理df索引:

 In [7]: sort2.reset_index(0, drop=True) Out[7]: ABC 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_bsum'] = df.groupby('A')['B'].transform(sum) df.sort(['a_bsum','C'], ascending=[True, False]).drop('a_bsum', axis=1) 

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

结果:

  ABC 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

一种方法是插入一个带有总和的虚拟列来sorting:

 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]: ABC 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]: ABC 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]: ABC 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