【问题标题】:Why does groupby operations behave differently为什么 groupby 操作的行为不同
【发布时间】:2020-08-31 17:45:59
【问题描述】:

在使用 pandas groupby functions 并在 groupby 之后操作输出时,我注意到某些函数在返回的索引以及如何操作方面表现不同。

假设我们有一个包含以下信息的数据框:

    Name   Type  ID
0  Book1  ebook   1
1  Book2  paper   2
2  Book3  paper   3
3  Book1  ebook   1
4  Book2  paper   2

如果我们这样做

df.groupby(["Name", "Type"]).sum()  

我们得到一个DataFrame

             ID
Name  Type     
Book1 ebook   2
Book2 paper   4
Book3 paper   3

其中包含一个 MultiIndex,其中包含 groupby 中使用的列:

MultiIndex([('Book1', 'ebook'),
            ('Book2', 'paper'),
            ('Book3', 'paper')],
           names=['Name', 'Type'])

还有一列名为ID

但如果我应用size() 函数,结果是Series

Name   Type 
Book1  ebook    2
Book2  paper    2
Book3  paper    1
dtype: int64

最后,如果我执行pct_change(),我们只会得到结果 DataFrame 列:

    ID
0   NaN
1   NaN
2   NaN
3   0.0
4   0.0

TL;博士。我想知道为什么有些函数返回 Series 而有些函数返回 DataFrame,因为这让我在处理同一个 DataFrame 中的不同操作时感到困惑。

【问题讨论】:

    标签: python pandas dataframe group-by


    【解决方案1】:

    输出不同,因为 聚合 不同,而这些主要控制返回的内容。想想等效的数组。数据相同,但一个“聚合”返回单个标量值,另一个返回与输入大小相同的数组

    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
    

    对于返回数组的聚合(如cumsumpct_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
    

    【讨论】:

    • 非常感谢您如此专注和具有教育意义的回应。
    【解决方案2】:

    来自文档

    Size

    Returns
    Series
    Number of rows in each group.
    

    对于sum,由于你没有传递sum的列,所以它会返回没有groupby键的数据框

    df.groupby(["Name", "Type"])['ID'].sum()  # return Series
    

    diffpct_change这样的函数不是agg,它将返回与原始数据框相同的index的值,对于countmeansum它们是agg,返回value 和 groupby key 作为索引

    【讨论】:

    • 我明白了,但是缺乏标准是有原因的吗?如果操作返回相同的结构会不会更容易?
    • @GabrielZiegler 取决于函数,例如 diff 将返回每一行,但 sum 会将整个 groupby 值视为一个输出。
    猜你喜欢
    • 2012-04-30
    • 2017-06-03
    • 2014-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-27
    相关资源
    最近更新 更多