分组键可以有多种方式,且类型不必相同
- 列表或数组, 某长度与待分组的轴一样
- 表示DataFrame某个列名的值
- 字典或Series,给出待分组轴上的值与分组名之间的对应关系
- 函数用于处理轴索引或索引中的各个标签
看一下示例:
import numpy as np import pandas as pd 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)}) df
Out[3]:
data1 data2 key1 key2
0 1.236062 -0.576333 a one
1 -1.925623 -0.874375 a two
2 1.472156 -0.005463 b one
3 -0.155766 2.318643 b two
4 -1.961012 -0.947843 a one
按key1分组,并计算data1列的平均值。访问data1,并根据key1调用groupby
#按key1分组,访问data1的平均值 df['data1'].groupby(df['key1']).mean() Out[4]: key1 a -0.883525 b 0.658195 Name: data1, dtype: float64
数据根据分组键进行了聚合,产生一个新的Series,其索引为key1列中的唯一值
如果一次传入多个数组,就会得到不同的结果
df['data1'].groupby([df['key1'], df['key2']]).mean() Out[5]: key1 key2 a one -0.362475 two -1.925623 b one 1.472156 two -0.155766 Name: data1, dtype: float64
#这里,通过两个键进行了分组,得到的Series具有一个层次化索引(由唯一的键对组成)
means.unstack()
Out[7]:
key2 one two
key1
a -0.362475 -1.925623
b 1.472156 -0.155766
GroupBy的size方法,可以返回一个含有分组大小的Series
df.groupby(['key1', 'key2']).size() Out[13]: key1 key2 a one 2 two 1 b one 1 two 1 dtype: int64
注意:分组键中的任何缺失 值都会被排除在结果之外
1、对分组进行迭代
GroupBy对象支持迭代,可以产生一组二元元组(由分组名和数据块组成)
for name, group in df.groupby('key1'): print(name) print(group) a data1 data2 key1 key2 0 1.236062 -0.576333 a one 1 -1.925623 -0.874375 a two 4 -1.961012 -0.947843 a one b data1 data2 key1 key2 2 1.472156 -0.005463 b one 3 -0.155766 2.318643 b two
对于多重键的情况,元组的第一个元素将会是由键值组成的元组:
for (k1, k2), group in df.groupby(['key1', 'key2']): print(k1, k2) print(group) a one data1 data2 key1 key2 0 1.236062 -0.576333 a one 4 -1.961012 -0.947843 a one a two data1 data2 key1 key2 1 -1.925623 -0.874375 a two b one data1 data2 key1 key2 2 1.472156 -0.005463 b one b two data1 data2 key1 key2 3 -0.155766 2.318643 b two
你可以对这些数据片段做任何操作,将这些数据片段做成一个字典:
pieces = dict(list(df.groupby('key1'))) pieces Out[18]: {'a': data1 data2 key1 key2 0 1.236062 -0.576333 a one 1 -1.925623 -0.874375 a two 4 -1.961012 -0.947843 a one, 'b': data1 data2 key1 key2 2 1.472156 -0.005463 b one 3 -0.155766 2.318643 b two}
pieces['b']
Out[19]:
data1 data2 key1 key2
2 1.472156 -0.005463 b one
3 -0.155766 2.318643 b two
groupby默认在axis = 0 上进行分组的,通过设置也可以在其他任何轴上进行分组
df.dtypes Out[20]: data1 float64 data2 float64 key1 object key2 object dtype: object
grouped = df.groupby(df.dtypes, axis = 1) dict(list(grouped)) Out[22]: {dtype('float64'): data1 data2 0 1.236062 -0.576333 1 -1.925623 -0.874375 2 1.472156 -0.005463 3 -0.155766 2.318643 4 -1.961012 -0.947843, dtype('O'): key1 key2 0 a one 1 a two 2 b one 3 b two 4 a one}
2、选取一个或一组列
对于由DataFrame产生的GroupBy对象,如果用一个(单个字符串)或一组(字符串数组)列名对其进行索引,就能实现选取部分列进行聚合的目的。也就是说
df.groupby('key1')['data1'] df.groupby('key1')['data2'] df['data1'].groupby(df['key1']) df['data2'].groupby(df['key2'])
尤其对于大数据集,很可能只需要对部分列进行聚合
df.groupby(['key1', 'key2'])[['data2']].mean() Out[25]: data2 key1 key2 a one -0.762088 two -0.874375 b one -0.005463 two 2.318643
这种索引操作所返回的对象是一个已分组的DataFrame或已分组的Series(如果传入的是标量形式的单个列名)
3、通过字典或Series进行分组
people.ix[2:3, ['b', 'c']] = np.nan #添加几个NA值 people Out[29]: a b c d e joe -0.237225 -0.583231 2.506186 0.764346 2.628579 Steve -0.125559 -0.185558 -0.226190 -1.624512 -0.988784 Wes 0.337773 NaN NaN -2.489380 -0.064929 Jim 1.052081 -0.888873 1.227694 0.809077 1.031245 Travis 0.042581 0.793400 0.316565 -1.098852 0.129532
假设已知列的分组关系,并根据根据分组计算列的总计
#ix再回顾温习一下 mapping = {'a': 'red', 'b': 'red', 'c': 'blue', 'd': 'blue', 'e': 'red', 'f': 'orange'} #现在只需将这个字典传给groupby即可 by_column = people.groupby(mapping, axis = 1) by_column.sum()
Out[32]:
blue red
joe 3.270531 1.808124
Steve -1.850702 -1.299900
Wes -2.489380 0.272844
Jim 2.036771 1.194453
Travis -0.782287 0.965513
Series也有同样的功能,被看做一个固定大小的映射,如果是Series作为分组键,则pandas会检查Series以确保其索引跟分组轴是对齐的
map_series = pd.Series(mapping) map_series Out[34]: a red b red c blue d blue e red f orange dtype: object
people.groupby(map_series, axis = 1).count() Out[35]: blue red joe 2 3 Steve 2 3 Wes 1 2 Jim 2 3 Travis 2 3
4、通过函数进行分组
根据人名的长度进行分组,虽然可以求取一个字符串长度数组,但其实仅仅传入len函数就可以
people.groupby(len).sum() Out[36]: a b c d e 3 1.152630 -1.472104 3.733879 -0.915957 3.594895 5 -0.125559 -0.185558 -0.226190 -1.624512 -0.988784 6 0.042581 0.793400 0.316565 -1.098852 0.129532
将函数跟数组、列表、字典、Series混合使用,因为任何东西最终都会被转换为数组
people.groupby([len, key_list]).min() #这个没有理解 Out[38]: a b c d e 3 one -0.237225 -0.583231 2.506186 -2.489380 -0.064929 two 1.052081 -0.888873 1.227694 0.809077 1.031245 5 one -0.125559 -0.185558 -0.226190 -1.624512 -0.988784 6 two 0.042581 0.793400 0.316565 -1.098852 0.129532
5、根据索引级别分组
层次化索引数据集最方便的地方就在于它能够根据索引级别进行聚合,要实现该目的,通过level关键字传入级别编号或名称即可
columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JP', 'JP'], [1, 3, 5, 1, 3]], names = ['cty', 'tenor']) hier_df = pd.DataFrame(np.random.randn(4, 5), columns = columns) hier_df Out[41]: cty US JP tenor 1 3 5 1 3 0 -0.248738 0.627953 2.071784 0.245686 -1.590562 1 1.723024 0.111605 -0.520688 -0.370661 -0.287072 2 0.061884 -0.502882 0.281061 0.083908 2.224092 3 -0.341529 0.945064 -0.785416 -0.443815 0.186289
hier_df.groupby(level = 'cty', axis =1).count() Out[42]: cty JP US 0 2 3 1 2 3 2 2 3 3 2 3
二、数据聚合
任何能够从数组产生标量值的数据转换过程。比如mean、 count 、min 以及 sum等。许多常见的聚合运算都有就地计算数据集统计信息的优化实现
quantile可以计算Series或DataFrame列的样本分位数
df
Out[43]:
data1 data2 key1 key2
0 1.236062 -0.576333 a one
1 -1.925623 -0.874375 a two
2 1.472156 -0.005463 b one
3 -0.155766 2.318643 b two
4 -1.961012 -0.947843 a one
grouped = df.groupby('key1') grouped grouped['data1'].quantile(0.9)
Out[47]:
key1
a 0.603725
b 1.309364
Name: data1, dtype: float64
#注意:如果传入的百分位上没有值,则quantile会进行线性插值
实际上,GroupBy会高效地对Series进行切片,然后对各片调用piece, quantile(0.9), 最后将这些结果组成成最终结果
使用自己的聚合函数,只需将其传入aggregate或agg方法即可
def peak_to_peak(arr): return arr.max() - arr.min() grouped.agg(peak_to_peak) Out[49]: data1 data2 key1 a 3.197074 0.371510 b 1.627922 2.324107
#describe也可以这样用
grouped.describe() Out[50]: data1 data2 key1 a count 3.000000 3.000000 mean -0.883525 -0.799517 std 1.835701 0.196743 min -1.961012 -0.947843 25% -1.943318 -0.911109 50% -1.925623 -0.874375 75% -0.344781 -0.725354 max 1.236062 -0.576333 b count 2.000000 2.000000 mean 0.658195 1.156590 std 1.151114 1.643392 min -0.155766 -0.005463 25% 0.251215 0.575563 50% 0.658195 1.156590 75% 1.065176 1.737617 max 1.472156 2.318643
经过优化的groupby方法
1、面向列的多函数应用
#备注,tips没有下载下来,这里仅作为展示实例 grouped = tips.groupby(['sex', 'smoker']) grouped_pct = grouped(['tip_pct']) grouped_pct.agg('mean') grouped_pct.agg('mean', 'std', peak_to_peak) #以无索引的形式返回聚合数据 tips.groupby(['sex', 'smoker'], as_index = False).mean()
三、分组级运算和转换
transform和apply方法,它能够执行更多其他的分组运算
为一个DataFrame添加 一个用于存储各索引分组平均值的列,一个办法是先聚合再合并
df Out[51]: data1 data2 key1 key2 0 1.236062 -0.576333 a one 1 -1.925623 -0.874375 a two 2 1.472156 -0.005463 b one 3 -0.155766 2.318643 b two 4 -1.961012 -0.947843 a one k1_means = df.groupby('key1').mean().add_prefix('mean_') k1_means Out[53]: mean_data1 mean_data2 key1 a -0.883525 -0.799517 b 0.658195 1.156590
pd.merge(df, k1_means, left_on = 'key1', right_index = True) Out[54]: data1 data2 key1 key2 mean_data1 mean_data2 0 1.236062 -0.576333 a one -0.883525 -0.799517 1 -1.925623 -0.874375 a two -0.883525 -0.799517 4 -1.961012 -0.947843 a one -0.883525 -0.799517 2 1.472156 -0.005463 b one 0.658195 1.156590 3 -0.155766 2.318643 b two 0.658195 1.156590
key = ['one', 'two', 'one', 'two', 'one'] people.groupby(key).mean() Out[57]: a b c d e one 0.047710 0.105085 1.411375 -0.941295 0.897728 two 0.463261 -0.537216 0.500752 -0.407717 0.021231 people.groupby(key).transform(np.mean) Out[58]: a b c d e joe 0.047710 0.105085 1.411375 -0.941295 0.897728 Steve 0.463261 -0.537216 0.500752 -0.407717 0.021231 Wes 0.047710 0.105085 1.411375 -0.941295 0.897728 Jim 0.463261 -0.537216 0.500752 -0.407717 0.021231 Travis 0.047710 0.105085 1.411375 -0.941295 0.897728
不难看出,transform会将一个函数应用到各个分组,然后将结果放置到适当的位置上
假设想从各分组中减去平均值,先创建一个距平化函数,然后将其传给transform
def demean(arr): return arr - arr.mean() demeaned = people.groupby(key).transform(demean) demeaned Out[60]: a b c d e joe -0.284934 -0.688315 1.094810 1.705641 1.730852 Steve -0.588820 0.351658 -0.726942 -1.216794 -1.010014 Wes 0.290063 NaN NaN -1.548084 -0.962657 Jim 0.588820 -0.351658 0.726942 1.216794 1.010014 Travis -0.005129 0.688315 -1.094810 -0.157557 -0.768195