13.高级-数据聚合 和 分组计算

import numpy as np
import pandas as pd
from pandas import Series,DataFrame
from numpy import nan as NA
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
df1=DataFrame(
    {
        'key1':list('aabba'),
        'key2':'one,two,one,two,one'.split(','),
        'data1':np.random.randint(0,10,5),
        'data2':np.random.randint(0,10,5)
    }
)
df1
key1	key2	data1	data2
0	a	one	7	2
1	a	two	4	5
2	b	one	3	7
3	b	two	9	8
4	a	one	1	2
s1=df1['data1']

Series.groupby

按照某个series 进行分组,返回一个分组后的对象

s1.groupby(df1['key1'])
<pandas.core.groupby.generic.SeriesGroupBy object at 0x0000017FF3763F28>
# 可以用list()转换,查看结构,可以知道分组对象的每个值都是分组依据和分组结果
list(s1.groupby(df1.key1))
[('a',
  0    7
  1    4
  4    1
  Name: data1, dtype: int32),
 ('b',
  2    3
  3    9
  Name: data1, dtype: int32)]
我们的分组对象是可以直接使用聚合(统计)函数
s1.groupby(df1.key1).sum()
key1
a    12
b    12
Name: data1, dtype: int32
s1.groupby(df1.key1).mean()
key1
a    4
b    6
Name: data1, dtype: int32
s1.groupby(df1.key1).count()
key1
a    3
b    2
Name: data1, dtype: int64

DataFrame.groupby

df1
key1	key2	data1	data2
0	a	one	7	2
1	a	two	4	5
2	b	one	3	7
3	b	two	9	8
4	a	one	1	2
# 用匹配的值
df1.groupby(df1.key2)
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000017FF379FEB8>
list(df1.groupby(df1.key2))
[('one',
    key1 key2  data1  data2
  0    a  one      7      2
  2    b  one      3      7
  4    a  one      1      2),
 ('two',
    key1 key2  data1  data2
  1    a  two      4      5
  3    b  two      9      8)]
# 如果分组依据来自本身的某列,直接用列名称
df1.groupby('key2')
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000017FF37B5668>
# 可以用其他独立的Series进行分组
df1.groupby(Series(['c','c','d','d','d']))
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000017FF37B5EF0>
list(df1.groupby(Series(['c','c','d','d','d'])))
[('c',
    key1 key2  data1  data2
  0    a  one      7      2
  1    a  two      4      5),
 ('d',
    key1 key2  data1  data2
  2    b  one      3      7
  3    b  two      9      8
  4    a  one      1      2)]
# 可以直接使用聚合函数
df1.groupby(Series(['c','c','d','d','d'])).sum()
data1	data2
c	11	7
d	13	17
df1.groupby(Series(['c','c','d','d','d'])).max()
key1	key2	data1	data2
c	a	two	7	5
d	b	two	9	8
使用多个依据进行分组

DataFrame.groupby([Series1,Series2,…])
如果Series(mapper)来源本身,可以直接使用列名称
DataFrame.groupby([columnName1,columnName2,…])

df1.groupby([df1.key1,df1.key2])
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000017FF37C3A58>
list(df1.groupby([df1.key1,df1.key2]))
[(('a', 'one'),
    key1 key2  data1  data2
  0    a  one      7      2
  4    a  one      1      2),
 (('a', 'two'),
    key1 key2  data1  data2
  1    a  two      4      5),
 (('b', 'one'),
    key1 key2  data1  data2
  2    b  one      3      7),
 (('b', 'two'),
    key1 key2  data1  data2
  3    b  two      9      8)]
list(df1.groupby(['key1','key2']))
[(('a', 'one'),
    key1 key2  data1  data2
  0    a  one      7      2
  4    a  one      1      2),
 (('a', 'two'),
    key1 key2  data1  data2
  1    a  two      4      5),
 (('b', 'one'),
    key1 key2  data1  data2
  2    b  one      3      7),
 (('b', 'two'),
    key1 key2  data1  data2
  3    b  two      9      8)]
df1.groupby(['key1','key2']).sum()
data1	data2
key1	key2		
a	one	8	4
two	4	5
b	one	3	7
two	9	8
# 假设有很多列,只需要一两列进行分组与计算
df2=DataFrame(
    np.random.randint(0,10,(10,10)),
    columns=list('ABCDEFGHIJ')
)
df2.insert(0,'key2','one,two,one,two,one'.split(',')*2)
df2.insert(0,'key1',list('aabbc')*2)
df2
key1	key2	A	B	C	D	E	F	G	H	I	J
0	a	one	4	4	7	9	6	5	0	8	2	5
1	a	two	8	5	1	9	6	1	5	5	4	5
2	b	one	7	7	5	9	9	9	7	5	8	6
3	b	two	9	9	2	0	9	0	9	2	3	6
4	c	one	2	9	8	3	1	6	1	5	6	5
5	a	one	8	6	4	7	5	7	7	8	8	4
6	a	two	4	3	8	7	3	4	8	2	2	6
7	b	one	3	0	0	0	9	7	8	3	0	3
8	b	two	9	7	4	3	5	2	1	7	1	4
9	c	one	6	8	1	6	1	6	9	6	1	5
如果需要分组计算的列不是很多

1.整体分组计算,然后取需要的列得到结果
2.先取需要的列,在进行分组计算

df2.groupby(['key1','key2']).sum()
A	B	C	D	E	F	G	H	I	J
key1	key2										
a	one	12	10	11	16	11	12	7	16	10	9
two	12	8	9	16	9	5	13	7	6	11
b	one	10	7	5	9	18	16	15	8	8	9
two	18	16	6	3	14	2	10	9	4	10
c	one	8	17	9	9	2	12	10	11	7	10
# 方法1
df2.groupby(['key1','key2']).sum()[['A','B']]
A	B
key1	key2		
a	one	12	10
two	12	8
b	one	10	7
two	18	16
c	one	8	17
# 方法2
df2[['A','B']].groupby([df2.key1,df2.key2]).sum()
A	B
key1	key2		
a	one	12	10
two	12	8
b	one	10	7
two	18	16
c	one	8	17
# 如果取得时候包含相关列,就可以直接使用列名称
df2[['key1','key2','A','B']].groupby(['key1','key2']).sum()
A	B
key1	key2		
a	one	12	10
two	12	8
b	one	10	7
two	18	16
c	one	8	17
gg=list(df2[['key1','key2','A','B']].groupby(['key1','key2']))
for each in gg:
    if each[0]==('b','two'):
        num1=gg.index(each)
num1
3
gg[num1]
(('b', 'two'),
   key1 key2  A  B
 3    b  two  9  9
 8    b  two  9  7)
我们分组对象,还可以转为列表后转为字典
dict(gg)
{('a',
  'one'):   key1 key2  A  B
 0    a  one  4  4
 5    a  one  8  6,
 ('a',
  'two'):   key1 key2  A  B
 1    a  two  8  5
 6    a  two  4  3,
 ('b',
  'one'):   key1 key2  A  B
 2    b  one  7  7
 7    b  one  3  0,
 ('b',
  'two'):   key1 key2  A  B
 3    b  two  9  9
 8    b  two  9  7,
 ('c',
  'one'):   key1 key2  A  B
 4    c  one  2  9
 9    c  one  6  8}
dict(gg).get(('b','two'))
key1	key2	A	B
3	b	two	9	9
8	b	two	9	7
# 查看df1的列类型
df1.dtypes
key1     object
key2     object
data1     int32
data2     int32
dtype: object
# 使用列类型进行分组
df1.groupby(df1.dtypes,axis=1)
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000017FF380B208>
list(df1.groupby(df1.dtypes,axis=1))
[(dtype('int32'),
     data1  data2
  0      7      2
  1      4      5
  2      3      7
  3      9      8
  4      1      2),
 (dtype('O'),
    key1 key2
  0    a  one
  1    a  two
  2    b  one
  3    b  two
  4    a  one)]
xxx.astype(yy) xxx对象的值转为yy类型
df1.data2.astype(float)
0    2.0
1    5.0
2    7.0
3    8.0
4    2.0
Name: data2, dtype: float64
df1['data3']=df1.data2.astype(float)
df1
key1	key2	data1	data2	data3
0	a	one	7	2	2.0
1	a	two	4	5	5.0
2	b	one	3	7	7.0
3	b	two	9	8	8.0
4	a	one	1	2	2.0
list(df1.groupby(df1.dtypes,axis=1))
[(dtype('int32'),
     data1  data2
  0      7      2
  1      4      5
  2      3      7
  3      9      8
  4      1      2),
 (dtype('float64'),
     data3
  0    2.0
  1    5.0
  2    7.0
  3    8.0
  4    2.0),
 (dtype('O'),
    key1 key2
  0    a  one
  1    a  two
  2    b  one
  3    b  two
  4    a  one)]
df1.insert(len(df1.columns),'data4',list('aabbc'))
df1
key1	key2	data1	data2	data3	data4
0	a	one	7	2	2.0	a
1	a	two	4	5	5.0	a
2	b	one	3	7	7.0	b
3	b	two	9	8	8.0	b
4	a	one	1	2	2.0	c
可以按照一列或多列进行分组
# 如 key1 就是一列 的值 其实是按行分
# 如 key1,key2 就是两列 其实是按行分
# 按列分组, 如:按照类型分 dtype,axis=1 
# 读取 成绩.csv 前十个人的信息,姓名作为行索引,学科作为列索引
data=pd.read_csv('成绩.csv',index_col='Unnamed: 0')
# 预览前5条 xxx.head()
data.head(8)
语文	数学	英语	化学	物理	生物	政治	历史	地理
赵大	97	90	68	86	73	94	70	70	72
孙兄	91	99	86	89	68	96	70	82	60
钱术	95	87	87	67	99	88	85	65	95
吴羊	92	98	94	64	96	82	65	85	69
王女	91	84	84	88	92	75	82	97	91
王卜	94	89	90	99	95	99	67	82	82
王可	94	90	76	68	93	82	93	99	83
钱巾	97	85	92	81	71	85	90	60	65
我们可以使用字典分组

如:用字典给每列取一个别名,如果语数外为主科,其他为副科
创建2个字典再组合成二维列表,再转为字典

方法1

dict1=dict.fromkeys(data.columns[:3],'主科')
dict2=dict.fromkeys(data.columns[3:],'副科')
list_1=[]
for each in dict1:
    list_1.append([each,dict1[each]])
for each in dict2:
    list_1.append([each,dict2[each]])
list_1
[['语文', '主科'],
 ['数学', '主科'],
 ['英语', '主科'],
 ['化学', '副科'],
 ['物理', '副科'],
 ['生物', '副科'],
 ['政治', '副科'],
 ['历史', '副科'],
 ['地理', '副科']]
dict(list_1)
{'语文': '主科',
 '数学': '主科',
 '英语': '主科',
 '化学': '副科',
 '物理': '副科',
 '生物': '副科',
 '政治': '副科',
 '历史': '副科',
 '地理': '副科'}

方法2:直接循环

list_2=[]
for index,value in enumerate(data.columns):
    if index<3:
        list_2.append([value,'主科'])
    else:
        list_2.append([value,'副科'])
list_2
[['语文', '主科'],
 ['数学', '主科'],
 ['英语', '主科'],
 ['化学', '副科'],
 ['物理', '副科'],
 ['生物', '副科'],
 ['政治', '副科'],
 ['历史', '副科'],
 ['地理', '副科']]
a=['a','b','c']
for i in range(len(a)):
    print(i,a[i])
0 a
1 b
2 c
for index,value in enumerate(a):
    print(index,value)
0 a
1 b
2 c

方法3

dict1
{'语文': '主科', '数学': '主科', '英语': '主科'}
dict2
{'化学': '副科', '物理': '副科', '生物': '副科', '政治': '副科', '历史': '副科', '地理': '副科'}
# 将两个字典变成字符串,第一个字典不要后面的},第二个字典不要前面的{,
# 中间用字符串‘,’连接,最后将整体用eval转成整数变成了一个字典
mapping=eval(str(dict1)[:-1]+','+str(dict2)[1:])
# 分组使用主副科分组
data.groupby(mapping,axis=1)
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000017FFC0B72E8>
data.groupby(mapping,axis=1).sum()
主科	副科
赵大	255	465
孙兄	276	465
钱术	269	499
吴羊	284	461
王女	259	525
王卜	273	524
王可	260	518
钱巾	274	452
李丁	270	481
吴丫	243	478
吴丑	266	511
钱火	266	465
王石	248	487
李人	246	511
郑正	256	435
王乃	249	475
李寸	247	478
郑之	289	437
钱口	274	417
孙寸	247	508
吴儿	254	507
李木	245	512
赵主	258	455
钱巨	287	516
郑电	257	516
赵工	245	524
李方	259	473
李门	243	463
李几	248	469
赵夫	256	504
吴井	267	474
郑天	273	521
孙计	254	439
郑尺	276	515
孙友	268	493
郑中	265	439
赵页	258	455
郑户	247	493
王了	242	511
赵爪	272	503
吴又	269	427
钱化	248	522
吴不	252	518
吴伏	241	470
王贝	260	472
李气	268	413
李田	265	452
李下	244	442
李匕	268	521
李心	283	457
# 每个分类有几个
data.groupby(mapping,axis=1).size()
主科    3
副科    6
dtype: int64
#使用Series 作为分组对象
map_series=Series(mapping)
map_series
语文    主科
数学    主科
英语    主科
化学    副科
物理    副科
生物    副科
政治    副科
历史    副科
地理    副科
dtype: object
data.groupby(map_series,axis=1).sum()
主科	副科
赵大	255	465
孙兄	276	465
钱术	269	499
吴羊	284	461
王女	259	525
王卜	273	524
王可	260	518
钱巾	274	452
李丁	270	481
吴丫	243	478
吴丑	266	511
钱火	266	465
王石	248	487
李人	246	511
郑正	256	435
王乃	249	475
李寸	247	478
郑之	289	437
钱口	274	417
孙寸	247	508
吴儿	254	507
李木	245	512
赵主	258	455
钱巨	287	516
郑电	257	516
赵工	245	524
李方	259	473
李门	243	463
李几	248	469
赵夫	256	504
吴井	267	474
郑天	273	521
孙计	254	439
郑尺	276	515
孙友	268	493
郑中	265	439
赵页	258	455
郑户	247	493
王了	242	511
赵爪	272	503
吴又	269	427
钱化	248	522
吴不	252	518
吴伏	241	470
王贝	260	472
李气	268	413
李田	265	452
李下	244	442
李匕	268	521
李心	283	457
# 直接创建Series使用列索引 变行索引,被转换的value就是分组依据
map_series2=Series(['主科']*3+['副科']*6,index=data.columns)
# 分组对象可以直接describe
data.groupby(map_series2,axis=1).describe()
count	mean	std	min	25%	50%	75%	max
主科	语文	50.0	94.26	2.947223	90.0	91.00	94.0	96.75	99.0
数学	50.0	88.98	6.192458	80.0	83.25	89.0	95.00	99.0
英语	50.0	77.22	11.775606	60.0	68.00	75.0	86.75	98.0
副科	化学	50.0	80.52	11.664074	60.0	71.00	80.5	89.75	99.0
物理	50.0	80.36	10.912939	62.0	71.00	79.0	89.75	99.0
生物	50.0	83.24	11.626079	60.0	72.00	85.0	92.75	99.0
政治	50.0	78.08	11.593172	60.0	69.00	79.0	88.00	98.0
历史	50.0	81.56	12.266031	60.0	71.75	82.0	93.75	99.0
地理	50.0	77.60	11.261276	60.0	68.25	76.0	87.00	99.0

其他聚合函数

# 假设有很多列,只需要一两列进行分组与计算。
df3 = DataFrame(
    np.random.randint(0,10,(10,3)),
    columns=list('ABC')
)
df3.insert(0,'key2','one,two,one,two,one'.split(',')*2)
df3.insert(0,'key1',list('aabbc')*2)
df3.groupby(['key1','key2']).sum()
A	B	C
key1	key2			
a	one	17	11	15
two	16	8	1
b	one	17	3	10
two	9	6	8
c	one	11	7	9
df3.groupby(['key1','key2']).max()
A	B	C
key1	key2			
a	one	9	7	9
two	9	7	1
b	one	9	3	6
two	5	5	8
c	one	9	5	6
df3.groupby(['key1','key2']).describe()
A	B	C
count	mean	std	min	25%	50%	75%	max	count	mean	...	75%	max	count	mean	std	min	25%	50%	75%	max
key1	key2																					
a	one	2.0	8.5	0.707107	8.0	8.25	8.5	8.75	9.0	2.0	5.5	...	6.25	7.0	2.0	7.5	2.121320	6.0	6.75	7.5	8.25	9.0
two	2.0	8.0	1.414214	7.0	7.50	8.0	8.50	9.0	2.0	4.0	...	5.50	7.0	2.0	0.5	0.707107	0.0	0.25	0.5	0.75	1.0
b	one	2.0	8.5	0.707107	8.0	8.25	8.5	8.75	9.0	2.0	1.5	...	2.25	3.0	2.0	5.0	1.414214	4.0	4.50	5.0	5.50	6.0
two	2.0	4.5	0.707107	4.0	4.25	4.5	4.75	5.0	2.0	3.0	...	4.00	5.0	2.0	4.0	5.656854	0.0	2.00	4.0	6.00	8.0
c	one	2.0	5.5	4.949747	2.0	3.75	5.5	7.25	9.0	2.0	3.5	...	4.25	5.0	2.0	4.5	2.121320	3.0	3.75	4.5	5.25	6.0
5 rows × 24 columns
df4 = DataFrame({
    'key1':list('aabbabaa')*2,
    'key2':'one/two/one/three/two/two/one/one'.split('/')*2,
    'data1': np.random.randint(1,100,16),
    'data2': np.random.randint(1,100,16)
})
df4
key1	key2	data1	data2
0	a	one	32	37
1	a	two	3	90
2	b	one	75	38
3	b	three	83	47
4	a	two	16	94
5	b	two	30	52
6	a	one	7	51
7	a	one	98	99
8	a	one	63	58
9	a	two	36	81
10	b	one	63	54
11	b	three	54	72
12	a	two	26	79
13	b	two	65	83
14	a	one	50	60
15	a	one	5	94
df4.groupby(['key1','key2']).size()
key1  key2 
a     one      6
      two      4
b     one      2
      three    2
      two      2
dtype: int64

自定义函数应用到 对象上

xxx.agg([funcName,fun1,fun2,fun3,…]) 可以是一个或多个函数
如果是Python中的常用函数,直接传 字符串形式的函数名进去 如 ‘sum’ ,‘mean’
如果是自定义的函数或者nump的函数,用函数名称 my_ptp , np.ptp

# 定义一个极差函数
def my_ptp(x):
    return x.max()-x.min()
# 应用到分组对象
df4.groupby(['key1','key2']).agg(my_ptp)
data1	data2
key1	key2		
a	one	93	62
two	33	15
b	one	12	16
three	29	25
two	35	31
# 也可以直接使用np.ptp极差函数
df4.groupby(['key1','key2']).agg(np.ptp)
data1	data2
key1	key2		
a	one	93	62
two	33	15
b	one	12	16
three	29	25
two	35	31
df4.groupby(['key1','key2']).agg(['mean','std','count',my_ptp])
data1	data2
mean	std	count	my_ptp	mean	std	count	my_ptp
key1	key2								
a	one	42.50	35.590729	6	93	66.5	24.647515	6	62
two	20.25	14.103782	4	33	86.0	7.164728	4	15
b	one	69.00	8.485281	2	12	46.0	11.313708	2	16
three	68.50	20.506097	2	29	59.5	17.677670	2	25
two	47.50	24.748737	2	35	67.5	21.920310	2	31
# 可以给这些聚合函数取别名  .agg(['显示名':'函数名'])
df4.groupby(['key1','key2']).agg([('平均值','mean'),('标准值','std'),('计数','count'),('极差',np.ptp)])
data1	data2
平均值	标准值	计数	极差	平均值	标准值	计数	极差
key1	key2								
a	one	42.50	35.590729	6	93	66.5	24.647515	6	62
two	20.25	14.103782	4	33	86.0	7.164728	4	15
b	one	69.00	8.485281	2	12	46.0	11.313708	2	16
three	68.50	20.506097	2	29	59.5	17.677670	2	25
two	47.50	24.748737	2	35	67.5	21.920310	2	31

还可以不同列数 ,使用不同的聚合函数

dict_map={
    'data1':['max','min','sum'],
    'data2':[('极差ptp',np.ptp),('方差var','var'),('标准差std','std')]
}
df4.groupby(['key1','key2']).agg(dict_map)
data1	data2
max	min	sum	极差ptp	方差var	标准差std
key1	key2						
a	one	98	5	255	62	607.500000	24.647515
two	36	3	81	15	51.333333	7.164728
b	one	75	63	138	16	128.000000	11.313708
three	83	54	137	25	312.500000	17.677670
two	65	30	95	31	480.500000	21.920310

as_index=True.默认让分组依据作为行索引

df4.groupby(['key1','key2'],as_index=False).agg(dict_map)
key1	key2	data1	data2
max	min	sum	极差ptp	方差var	标准差std
0	a	one	98	5	255	62	607.500000	24.647515
1	a	two	36	3	81	15	51.333333	7.164728
2	b	one	75	63	138	16	128.000000	11.313708
3	b	three	83	54	137	25	312.500000	17.677670
4	b	two	65	30	95	31	480.500000	21.920310
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值