分组运算,一般都是根据某个(些)条件将数据进行分组,然后对每个分组数据进行操作,然后合并这些分组或每个分组单独输出。分组可以在行的方向上,也可以在列的方向上。注意:空值会被过滤。
测试数据如下
df
Out[1]:
商品 地区 销量
0 李老吉 北京 10
1 娃啥啥 上海 13
2 康帅傅 广州 34
3 嗨非丝 深圳 25
4 娃啥啥 北京 19
5 康帅傅 上海 31
6 李老吉 重庆 24
groupby()返回的是一个生成器,可以使用for访问每一分组数据。
for k ,v in df.groupby('商品'):
print(k, v)
嗨非丝 商品 地区 销量
3 嗨非丝 深圳 25
娃啥啥 商品 地区 销量
1 娃啥啥 上海 13
4 娃啥啥 北京 19
康帅傅 商品 地区 销量
2 康帅傅 广州 34
5 康帅傅 上海 31
李老吉 商品 地区 销量
0 李老吉 北京 10
6 李老吉 重庆 24
for k, v in df.groupby(df['商品']):
print(k, v)
嗨非丝 商品 地区 销量
3 嗨非丝 深圳 25
娃啥啥 商品 地区 销量
1 娃啥啥 上海 13
4 娃啥啥 北京 19
康帅傅 商品 地区 销量
2 康帅傅 广州 34
5 康帅傅 上海 31
李老吉 商品 地区 销量
0 李老吉 北京 10
6 李老吉 重庆 24
只需传入一个列表,列表中可直接使用列名也可以使用多个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
比如,我们只想对商品进行分组,然后统计。不关心地区字段(实际可能有很多其他无关字段)。
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: '商品'
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
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
在实现这个功能之前,我们需要更彻底地理解传入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
实际上,上面分组操作为了方便输出,已经使用了一些分组后操作。如分组求和。常用的一些分组后数值操作如下表
操作 | 说明 |
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