对数据集进行分类,并在每一组上应用一个聚合函数或转换函数。在载入,合并,准备数据集后需要计算分组统计或者数据透视表用于报告或可视化的目的。pandas提供了一个灵活的groupby接口,允许你以一种自然的方式对数据集进行切片,切块和总结。
1.1 GroupBy 机制
数据包含在pandas对象中,可以是series or dataframe 或其他数据结构,之后根据你提供的一个或多个键分离到各个组中,分离操作是在数据对象的特定轴上进行的。例如,datafrme可以在行方向(axis=0) 或列方向(axis=1)进行分组。分组操作后对各个组应用函数产生新值,组合结构联合成一个结果对象。
分组键可以是多种形式,并且键不一定是相同的类型:
- 与需要分组的轴向长度一致的值列表或值数组
- dataframe的列名
- 可用将分组轴向 上的值和分组名称相匹配的字典或series。
- 可以在轴索引或索引中的单个标签上调用的函数。
df=pd.DataFrame({'key1':['a','a','b','b','a'],
'key2':['one','two','one','two','one'],
'data1': np.random.randn(5),
'data2': np.random.randn(5)})
key1 key2 data1 data2
0 a one 0.784580 0.966883
1 a two 0.023604 -0.488664
2 b one 1.974237 0.845493
3 b two 0.588121 0.564195
4 a one 0.643017 0.255800
grouped=df['data1'].groupby(df['key1']) # 根据key1标签计算data1列的均值。可以使用groupby方法
grouped # 得到一个GroupBy 对象。除了一些关于分组件df['key1'] 的一些中间数据之外,还没进行计算。
<pandas.core.groupby.generic.SeriesGroupBy object at 0x000001E641692CC8>
# 这个对象有所有必须的信息,可以在每一个分组上进行一些操作。
grouped.mean() # 调用GroupBy 的mean 方法求平均值。
key1
a 0.483734
b 1.281179 # 数据(一个series)根据分组键进行了聚合,并产生一个新的series,这个series使用key1 列的唯一值作为索引
Name: data1, dtype: float64
means=df['data1'].groupby([df['key1'],df['key2']]).mean() # 将多个数组作为列表传入。
means # 使用两个键对数据进行分组,并且结果 series 含有多层索引。
key1 key2 # 感觉这个groupby 就是将后面的参数变成索引,然后索引相同的进行计算。。。。
a one 0.713799
two 0.023604
b one 1.974237
two 0.588121
Name: data1, dtype: float64
means.unstack()
key2 one two
key1
a 0.713799 0.023604
b 1.974237 0.588121
states=np.array(['Ohio','California','California','Ohio','Ohio'])
years=np.array([2005,2005,2006,2005,2006]) # 分组键可以是正确长度的任意数组。
df['data1'].groupby([states,years]).mean()
California 2005 0.023604
2006 1.974237
Ohio 2005 0.686350
2006 0.643017
Name: data1, dtype: float64
df.groupby('key1').mean() # 分组信息作为你想要继续处理的数据,通常包含在同一个dataframe中。
# 这种情况下,你可以传递列名作为分组键
data1 data2
key1
a 0.483734 0.244673 # 可用看见这里没有key2 列,这是因为df['key2'] 并不是数值列,排除。。
b 1.281179 0.704844 # 啊。。怎么排除的。。
df.groupby(['key1','key2']).mean()
data1 data2
key1 key2
a one 0.713799 0.611341
two 0.023604 -0.488664
b one 1.974237 0.845493
two 0.588121 0.564195
df.groupby(['key1','key2']).size() # 通用的方法,返回包含组大小信息的series
key1 key2
a one 2
two 1
b one 1
two 1
dtype: int64
分组键中的任何缺失值将被排除在结果之外。
1.1.1 历遍各组
GroupBy 对象支持迭代,会生成一个包含组名和数据块的2维元组序列。
for name,group in df.groupby('key1'):
print(name)
print(group)
a
key1 key2 data1 data2
0 a one 0.784580 0.966883
1 a two 0.023604 -0.488664
4 a one 0.643017 0.255800
b
key1 key2 data1 data2
2 b one 1.974237 0.845493
3 b two 0.588121 0.564195
for (k1,k2),group in df.groupby(['key1','key2']):
print((k1,k2))
print(group)
('a', 'one')
key1 key2 data1 data2
0 a one 0.784580 0.966883
4 a one 0.643017 0.255800
('a', 'two')
key1 key2 data1 data2
1 a two 0.023604 -0.488664
('b', 'one')
key1 key2 data1 data2
2 b one 1.974237 0.845493
('b', 'two')
key1 key2 data1 data2
3 b two 0.588121 0.564195
pieces =dict(list(df.groupby('key1'))) # 转换成字典,选择在任何一块数据上进行操作,
pieces['b']
key1 key2 data1 data2
2 b one 1.974237 0.845493
3 b two 0.588121 0.564195
# 默认情况下,groupbt 在axis-0 的轴向上分组,也可以在其他任意轴向上进行分组,
df.dtypes
key1 object
key2 object
data1 float64
data2 float64
dtype: object
grouped=df.groupby(df.dtypes,axis=1)
for dtype,group in grouped:
print(dtype)
print(group)
float64
data1 data2
0 0.784580 0.966883
1 0.023604 -0.488664
2 1.974237 0.845493
3 0.588121 0.564195
4 0.643017 0.255800
object
key1 key2
0 a one
1 a two
2 b one
3 b two
4 a one
1.1.2 选择一列或所有列的子集
将从dataframe 创建的GroupBy 对象用列名称或列名称数组进行索引时,会产生用于聚合的列子集的效果。
df.groupby('key1')['data1'].size() # == df['data1'].groupby(df['key1'])
key1
a 3
b 2
Name: data1, dtype: int64
df.groupby('key1')[['data2']].size() # == df[['data2']].groupby(df['key1'])
df.groupby(['key1','key2'])[['data2']].mean()
data2
key1 key2
a one 0.611341 # 返回dataframe 的形式
two -0.488664
b one 0.845493
two 0.564195
# 如果传递的是列表或数组,则此索引操作返回的对象是分组的dataframe,如果只有单个列作为标量传递,则为分组的sereis。
s_grouped=df.groupby(['key1','key2'])['data2']
s_grouped
<pandas.core.groupby.generic.SeriesGroupBy object at 0x000001E641A5E248>
s_grouped.mean()
key1 key2
a one 0.611341 # seres 的形式
two -0.488664
b one 0.845493
two 0.564195
Name: data2, dtype: float64
1.1.1 使用字典和series分组
分组信息可能会以非数组形式存在。
people=pd.DataFrame(np.random.randn(5,5),columns=['a','b','c','d','e'],
index=['Joe','Steve','Wes','Jim','Travis'])
people.iloc[2:3,[1,2]]=np.nan
people
a b c d e
Joe 0.212254 0.167760 0.478722 1.340769 1.287925
Steve -1.071535 1.101858 -2.195090 0.306472 1.000913
Wes -0.358333 NaN NaN -0.155213 1.126299
Jim 1.286113 -0.886354 -0.488328 -0.131525 1.012031
Travis 0.042763 -0.425792 -0.758249 0.185632 1.143018
mapping={'a':'red','b':'red','c':'blue','d':'blue','e':'red','f':'orange'} # 建立个列的分组关系,然后累加
people.groupby(mapping,axis=1).sum() # 添加的f 是表示为用的分组键也是莫问题的。。
blue red
Joe 1.819491 1.667939
Steve -1.888619 1.031236
Wes -0.155213 0.767966
Jim -0.619853 1.411790
Travis -0.572617 0.759989
map_series=pd.Series(mapping)
map_series
a red
b red
c blue
d blue
e red
f orange
dtype: object
people.groupby(map_series,axis=1).count()
blue red
Joe 2 3
Steve 2 3
Wes 1 2
Jim 2 3
Travis 2 3
1.1.4 使用函数分组
作为分组键传递的函数将会按照每个索引值调用一次,同时返回值会被用作分组名称。就如上面那个例子,以人的名字作为索引值,以名字长度进行分组。虽然可用计算出字符串长度的数组,但是传递len函数更加简单,
people.groupby(len).sum()
a b c d e
3 1.140035 -0.718594 -0.009606 1.054031 3.426255
5 -1.071535 1.101858 -2.195090 0.306472 1.000913
6 0.042763 -0.425792 -0.758249 0.185632 1.143018
key_list=['one','one','one','two','two']
people.groupby([len,key_list]).min() # 将函数与数组,字典或series进行混合,所有对象都会在内部转换为数组。
a b c d e
3 one -0.358333 0.167760 0.478722 -0.155213 1.126299
two 1.286113 -0.886354 -0.488328 -0.131525 1.012031
5 one -1.071535 1.101858 -2.195090 0.306472 1.000913
6 two 0.042763 -0.425792 -0.758249 0.185632 1.143018
1.1.5 根据索引层级分组
能够在轴索引的某个层级上进行聚合,这就很方便
columns=pd.MultiIndex.from_arrays([['US','US','US','JP','JP'],
[1,3,5,1,3]],
names=['city','tenor'])
hief_df=pd.DataFrame(np.random.randn(4,5),columns=columns)
hief_df
city US JP
tenor 1 3 5 1 3
0 0.203905 0.337928 1.020350 0.712070 1.372785
1 -0.556021 -0.490895 -0.051140 -1.117818 -1.368995
2 -1.012746 0.855241 0.037147 1.202405 0.763244
3 -0.118763 0.815760 -2.313174 -0.549343 0.318364
hief_df.groupby(level='city',axis=1).count()
city JP US
0 2 3
1 2 3
2 2 3
3 2 3