pandas【groupby函数】用法总结

groupby函数用法

  • 函数定义
  • axis参数的作用
  • level参数的作用
  • as_index参数的作用
  • sort参数的作用
  • group_keys参数的作用
  • squeeze参数的作用
  • observed参数的作用
  • dropna参数的作用

函数定义

pandas中的groupby函数用于根据一个或者多个字段划分分组。
首先了解一下groupby函数的定义:

chipo.groupby(
    by=None,  # 进行分组的字段
    axis: 'Axis' = 0, # 轴,axis=0表示按照行进行分组,axis=1表示按照列进行分组,默认按行
    level: 'IndexLabel | None' = None, # 指定分组的层级,处理多层索引时有用
    as_index: 'bool' = True, # 用于控制分组后的结果是否以分组列作为索引。
    sort: 'bool' = True, # 用于指定分组后的结果是否按照分组键进行排序
    group_keys: 'bool | lib.NoDefault' = <no_default>, # 用于控制分组键是否显示在结果中。
    squeeze: 'bool | lib.NoDefault' = <no_default>, # 控制返回结果的形状,True时只保留每个组中的一列,而将其他列作为索引,False 或未指定时,表示保留所有聚合后的列
    observed: 'bool' = False, # 控制分组键的观察方式。False时,对于不存在的一个分组将会在结果中显示,True时,对于不存在的一个分组将会在结果中忽略,注意:observed参数仅仅在`Categorical`类型的分组键上有用,对于其他类型的键,将忽略`observed`参数的设置。
    dropna: 'bool' = True, # 控制在分组过程中是否删除包含缺失值(NaN)的分组
) -> 'DataFrameGroupBy'

对各个参数的解释:
by=None, 进行分组的字段
axis: 'Axis' = 0, 轴,axis=0表示按照行进行分组,axis=1表示按照列进行分组,默认按行
level: 'IndexLabel | None' = None, 指定分组的层级,处理多层索引时有用
as_index: 'bool' = True, 用于控制分组后的结果是否以分组列作为索引。
sort: 'bool' = True, 用于指定分组后的结果是否按照分组键进行排序
group_keys: 'bool | lib.NoDefault' = , 用于控制分组键是否显示在结果中。
squeeze: 'bool | lib.NoDefault' = , 控制返回结果的形状,True时只保留每个组中的一列,而将其他列作为索引,False 或未指定时,表示保留所有聚合后的列,将被弃用。
observed: 'bool' = False, 控制分组键的观察方式。False时,对于不存在的一个分组将会在结果中显示,True时,对于不存在的一个分组将会在结果中忽略,注意:observed参数仅仅在Categorical类型的分组键上有用,对于其他类型的键,将忽略observed参数的设置
dropna: 'bool' = True, 控制在分组过程中是否删除包含缺失值(NaN)的分组

接下来我将分别针对每一个参数给出例子:

axis参数的作用

axis参数,它用于指定在哪个轴上进行分组操作。
axis参数可以取两个值:0或1。默认情况下,axis参数的取值为0,表示按行进行分组。取1表示按列进行分组。
需要注意的是,axis=1参数通常需要和groupby函数的其他参数配合使用,以指定需要进行聚合操作的列。
我找不到axis=1时的例子,难受…

level参数的作用

level参数用于指定分组的层级,处理多层索引时非常有用。
假设我们有一个包含多层索引的数据框(DataFrame),其中有两个层级:A和B。我们想要按照层级A进行分组操作。

import pandas as pd

# 创建一个包含多层索引的数据框
df = pd.DataFrame({
    'A': ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'],
    'B': ['one', 'one', 'two', 'two', 'two', 'one', 'two', 'one'],
    'C': [1, 2, 3, 4, 5, 6, 7, 8],
    'D': [10, 20, 30, 40, 50, 60, 70, 80]
})
df = df.set_index(['A', 'B']) # 设置A,B列为索引列,set_index()用于将一个或多个列设置为索引(index)

下面是我们生成的数据框:

       C   D
A   B       
foo one  1  10
bar one  2  20
foo two  3  30
bar two  4  40
foo two  5  50
bar one  6  60
foo two  7  70
    one  8  80

现在,我们将使用groupby函数以及level参数按照层级A对数据进行分组:

grouped = df.groupby(level='A')

这将返回一个GroupBy对象,它表示按照层级A进行分组后的数据。我们打印一下GroupBy对象,并且使用for循环打印GroupBy对象的每一个分组:

print(grouped) # 打印grouped对象,(打印出来的是内存地址)
for group_name,group_data in grouped: # 遍历grouped中的每一个分组,并打印
    print(f'Group: {group_name}')
    print(group_data)
    print()

输出结果如下:

<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000002A3E8DC7520>
Group: bar
         C   D
A   B         
bar one  2  20
    two  4  40
    one  6  60

Group: foo
         C   D
A   B         
foo one  1  10
    two  3  30
    two  5  50
    two  7  70
    one  8  80

我们可以通过应用聚合函数(例如sum、mean等)来进一步处理分组后的数据。例如,我们计算每个分组的总和:

grouped_sum = grouped.sum()

这将返回以下结果:

     C    D
A          
bar  12   120
foo  24   220

我们也可以通过指定多个层级来进行多级分组。例如,我们按照层级A和层级B进行分组:

grouped = df.groupby(level=['A', 'B'])

这将返回一个按照层级A和层级B进行分组后的GroupBy对象。
我们计算每个分组的总和:

grouped_sum = grouped.sum()

这将返回以下结果:

          C    D
A   B           
bar one   8   80
    two   4   40
foo one   9   90
    two  15  150

as_index参数的作用

as_index参数用于控制分组后的结果是否以分组列作为索引。默认为True,即分组列将作为结果 DataFrame 的索引。
首先使用下面的代码生成样例数据:

import random
import pandas as pd
import numpy as np

df = pd.DataFrame(
    {
        'name': ['bob' + str(i) for i in range(1, 11)],
        'company': [random.choice(['A', 'B', 'C']) for i in range(10)],
        'salary': np.random.randint(5,50,10),
        'age':np.random.randint(15,50,10)
    }

)
print(df)

生成的样本数据如下:

    name company  salary  age
0   bob1       B       8   33
1   bob2       C      16   16
2   bob3       B      20   38
3   bob4       C      34   48
4   bob5       A      11   25
5   bob6       A      46   35
6   bob7       C      18   23
7   bob8       A      18   49
8   bob9       B       7   18
9  bob10       A      23   19

接下来根据company分组,并设置as_index=True,并且计算每一个分组的salary的和:

grouped = df.groupby('company',as_index=True)
salary_sum = grouped['salary'].sum()
print(salary_sum)
A_salary = salary_sum.loc['A'] # 此时分组列将作为结果 DataFrame 的索引,可以通过索引'A'输出A的总工资
print(A_salary)

得到的结果如下:

company
A    154
B    156
Name: salary, dtype: int32
154

可以看到,此时分组列将作为结果 DataFrame 的索引,可以通过索引’A’输出A的总工资,但是聚合列的列名丢失了。

接下来还是根据company分组,但是设置as_index=False,并且计算每一个分组的salary的和:

grouped = df.groupby('company',as_index=True)
salary_sum = grouped['salary'].sum()
print(salary_sum)
A_salary = salary_sum.loc['A'] # 此时分组列将不会作为结果 DataFrame 的索引,也就不可以通过索引'A'输出A的总工资了
print(A_salary)

得到的结果如下:

  company  salary
0       A     235
1       B      16
2       C       5
KeyError: 'A' # 报错

可以看到,此时分组列将不会作为结果 DataFrame 的索引,并且不可以通过索引’A’输出A的总工资,并且聚合列的列名没有丢失。
此时可以使用set_index()函数将’company’列重新变成索引,这样就可以再次使用company索引了:

salary_sum = salary_sum.set_index('company')
print(salary_sum)
print(salary_sum.loc['A'])

运行结果如下:

         salary
company        
A           137
B            87
C            32

salary    137
Name: A, dtype: int32

sort参数的作用

sort 参数用于指定分组后的结果是否按照分组键进行排序,默认为 True,表示按照分组键排序(升序排序)。当 sort 参数设置为 False 时,分组结果将按照原始数据的顺序保留。
sort=False时的一个例子:

import random
import pandas as pd
import numpy as np

df = pd.DataFrame(
    {
        'name': ['bob' + str(i) for i in range(1, 11)],
        'company': [random.choice(['A', 'B', 'C']) for i in range(10)],
        'salary': np.random.randint(5,50,10),
        'age':np.random.randint(15,50,10)
    }

)
grouped = df.groupby('company',sort=False)
for name,group in grouped:
    print(name)
    print(group)

结果如下:

B
   name company  salary  age
0  bob1       B      45   48
1  bob2       B      10   16
2  bob3       B      42   15
5  bob6       B      25   18
C
    name company  salary  age
3   bob4       C      19   40
7   bob8       C      23   36
8   bob9       C      23   48
9  bob10       C      49   49
A
   name company  salary  age
4  bob5       A      49   15
6  bob7       A      35   41

可以看到分组结果并没有按照分组键company进行排序(键的排列顺序是:B,C,A)。
还是使用上面的数据样本,当sort=True时的一个例子:

grouped = df.groupby('company',sort=True)
for name,group in grouped:
    print(name)
    print(group)

运行结果如下:

A
    name company  salary  age
1   bob2       A      14   36
2   bob3       A      29   37
7   bob8       A      12   25
8   bob9       A      37   18
9  bob10       A      28   35
B
   name company  salary  age
0  bob1       B      40   41
3  bob4       B      24   16
4  bob5       B      49   38
5  bob6       B      12   37
C
   name company  salary  age
6  bob7       C      41   22

进程已结束,退出代码0

可以看到分组结果已经按照分组键company进行排序(键的排列顺序是:A,B,C)。

group_keys参数的作用

group_keys参数用于控制分组键是否显示在结果中,group_keys参数的默认值为True,这意味着在分组结果中会显示分组键。也就是说,对于每个分组,将在结果中添加一个带有分组键的索引层级。这对于快速查看分组结果以及后续操作是很有用的。

我找了很多资料和例子,发现group_keys无论是只在分组的时候使用,还是配合聚合函数使用,全部不起作用。
比如group_keys配合聚合函数使用的结果如下:

import pandas as pd

# 创建一个包含多层索引的数据框
df = pd.DataFrame({
    'A': ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'],
    'B': ['one', 'one', 'two', 'two', 'two', 'one', 'two', 'one'],
    'C': [1, 2, 3, 4, 5, 6, 7, 8],
    'D': [10, 20, 30, 40, 50, 60, 70, 80]
})
df = df.set_index(['A', 'B']) # 设置A,B列为索引列,set_index()用于将一个或多个列设置为索引(index)
# 对Category列进行分组,并计算每个分组的平均值
grouped = df.groupby(['A', 'B'], group_keys=True).mean()
print(grouped)
print('----------------')
grouped = df.groupby(['A', 'B'], group_keys=False).mean()
print(grouped)

group_keys=Truegroup_keys=False的运行结果都是一样的:

           C     D
A   B             
bar one  4.0  40.0
    two  4.0  40.0
foo one  4.5  45.0
    two  5.0  50.0
----------------
           C     D
A   B             
bar one  4.0  40.0
    two  4.0  40.0
foo one  4.5  45.0
    two  5.0  50.0

进程已结束,退出代码0

又或者group_keys仅仅在分组的时候使用,而不配合聚合函数使用时的结果:

import random
import pandas as pd
import numpy as np

df = pd.DataFrame(
    {
        'name': ['bob' + str(i) for i in range(1, 11)],
        'company': [random.choice(['A', 'B', 'C']) for i in range(10)],
        'salary': np.random.randint(5,50,10),
        'age':np.random.randint(15,50,10)
    }

)
grouped = df.groupby('company',group_keys=True)
for name,group in grouped:
    print(name)
    print(group)
    print()
print('-------------------------------')
grouped = df.groupby('company',group_keys=False)
for name,group in grouped:
    print(name)
    print(group)
    print()

group_keys=Truegroup_keys=False的运行结果仍然都是一样的:

A
    name company  salary  age
1   bob2       A      43   45
7   bob8       A      46   25
8   bob9       A      37   20
9  bob10       A      16   37

B
   name company  salary  age
0  bob1       B      12   22
2  bob3       B      35   16
3  bob4       B      45   26
4  bob5       B      19   41
5  bob6       B      39   17

C
   name company  salary  age
6  bob7       C      33   21

-------------------------------
A
    name company  salary  age
1   bob2       A      43   45
7   bob8       A      46   25
8   bob9       A      37   20
9  bob10       A      16   37

B
   name company  salary  age
0  bob1       B      12   22
2  bob3       B      35   16
3  bob4       B      45   26
4  bob5       B      19   41
5  bob6       B      39   17

C
   name company  salary  age
6  bob7       C      33   21

最后我在下面的文章找到了一个例子:
python - pandas.groupby 的 group_keys 参数实际上是做什么的?

根据上面的文章描述,group_keys函数只有在和apply()函数配合时才有可能起作用,为什么是有可能呢?先来看看下面这个例子:

apply() 函数用于在 DataFrame 或 Series 的每个元素上应用一个函数,并返回一个新的 DataFrame 或 Series 对象
groupby()方法对数据进行分组之后,使用apply()方法可以用于在每个组上应用自定义函数

我首先自定义了一个sum_salary()函数,实现了聚合函数sum()的功能,然后使用apply()函数将这个自定义函数应用到groupby之后的分组中:

import random
import pandas as pd
import numpy as np

df = pd.DataFrame(
    {
        'name': ['bob' + str(i) for i in range(1, 11)],
        'company': [random.choice(['A', 'B', 'C']) for i in range(10)],
        'salary': np.random.randint(5, 50, 10),
        'age': np.random.randint(15, 50, 10)
    }

)


def sum_salary(group):  # 自定义函数,传入一个分组对象,计算每一个组的salary的和,(当然也可以使用lambda函数代替)
    return group['salary'].sum()


grouped = df.groupby('company', group_keys=True)
result1 = grouped.apply(sum_salary)
print(result1)
print('===================================')
grouped = df.groupby('company', group_keys=False)
result2 = grouped.apply(sum_salary)
print(result2)


运行结果如下:

company
A    52
B    89
C    32
dtype: int64
===================================
company
A    52
B    89
C    32
dtype: int64

可以发现group_keys=Truegroup_keys=False的运行结果仍然都是一样的。

接下来,我重新定义了自定义函数,使用自定义函数输出每一个分组的前两行:

import random
import pandas as pd
import numpy as np

df = pd.DataFrame(
    {
        'name': ['bob' + str(i) for i in range(1, 11)],
        'company': [random.choice(['A', 'B', 'C']) for i in range(10)],
        'salary': np.random.randint(5, 50, 10),
        'age': np.random.randint(15, 50, 10)
    }

)

grouped = df.groupby('company', group_keys=True)
result1 = grouped.apply(lambda x:x.iloc[:2]) # 匿名函数的作用是输出每一个分组的前两行
print(result1)
print('===================================')
grouped = df.groupby('company', group_keys=False) # 匿名函数的作用是输出每一个分组的前两行
result2 = grouped.apply(lambda x:x.iloc[:2])
print(result2)

得到的结果如下:

           name company
company                
A       1  bob2       A
        2  bob3       A
B       0  bob1       B
        5  bob6       B
C       3  bob4       C
        8  bob9       C
===================================
   name company
1  bob2       A
2  bob3       A
0  bob1       B
5  bob6       B
3  bob4       C
8  bob9       C

通过上面的结果可以发现group_keys=Truegroup_keys=False的运行结果终于不一样了,
group_keys=True时,分组键保留在结果中,并且作为结果的索引。group_keys=False时,分组键保留没有在结果中,并且不能作为结果的索引。
比如下面分别以分组键’A’来查询相应的分组的例子,group_keys=True时可以查询和,group_keys=False时不可以查询:

import random
import pandas as pd
import numpy as np

df = pd.DataFrame(
    {
        'name': ['bob' + str(i) for i in range(1, 11)],
        'company': [random.choice(['A', 'B', 'C']) for i in range(10)],
        'salary': np.random.randint(5, 50, 10),
        'age': np.random.randint(15, 50, 10)
    }

)

grouped = df.groupby('company', group_keys=True)
result1 = grouped.apply(lambda x:x.iloc[:2]) # 匿名函数的作用是输出每一个分组的前两行
print(result1.loc['A']) # 通过索引A取A分组
print('===================================')
grouped = df.groupby('company', group_keys=False) # 匿名函数的作用是输出每一个分组的前两行
result2 = grouped.apply(lambda x:x.iloc[:2])
print(result2.loc['A']) # 通过索引A取A分组


运行结果:

 name company  salary  age
6  bob7       A       6   27
===================================
KeyError: 'A' # 报错信息

通过上面的分析可以做出如下总结:
group_keys参数不可以和聚合函数配合使用,当调用聚合函数时,其本身的索引会失效,此时传递group_keys=False无效(与group_keys=True一样)。

通过前面对as_index参数和group_keys参数的说明可以发现它们两者具有相似之处,
当as_index和group_keys的值都是True时,它们两个的作用相同,产出的结果一摸一样.当as_index和group_keys的值都是False时,group_keys仅仅消除分组键而没有生成新的索引,as_index在消除分组键的同时又给每一个分组生成了一个新的索引。如下所示:
当as_index和group_keys的值都是False时,as_index的为结果生成了新的索引:

group_keys=False:
   name company  salary  age
1  bob2       A      12   19
2  bob3       A      47   40
8  bob9       B      19   48
0  bob1       C      14   26
3  bob4       C      28   17
=====================================
as_index=False:
     name company  salary  age
0 1  bob2       A      12   19
  2  bob3       A      47   40
1 8  bob9       B      19   48
2 0  bob1       C      14   26
  3  bob4       C      28   17

当as_index和group_keys的值都是True时,两者没有区别:

group_keys=True:
           name company  salary  age
company                             
A       0  bob1       A      32   26
        1  bob2       A       9   47
B       3  bob4       B      42   47
        4  bob5       B      45   43
C       2  bob3       C       8   40
        5  bob6       C      28   34
=====================================
as_index=True:
           name company  salary  age
company                             
A       0  bob1       A      32   26
        1  bob2       A       9   47
B       3  bob4       B      42   47
        4  bob5       B      45   43
C       2  bob3       C       8   40
        5  bob6       C      28   34

squeeze参数的作用

squeeze 参数是 groupby 函数的一个可选参数,用于控制返回结果的形状。

squeeze 参数设置为 True 时,表示只保留每个组中的一列,而将其他列作为索引。这样可以将结果矩阵从二维压缩为一维,使结果更加简洁易读。

squeeze 参数设置为 False 或未指定时,表示保留所有聚合后的列,返回一个二维矩阵。

官方文档说squeeze参数在未来的pandas版本中会被弃用,那就不在这个参数上浪费时间了。

observed参数的作用

当使用 Categorical 分组器(作为单个分组器,或作为多个分组器的一部分)时, observed=False 是返回所有可能的分组器值的笛卡尔积, observed=True 是仅返回那些被观察到的分组器。
需要注意的是,observed参数只在Categorical类型的分组键上有用,对于其他类型的键,将忽略observed参数的设置。

Categorical是Pandas中的分类类型数据,当数据只有少数几种可能取值但有大量重复字符串字段,利用categorical类型可有效节省内存,提高数据分析的效率。常见的分类如性别、职业等取值有限的类别数据。

下面给出ovservled的具体例子:

import pandas as pd
import numpy as np

df = pd.DataFrame(
    {
        'company': pd.Categorical(['A','A','B','B','A','B','B','A','C','C']), # 只有将company转换成Categorical类型observed才会起作用。
        'class':pd.Categorical(['foo',None,'bar',None,'bar','bar',None,'foo','foo',None]),
        'salary': np.random.randint(5,50,10),
        'age':np.random.randint(15,50,10)
    }

)
grouped = df.groupby(['company','class'],observed=False).count()
print(grouped)
grouped = df.groupby(['company','class'],observed=True).count()
print(grouped)

运行结果如下:

observed=False:
               salary  age
company class             
A       bar         1    1
        foo         2    2
B       bar         2    2
        foo         0    0
C       bar         0    0
        foo         1    1
observed=True:
               salary  age
company class             
A       foo         2    2
        bar         1    1
B       bar         2    2
C       foo         1    1

通过上面的结果可以发现对于不存在的分组B-fooC-bar,当observed=False时,在结果中并没有被忽略。当observed=True时,在结果中被忽略掉了。

dropna参数的作用

dropna参数用于控制在分组过程中是否删除含有缺失值(NAN)的分组.当dropna为True时,任何包含缺失值的分组都会被从结果中排除掉。当dropna为False时,包含缺失值的分组将被保留在结果中,默认为True。
现在给出下面的例子:

import pandas as pd
import numpy as np

df = pd.DataFrame(
    {
        'company': ['A','A','B','B','A','B','B','A','C','C'],
        'class':['foo',None,'bar',None,'bar','bar',None,'foo','foo',None],
        'salary': np.random.randint(5,50,10),
        'age':np.random.randint(15,50,10)
    }

)
grouped = df.groupby(['company','class'],dropna=False).count()
print(grouped)
grouped = df.groupby(['company','class'],dropna=True).count()
print(grouped)

运行结果如下:

dropna=False:
               salary  age
company class             
A       bar         1    1
        foo         2    2
        NaN         1    1
B       bar         2    2
        NaN         2    2
C       foo         1    1
        NaN         1    1
     
dropna=True:
               salary  age
company class             
A       bar         1    1
        foo         2    2
B       bar         2    2
C       foo         1    1

通过上面的运行结果可以发现,当dropna=False时,对于含有控制的分组键class,pandas也将其作为一个分组输出。当dropna=True时,对于含有控制的分组键class,pandas将其忽略掉了。

通过观察可以发现dropna和observed也有相似和不同之处。
相同的是当dropnaobserved都是True时,它们都将含有空值的键的分组忽略掉了。
不同的是observed仅仅对Categorical类型的分组键上有用,对于其他类型的键,将忽略observed参数的设置。dropna两者皆可。并且当dropnaobserved都是Falsse时,以上面生成的数据为例子,对于observed参数,pandas会计算[A,B,C]和[bar,foo]的笛卡尔积;对于dropna参数,pandas会计算[A,B,C]和[bar,foo,NAN]的笛卡尔积。
如下:

dropna=False:
               salary  age
company class             
A       bar         1    1
        foo         2    2
        NaN         1    1
B       bar         2    2
        NaN         2    2
C       foo         1    1
        NaN         1    1
observed=False:
               salary  age
company class             
A       bar         1    1
        foo         2    2
B       bar         2    2
        foo         0    0
C       bar         0    0
        foo         1    1  

你可能感兴趣的:(#,pandas,Python基础知识,pandas,python,人工智能)