输出不同,因为 聚合 不同,而这些主要控制返回的内容。想想等效的数组。数据相同,但一个“聚合”返回单个标量值,另一个返回与输入大小相同的数组
import numpy as np
np.array([1,2,3]).sum()
#6
np.array([1,2,3]).cumsum()
#array([1, 3, 6], dtype=int32)
DataFrameGroupBy 对象的聚合也是如此。 groupby 的第一部分所做的就是创建从 DataFrame 到组的映射。由于这并没有真正做任何事情,因此具有不同操作的相同 groupby 没有理由需要返回相同类型的输出(见上文)。
gp = df.groupby(["Name", "Type"])
# Haven't done any aggregations yet...
这里的另一个重要部分是我们有一个 DataFrameGroupBy 对象。还有 SeriesGroupBy 对象,这种差异可能会改变回报。
gp
#<pandas.core.groupby.generic.DataFrameGroupBy object>
那么当你聚合时会发生什么?
使用DataFrameGroupBy,当您选择一个聚合(如sum),每个组折叠为一个值时,返回将是一个DataFrame,其中索引是唯一的分组键。返回是一个DataFrame,因为我们提供了一个 DataFrameGroupBy 对象。 DataFrames 可以有多个列,如果有另一个数字列,它也会聚合它,因此需要 DataFrame 输出。
gp.sum()
# ID
#Name Type
#Book1 ebook 2
#Book2 paper 4
#Book3 paper 3
另一方面,如果您使用 SeriesGroupBy 对象(选择带有 [] 的单个列),那么您将再次获得一个带有唯一组键索引的 Series。
df.groupby(["Name", "Type"])['ID'].sum()
|------- SeriesGroupBy ----------|
#Name Type
#Book1 ebook 2
#Book2 paper 4
#Book3 paper 3
#Name: ID, dtype: int64
对于返回数组的聚合(如cumsum、pct_change),DataFrameGroupBy 将返回一个 DataFrame,而 SeriesGroupBy 将返回一个系列。但是索引不再是唯一的组键。这是因为这没有什么意义。通常,您希望在组内进行计算,然后将结果分配回给原始 DataFrame。因此,返回的索引就像您为聚合提供的原始 DataFrame 一样。这使得创建这些列变得非常简单,因为 pandas 会处理所有的对齐方式
df['ID_pct_change'] = gp.pct_change()
# Name Type ID ID_pct_change
#0 Book1 ebook 1 NaN
#1 Book2 paper 2 NaN
#2 Book3 paper 3 NaN
#3 Book1 ebook 1 0.0 # Calculated from row 0 and aligned.
#4 Book2 paper 2 0.0
但是size 呢?那个有点奇怪。组的size 是一个标量。组有多少列或这些列中的值是否丢失并不重要,因此向其发送 DataFrameGroupBy 或 SeriesGroupBy 对象是无关紧要的。因此pandas 将始终返回Series。再次作为返回标量的组级别聚合,让返回由唯一组键索引是有意义的。
gp.size()
#Name Type
#Book1 ebook 2
#Book2 paper 2
#Book3 paper 1
#dtype: int64
最后为了完整性,尽管像 sum 这样的聚合返回单个标量值,但将这些值带回原始 DataFrame 中该组的每一行通常很有用。但是,正常 .sum 的返回具有不同的索引,因此它不会对齐。您可以 merge 将值返回到唯一键上,但 pandas 提供了 transform 这些聚合的能力。由于这里的目的是将其带回原始 DataFrame,因此 Series/DataFrame 的索引就像原始输入一样
gp.transform('sum')
# ID
#0 2 # Row 0 is Book1 ebook which has a group sum of 2
#1 4
#2 3
#3 2 # Row 3 is also Book1 ebook which has a group sum of 2
#4 4