Python Pandas:使用groupby()和agg()时,顺序是否保留?

78

我经常使用pandas的agg()函数对数据框中的每一列运行汇总统计。例如,以下是生成平均值和标准差的方法:

df = pd.DataFrame({'A': ['group1', 'group1', 'group2', 'group2', 'group3', 'group3'],
                   'B': [10, 12, 10, 25, 10, 12],
                   'C': [100, 102, 100, 250, 100, 102]})

>>> df
[output]
        A   B    C
0  group1  10  100
1  group1  12  102
2  group2  10  100
3  group2  25  250
4  group3  10  100
5  group3  12  102

在这两种情况下,单独的行发送到agg函数的顺序并不重要。但考虑以下示例:

df.groupby('A').agg([np.mean, lambda x: x.iloc[1] ])

[output]

        mean  <lambda>  mean  <lambda>
A                                     
group1  11.0        12   101       102
group2  17.5        25   175       250
group3  11.0        12   101       102
在这种情况下,Lambda函数能够正常运行,输出每个组中的第二行。然而,在pandas文档中我没有发现任何暗示这在所有情况下都是正确的。我想使用agg()和加权平均函数,因此我希望确保进入该函数的行与它们在原始数据帧中出现的顺序相同。
有人知道吗,最好是通过文档或pandas源代码的某个地方,可以保证这是正确的吗?

1
是的,我看不到任何保证文档中有序性的内容,所以依赖它似乎有点不明智。如果排序由您的 B 列反映,则可以在 lambda 中对每个组按 B 进行排序以确保顺序正确。 - Marius
不幸的是,我想保持行按未包含在聚合中的列排序。在 agg() 调用之前,数据框已经排序,因此只有在 groupby() 的一部分重新排序时才会出现问题。 - BringMyCakeBack
6个回答

47
为了维持秩序,您需要使用 .groupby(..., sort=False)。在您的情况下,分组列已经排序,所以这并没有什么区别,但通常必须使用 sort=False 标志:
为了保持秩序,请使用 .groupby(..., sort=False)。如果您的分组列已经排序,则不会有任何差异,但通常都应该使用 sort=False 标志:
 df.groupby('A', sort=False).agg([np.mean, lambda x: x.iloc[1] ])

8
groupby 有一个 sort= 标志,但这与对组本身的排序有关,而不是组内观测值的排序。 - The Unfun Cat
1
考虑到这经常被使用,他们应该将其设为默认参数。 - NaN
3
具有讽刺意味的是,文档还说“关闭此功能可以获得更好的性能。”这更加证明了为什么它应该是一个可选功能,而不是默认功能。最重要的是,它会对调用者可能不期望的数据进行更改。 - Roland Pihlakas
1
从pandas 1.5.3版本开始,“sort”默认为True。这与我的预期相反。 - BenSeedGangMu

41

查看这个改进问题

简短回答是,groupby将保留传入的顺序。你可以通过以下代码验证:

In [20]: df.sort_index(ascending=False).groupby('A').agg([np.mean, lambda x: x.iloc[1] ])
Out[20]: 
           B             C         
        mean <lambda> mean <lambda>
A                                  
group1  11.0       10  101      100
group2  17.5       10  175      100
group3  11.0       10  101      100

然而,对于重新采样而言,情况并非如此,因为它需要一个单调索引(它可以使用非单调索引,并将其排序)。

虽然groupby有一个“sort=”标志,但这只涉及组本身的排序,而不是组内观测值的排序。

FYI:df.groupby('A').nth(1)是安全的获取组中第二个值的方法(因为您上面的方法在组具有<2个元素时会失败)。


感谢您的澄清和问题链接!我最初使用iloc作为示例,因为我无法弄清楚如何将nth()传递给agg()调用(因为此时x是一个系列)。除了作为DataFrame成员函数之外,还有其他调用nth()的方法吗? - BringMyCakeBack
nth仅在分组后定义。你说的“除了DataFrame成员函数”是什么意思? - Jeff
1
我的意思是我不知道如何将nth()作为发送到agg()的函数列表中的一个函数进行传递。你不能这样做.agg([np.mean, nth])DataFrame.nth()lambda x: x.nth(2)。这就是我使用iloc的原因,尽管它会抛出索引错误。最好的方法可能是不要试图在一步中完成所有操作;先使用nth()再使用agg(),然后合并它们。 - BringMyCakeBack

30

5

3

很不幸,这个问题的答案是否定的。在过去的几天里,我创建了一个非均匀分块算法,并发现它无法保留顺序,因为groupby会引入子框架,其中每个框架的键是组输入。所以你最终会得到:

allSubFrames = df.groupby("myColumnToOrderBy")
for orderKey, individualSubFrame in allSubFrames:
     do something...

由于使用字典,您将失去排序信息。

如果您之后要执行排序,如上所述,对于大量数据集,我刚刚进行了测试,您最终会得到O(n log n)的计算。

然而,我发现,如果您有按顺序排列的时间序列数据,其中您想保留顺序,则最好将排序列更改为列表,然后创建一个计数器来记录每个时间序列中的第一项。这样可以得到O(n)的计算。

因此,本质上,如果您使用的是相对较小的数据集,则上面提出的答案是合理的,但如果使用大数据集,则需要考虑避免使用groupby和sort。而是使用:list(df['myColumnToOrderBy'])并迭代它。


1
请问您能否在您的回答中添加一个简单的可工作代码示例? - Nimpo
不行,因为你需要一个庞大的数据集,而且我花了很长时间来解决这个问题,现在也不再从事那个项目了。 - Eamonn Kenny

-1
更简单的方法:
  import pandas as pd
  pd.pivot_table(df,index='A',aggfunc=(np.mean))

输出:

            B    C
     A                
   group1  11.0  101
   group2  17.5  175
   group3  11.0  101

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接