Pandas 按组聚合和列排序
Posted
技术标签:
【中文标题】Pandas 按组聚合和列排序【英文标题】:Pandas sort by group aggregate and column 【发布时间】: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
如何做到这一点?
【问题讨论】:
【参考方案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_index
和drop=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
【讨论】:
另外,我假设groupby
的sort=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
【讨论】:
以上是关于Pandas 按组聚合和列排序的主要内容,如果未能解决你的问题,请参考以下文章