学科分类
目录
数据分析

通过groupby()方法将数据拆分成组

分组聚合的第一个步骤是将数据拆分成组。在Pandas中,可以通过groupby()方法将数据集按照某些标准划分成若干个组,该方法的语法格式如下:

groupby(by=None, axis=0, level=None, as_index=True, sort=True,group_keys=True,
        squeeze=False, observed=False, **kwargs)

部分参数表示的含义如下:

(1) by:用于确定进行分组的依据。

(2) axis:表示分组轴的方向,可以为0(表示按行)或1(表示按列),默认为0。

(3) level:如果某个轴是一个MultiIndex 对象(索引层次结构),则会按特定级别或多个级别分组。

(4) as_index:表示聚合后的数据是否以组标签作为索引的DataFrame对象输出,接收布尔值,默认为True。

(5) sort:表示是否对分组标签进行排序,接收布尔值,默认为True。

通过groupby()方法执行分组操作,会返回一个GroupBy对象,该对象实际上并没有进行任何计算,只是包含一些关于分组键(比如df_obj['key1'])的中间数据而已。一般,使用Series调用groupby()方法返回的是SeriesGroupBy对象,而使用DataFrame调用groupby()方法返回的是DataFrameBy对象。

在进行分组时,可以通过groupby()方法的by参数来指定按什么标准分组,by参数可以接收的数据有多种形式,类型也不必相同,常用的分组方式主要有以下4种:

  • 列表或数组,其长度必须与待分组的轴一样。

  • DataFrame对象中某列的名称。

  • 字典或Series对象,给出待分组轴上的值与分组名称之间的对应关系。

  • 函数,用于处理轴索引或索引中的各个标签。

为了能够让读者更好地理解,接下来,分别对上述几种分组方式进行详细地讲解,具体内容如下。

1. 通过列名进行分组

在Pandas对象中,如果它的某一列数据满足不同的划分标准,则可以将该列当做分组键来拆分数据集。例如,创建一个DataFrame对象,具体代码如下。

In [1]: import pandas as pd
        df = pd.DataFrame({"Key":['C','B','C','A','B','B','A','C','A'],
                           "Data":[2,4,6,8,10,1,14,16,18]})
        df
Out[1]:
 Key Data
0  C   2
1  B   4
2  C   6
3  A   8
4  B  10
5  B   1
6  A  14
7  C  16
8  A  18

然后,调用groupby()方法时把列名Key传给by参数,代表将Key作为分组键,让df对象按照Key列进行分组,具体示例代码如下:

In [2]: # 按Key列进行分组
        df.groupby(by='Key')
Out[2]: <pandas.core.groupby.groupby.DataFrameGroupBy object at 0x0000000006E274A8>

从输出的结果可以看出,DataFrame经过分组后得到了一个DataFrameGroupBy对象,该对象是一个可迭代的对象,即只有在真正需要的时候才会执行计算(采用惰性计算)。

如果要查看每个分组的具体内容,则可以使用for循环遍历DataFrameGroupBy对象。例如,在上述示例代码的基础上,使用一个变量接收返回的DataFrameGroupBy对象,之后用for循环进行遍历,示例代码如下。

In [3]: group_obj = df.groupby('Key')
        # 遍历分组对象
        for i in group_obj:
            print(i)
        # 输出结果

('A',  Key Data
3  A   8
6  A  14
8  A  18)
('B',  Key Data
1  B   4
4  B  10
5  B   1)
('C',  Key Data
0  C   2
2  C   6
7  C  16) 

上述示例中,调用groupby()方法让df对象按分组键Key对应的一列数据进行分组,这表明(其划分标准)一旦发现该列中存在相同的数据,就把其所在的一行从整个df对象中拆分出来,与其他具有相同键的数据组合在一起,例如,所有“A”为一组,所有“B”为一组,所有“C”为一组,共分成3组数据,这些分组信息都保存在group_obj对象中,使用for循环遍历打印每个分组的信息。

上述示例输出了三个表示分组的元组,每个元组的第一个元素为该组的名称(Key列的数据),第二个元素为该组的具体数据。

2、通过Series对象进行分组

除此之外,还可以自定义Series类对象,将其作为分组键进行分组。例如,创建一个5行4列的DataFrame对象,示例代码如下:

In [4]: import pandas as pd
        import numpy as np
        df = pd.DataFrame({'key1': ['A', 'A', 'B', 'B', 'A'],
                           'key2': ['one', 'two', 'one', 'two', 'one'],
                           'data1': [2, 3, 4, 6, 8],
                           'data2': [3, 5, 6, 3, 7]})
        df
Out[4]:
 key1 key2 data1 data2
0  A one    2    3
1  A two    3    5
2  B one    4    6
3  B two    6    3
4  A one    8    7

然后创建一个用做分组键的Series对象,示例代码如下。

In [5]: se = pd.Series(['a', 'b', 'c', 'a', 'b'])
        se
Out[5]:
0  a
1  b
2  c
3  a
4  b
dtype: object

接下来,在调用groupby()方法时把se对象传入给by参数,将se对象作为分组键拆分df对象,以得到一个分组对象,遍历该分组对象并查看每个分组的具体内容,示例代码如下。

In [6]: group_obj = df.groupby(by = se)  # 按自定义Series对象进行分组
        for i in group_obj:              # 遍历分组对象
            print(i)
Out[6]:
('a',  key1 key2 data1 data2
0  A one   2   3
3  B two   6   3)
('b',  key1 key2 data1 data2
1  A  two   3    5
4  A  one   8    7)
('c',  key1 key2 data1 data2
2  B  one   4    6)

从输出结果中可以看出,se将df对象分为a、b、c三组,其中索引0和3对应的两行为a组数据,索引1和4对应的两行为b组数据,索引2对应的一行为c组数据。

如果Series对象的长度与原数据的行索引长度不相等时,那么在进行分组时会怎么样呢?接下来,我们使用代码进行测试,具体如下。

In [7]: # 当Series长度与原数据的索引值长度不同时
        se = pd.Series(['a', 'a', 'b'])
        group_obj = df.groupby(se)
        for i in group_obj:      # 遍历分组对象
            print(i)
Out[7]:
('a',  key1 key2 data1 data2
0  A  one   2    3
1  A  two   3    5)
('b',  key1 key2 data1 data2
2  B  one   4    6)

上述示例中,首先创建了一个Series对象,它里面共包含三行数据,其中前两行的数据是“a”, 最后一行数据为“b”,然后调用groupby()方法按se对象将df对象拆分成组,由于se只有三行数据,所以它只需要对df对象的前三行数据进行分组,即df对象的前两行分为一组,最后一行分为一组。

值得一提的是,如果Series对象的索引长度与Pandas对象的索引长度不相同时,则只会将部分(具有相同索引长度)数据进行分组,而不会将全部的数据进行分组。

3、通过字典进行分组

当用字典对DataFrame进行分组时,则需要确定轴的方向及字典中的映射关系,即字典中的键为列名,字典的值为自定义的分组名。例如,创建一个5行5列的DataFrame对象,具体代码如下:

In [8]: from pandas import DataFrame, Series
        num_df = DataFrame({'a': [1, 2, 3, 4, 5],
                            'b': [6, 7, 8, 9, 10],
                            'c': [11, 12, 13, 14, 15],
                            'd': [5, 4, 3, 2, 1],
                            'e': [10, 9, 8, 7, 6]})
        num_df

Out[8]:
  a  b  c d  e
0 1  6 11 5 10
1 2  7 12 4  9
2 3  8 13 3  8
3 4  9 14 2  7
4 5 10 15 1  6

然后,创建一个表示分组规则的字典,其中字典的键为num_df对象的列名称,值为自定义的分组名称,具体代码如下。

In [9]: # 定义分组规则
        mapping = {'a':'第一组','b':'第二组','c':'第一组','d':'第三组','e':'第二组'}
        mapping
Out[9]:
{'a':'第一组', 
'b': '第二组', 
'c': '第一组', 
'd': '第三组', 
'e': '第二组'}

接着调用groupby()方法,在该方法中传入刚创建的字典mapping,将mapping作为分组键拆分num_df对象,具体代码如下。

In [10]: # 按字典分组
         by_column = num_df.groupby(mapping, axis=1)
         for i in by_column:
                print(i)
Out[10]:
('第一组',  a  c
0 1 11
1 2 12
2 3 13
3 4 14
4 5 15)
('第三组',  d
0 5
1 4
2 3
3 2
4 1)
('第二组',   b  e
0  6 10
1  7  9
2  8  8
3  9  7
4 10  6)

上述示例在拆分num_df时,按照横轴的方向进行分组,将a列、c列数据映射到第一组;将b列、e列数据映射到第二组;将d列数据映射到第三组。从输出结果中可以看出,num_df共分为“第一组”、“第二组”、“第三组”三组。

4、通过函数进行分组

与字典或Series对象相比,使用函数作为分组键会更加灵活,任何一个被当做分组键的函数都会在各个索引值上被调用一次,返回的值会被用作分组名称。

创建一个DataFrame对象,将其行索引的名称设为字符串类型的,具体代码如下。

In [11]: import pandas as pd
         df = pd.DataFrame({'a': [1, 2, 3, 4, 5],
                            'b': [6, 7, 8, 9, 10],
                            'c': [5, 4, 3, 2, 1]},
                           index=['Sun', 'Jack', 'Alice', 'Helen', 'Job'])
        df

Out[11]:
​    a  b c
Sun  1  6 5
Jack  2  7 4
Alice 3  8 3
Helen 4  9 2
Job  5 10 1

如果以行索引名称的长度进行分组,则长度相同的行索引名称会分为一组,即索引名称长度为3的分为一组,长度为4的分为一组,长度为5的分为一组,共分成三组。接下来,以行索引名称的长度作为分组键,将DataFrame对象的数据拆拆分成三组数据,具体代码如下。

In [12]: groupby_obj = df.groupby(len)  # 使用内置函数len进行分组
         for group in groupby_obj:    # 遍历分组对象
            print(group)
Out[12]:
(3,   a  b c
Sun 1  6 5
Job 5 10 1)
(4,    a b c
Jack 2 7 4)
(5,    a b c
Alice 3 8 3
Helen 4 9 2)

上述示例中,在调用groupby()方法时传入了内置函数len,表明len函数会对行索引一列执行求长度的操作,调用len函数返回的长度值作为分组名称,一旦发现索引名称的长度值一样,就归类为一组。

从输出结果中可以看出,索引名称长度为3的“Sun”和“Job”归为第一组,长度为4的是“Jack”单独为第二组,长度为5的是“Alice”和“Helen”归为第三组。

点击此处
隐藏目录