Pandas GroupBy数据分组处理

分组运算,一般都是根据某个(些)条件将数据进行分组,然后对每个分组数据进行操作,然后合并这些分组或每个分组单独输出。分组可以在行的方向上,也可以在列的方向上。注意:空值会被过滤。

测试数据如下

df

Out[1]: 
    商品  地区  销量
0  李老吉  北京  10
1  娃啥啥  上海  13
2  康帅傅  广州  34
3  嗨非丝  深圳  25
4  娃啥啥  北京  19
5  康帅傅  上海  31
6  李老吉  重庆  24

一、实现分组的方式

groupby()返回的是一个生成器,可以使用for访问每一分组数据。

1、直接传入分组的键

for k ,v in df.groupby('商品'):
    print(k, v)

嗨非丝     商品  地区  销量
3  嗨非丝  深圳  25
娃啥啥     商品  地区  销量
1  娃啥啥  上海  13
4  娃啥啥  北京  19
康帅傅     商品  地区  销量
2  康帅傅  广州  34
5  康帅傅  上海  31
李老吉     商品  地区  销量
0  李老吉  北京  10
6  李老吉  重庆  24

 2.使用Series对象

for k, v in df.groupby(df['商品']):
    print(k, v)

嗨非丝     商品  地区  销量
3  嗨非丝  深圳  25
娃啥啥     商品  地区  销量
1  娃啥啥  上海  13
4  娃啥啥  北京  19
康帅傅     商品  地区  销量
2  康帅傅  广州  34
5  康帅傅  上海  31
李老吉     商品  地区  销量
0  李老吉  北京  10
6  李老吉  重庆  24

 3.多个分组键

只需传入一个列表,列表中可直接使用列名也可以使用多个Series。同时,也可以看到,k的值是元组,包含两个分组键的值。

# 也可以传入[df['商品'], df['地区']]
for k, v in df.groupby(['商品', '地区']):
    print(k, v)

('嗨非丝', '深圳')     商品  地区  销量
3  嗨非丝  深圳  25
('娃啥啥', '上海')     商品  地区  销量
1  娃啥啥  上海  13
('娃啥啥', '北京')     商品  地区  销量
4  娃啥啥  北京  19
('康帅傅', '上海')     商品  地区  销量
5  康帅傅  上海  31
('康帅傅', '广州')     商品  地区  销量
2  康帅傅  广州  34
('李老吉', '北京')     商品  地区  销量
0  李老吉  北京  10
('李老吉', '重庆')     商品  地区  销量
6  李老吉  重庆  24

 4.对部分列进行分组

比如,我们只想对商品进行分组,然后统计。不关心地区字段(实际可能有很多其他无关字段)。

df.groupby('商品')['销量'].sum()
Out[8]: 
商品
嗨非丝    25
娃啥啥    32
康帅傅    65
李老吉    34

上面代码也可以这样写

df['销量'].groupby(df['商品']).sum()
Out[9]: 
商品
嗨非丝    25
娃啥啥    32
康帅傅    65
李老吉    34

 注意,这种写法,groupby()就不能直接传入列名。因为df['销量']已经是一个Series,此时再直接传入其他列名会报KeyError。

df['销量'].groupby('商品').sum()

# KeyError: '商品'

 5.通过字典进行分组

df = pd.DataFrame(np.random.randn(3, 4), columns=['A', 'B', 'C', 'D'])
dct = {'A': 'one', 'B': 'two', 'C': 'one', 'D': 'two'}
df

Out[1]: 
          A         B         C         D
0 -0.364443 -0.625101  1.275733 -1.185416
1  0.350185 -0.091564 -0.314260 -0.529303
2 -0.770483 -0.187238  0.830992  1.804910


 字典的作用实际上就是建立了这样一种映射关系,即保持同属一组的列(或行)对应的字典值相同。

# 默认axis=0
df.groupby(dct, axis=1).sum()
Out[2]: 
        one       two
0  0.911290 -1.810518
1  0.035926 -0.620867
2  0.060509  1.617673

  6.层次化索引的分组

columns = pd.MultiIndex.from_arrays([['US', 'US', 'CHN', 'CHN', 'CHN'],
                                     ['A', 'B', 'A', 'B', 'C']], names=['country', 'project'])
df = pd.DataFrame(np.random.randn(3, 5), columns=columns)
print(df)
print(df.groupby(level='country', axis=1).sum())


# 输出:
country        US                 CHN                    
project         A         B         A         B         C
0        0.334900 -1.989492 -0.877559 -0.779063 -0.813791
1       -0.233066 -0.301358  0.924764 -0.217365  0.095670
2        0.420070 -0.658127 -0.680005 -1.604601 -0.847595
country       CHN        US
0       -2.470413 -1.654591
1        0.803069 -0.534424
2       -3.132200 -0.238057

 7.通过函数进行分组

在实现这个功能之前,我们需要更彻底地理解传入groupby()的参数最终都是什么。虽然我们前面展现了很多种具体的书写方式,其实质都是传入一个数组,类似['A', 'B', 'A', 'C', 'B']。默认作用在index上,即第0和第2行划为一组,第1和第4行为一组。作用在列上也类似。因此,函数的作用就是根据条件生成这样的数组作为分组依据。

df = pd.DataFrame({'商品': ['李老吉', '娃啥啥', '康帅傅', '嗨非丝', '娃啥啥', '康帅傅', '李老吉'],
                   '地区': ['北京', '上海', '广州', '深圳', '北京', '上海', '重庆'],
                   '销量': [10, 13, 34, 25, 19, 31, 24]})


def fun(k):
    if df['地区'][k] in ['北京', '上海']:
        return '国际一线'
    else:
        return '国内一线'


print(df.groupby(fun).sum())


# 输出:       销量
#      国内一线  83
#      国际一线  73

二、分组后的操作

实际上,上面分组操作为了方便输出,已经使用了一些分组后操作。如分组求和。常用的一些分组后数值操作如下表

groupby分组后操作
操作 说明
sum 求和
count 计数
mean 均值
median 中位数
std,var 标准差,方差
min,max 最小值,最大值
prod 乘积
first,last 第一个,最后一个

这些方法的使用和前面的sum一致,不再赘述。下面介绍如何对分组后数据进行自定义操作。

df = pd.DataFrame({'商品': ['李老吉', '娃啥啥', '康帅傅', '嗨非丝', '娃啥啥', '康帅傅', '李老吉'],
                   '地区': ['北京', '上海', '广州', '深圳', '北京', '上海', '重庆'],
                   '销量': [10, 13, 34, 25, 19, 31, 24]})


def fun(sdf):
    if sum(sdf.iloc[:, -1]) > 40:
        print('{}大卖'.format(sdf.iloc[0, 0]))
    else:
        print('{}还需努力'.format(sdf.iloc[0, 0]))
    return 1


df.groupby(df['商品']).apply(fun)


# 输出:
嗨非丝还需努力
娃啥啥还需努力
康帅傅大卖
李老吉还需努力
商品
嗨非丝    1
娃啥啥    1
康帅傅    1
李老吉    1

基于这个基础,你可以完全自由定义处理函数,实现各种功能。当然,简单的fun完全可以用lambda实现。

补充:如果你自定义的函数除了传入分组后的df,还须接收其他参数。可以通过关键字方式传递。此处filter和apply用法是相同的。

def fun(df, s):
    print(df)
    print(s)


def main():
    df = DataFrame([['a', 1], ['b', 2], ['a', 3]], columns=['name', 'age'])
    s = '我是一个被需要的参数'
    df.groupby(['name']).filter(fun, s=s)


main()


# 输出:
  name  age
0    a    1
2    a    3
我是一个被需要的参数
  name  age
1    b    2
我是一个被需要的参数

再次补充:

有时候只想获取每个分组的统计数量、对具体内容不感兴趣,可以使用下面方法

df
Out[1]: 
  商品类别  商品名称  单价  销量
0   硬件    电脑   1   8
1   硬件   显示器   2   7
2   软件  操作系统   3   6
3   软件  操作系统   4   5
4   软件  办公软件   5   4
5   硬件    电脑   6   3
6   软件  操作系统   7   2
7   软件  办公软件   8   1

df.groupby(['商品类别', '商品名称']).size()
Out[2]: 
商品类别  商品名称
硬件    显示器     1
      电脑      2
软件    办公软件    2
      操作系统    3

size()函数返回的是一个含有多级index的Series,最外层“商品类别”实际上相当于进行了同类合并单元格的意思。如果想拆分,可以使用unstack()函数。我的感受是,合并是为了方便人查看,拆分是为了方便数据处理。

type(df.groupby(['商品类别', '商品名称']).size())
Out[3]: pandas.core.series.Series

df.groupby(['商品类别', '商品名称']).size().unstack()
Out[4]: 
商品名称  办公软件  操作系统  显示器   电脑
商品类别                      
硬件     NaN   NaN  1.0  2.0
软件     2.0   3.0  NaN  NaN

你可能感兴趣的:(Pandas,数据分析,python,数据分析)