Skip to content

python pandas数据分组

0 背景

在标准的数据探索过程中,我们通常按照特定的维度或标准来分割数据集。这种分割帮助我们理解数据在这些维度上的集中趋势。通过对数据按照这些粒度进行聚合分析,我们可以更好地洞察数据模式,从而帮助解决特定的问题。本文内容将涉及python pandas数据分组的基本原理和一些简单的操作技巧。

1 使用

import pandas as pd
data = {
    'a': ['A', 'B', 'B', 'A', 'E', ],
    'b': [1, 2, 3, 4, 5, ],
    'c': [1.34, 2.5, 3.14, 2.12, 1.41, ],
    'd': [2, 3, 1, None, 2, ],
}
df = pd.DataFrame(data)
df
   a  b     c    d
0  A  1  1.34  2.0
1  B  2  2.50  3.0
2  B  3  3.14  1.0
3  A  4  2.12  NaN
4  E  5  1.41  2.0

获取分组对象

为便于后面的分析, 将直接基于分组对象 group 分析。DataFrame对象进行分组获得DataFrameGroupBy分组对象。

group = df.groupby('a')

这是对 DataFrame对象进行分组,当然,也还可以对 Series对象进行分组,获取分组对象 SeriesGroupBy。比如:

df.c.groupby(df.a)

按标签分组

最简单的分组方法是指定DataFrame中的一列,按这列的去重数据 分组。也可以指定多列,按这几列的排列组合去重进行分组。示例如下:

# 按单列分组
grouped = df.groupby('a')
# 按行分组
grouped = df.groupby('b', axis='columns') 
# 按照多列分组
grouped = df.groupby(['a', 'b']) 

可以通过get_group()方法获取分组后分组对象某个分组的内容,返回一个 DataFrame 对象:

group.get_group('a')

按表达式分组

按照索引值是否是偶数分成两组(True、False):

df.groupby(lambda x:x % 2 == 0)
# or
df.groupby(df.index % 2 == 0)
         a  b     c    d
False   BA  6  4.62  3.0
True   ABE  9  5.89  5.0

按索引值是否大于2分成两组(True、False):

df.groupby(lambda x:x > 2)
# or
df.groupby(df.index > 2)
         a  b     c    d
False  ABB  6  6.98  6.0
True    AE  9  3.53  2.0

按照列名是否包含a字母分成两组(True、False):

df.T.groupby(lambda x: 'b' in x)

# or
# 该分组方式未来的版本中可能会删除,可以优先使用上面转置的方式对列进行分组
df.groupby(lambda x: 'b' in x, axis=1)

另外, 我们还可以利用一些筛选条件进行分组:

# 按照姓名的姓氏分组
df2.groupby(df.name.str[0]).sum()

# 按A及B部门、其他部门分组
df2.groupby(df2.department.isin(['A', 'B']))
# 按姓名的第一个字符和第二个字符分组
df2.groupby([df.name.str[0], df.name.str[1]])
# 按日期和小时分组
df2.groupby([df.time.date, df.time.hour])

按函数分组

按照自定义的函数分组.

按照b列的奇偶性分组, 实际上我们可以使用apply函数调用其他自定义的函数进行分组:

# 按照b的奇偶性分组, 当然, 实际上我们可以使用apply函数调用其他自定义的函数进行分组
df.groupby(df.b.apply(lambda x: x % 2)).sum()
# 多种方法混合分组
df.groupby(['a', df.b.apply(lambda x: x % 2)]).sum()

分组器

为便于接下来的操作, 我们创建一个时间序列数据

df['date'] = pd.date_range(start='2023-10-23', end='2024-3-21', freq='BM')
df
   a  b     c    d       date
0  A  1  1.34  2.0 2023-10-31
1  B  3  2.50  3.0 2023-11-30
2  B  3  3.14  1.0 2023-12-29
3  A  4  2.12  NaN 2024-01-31
4  E  5  1.41  2.0 2024-02-29

按照字段的分组器进行分组

# 按照字段进行分组
df.groupby(pd.Grouper('a')).count()

创建一个间隔时间为70天的分组器,按照该层次等级分组

# 创建一个间隔时间为70天的分组器,按照该层次等级分组
grouper = pd.Grouper(key='date', freq='70d')
df.groupby(grouper).sum()
              a  b     c    d
date                         
2023-10-31  ABB  7  6.98  6.0
2024-01-09   AE  9  3.53  2.0

按等级分组

按等级之前, 我们需要知道 pd.cut() 函数, 常用于将连续的数值数据分割成离散的区间。这种方法通常用于数据分组或分箱(binning)操作,将数据划分成不同的类别以进行分析。

# 4等分
pd.cut(df.b, bins=4)
# 自定义分组, 将b列分成[0, 2, 4, 5] 3个区间
pd.cut(df.b, bins=[0, 2, 4, 5])
# 自定义分组, 将b列分成[0, 2, 4, 5] 3个区间, 并给每个区间取个别名
pd.cut(df.b, bins=[0, 2, 4, 5], labels=['低', '中', '高'])
0    
1    
2    
3    
4    
Name: b, dtype: category
Categories (3, object): ['低' < '中' < '高']

按照每年一个周期分组, 创建一个分组器, 并进行分组.

df.groupby(pd.cut(df.date, pd.date_range('2020', '2025', freq='1YS'), right=False)).count()
                          a  b  c  d  date
date                                      
[2021-01-01, 2022-01-01)  0  0  0  0     0
[2022-01-01, 2023-01-01)  0  0  0  0     0
[2023-01-01, 2024-01-01)  3  3  3  3     3
[2024-01-01, 2025-01-01)  2  2  2  1     2

索引

对数据表进行groupby()分组操作后, 分组的字段会默认成为索引,如果不想让它成为索引,可以使用 as_index=False 进行设置:

df.groupby('a', as_index=False)

排序

对数据表进行groupby()分组操作后, 分组字段会默认成为索引,数据会对索引进行排序,如果不想排序,可以使用sort=False进行设置。不排序的情况下会按索引出现的顺序排列:

df.groupby('a', sort=False)

2 关于

groupby 函数可以简单的理解为为拆开数据、应用数据和合并数据。本文主要介绍python Pandas提供的多种多样的分组方法,能够让我们灵活方便地对数据进行分组,达到更好的数据分析的效果。

欢迎关注我的微信公众号