Pandas入门(中)
来源:互联网 发布:keilc51中文版软件 编辑:程序博客网 时间:2024/06/03 06:03
Pandas入门(中)
Martin
- Pandas入门中
- GroupBy技术
- 对分组进行迭代
- 选取一个或一组列
- 通过字典或Series进行分组
- 通过函数进行分组
- 根据索引级别分组
- 数据聚合
- 面向列的多函数应用
- 以无索引形式返回聚合数据
- 分组级运算和转换
- apply一般性的拆分-应用-合并
- 禁止分组键
- 透视表
- GroupBy技术
GroupBy技术
“split-apply-combine”(拆分-应用-合并),这个几个词很好的描述了分组运算的整个过程。分组运算的第一个阶段,pandas
对象(无论是Series
是还DataFrame
抑或其他)中的数据会根据你所提供的一个或者多个键被拆分(split)为多组。拆分操作是在对象的特定轴上执行的。例如,DataFrame
可以在其行(axis=0)或列(axis=1)上进行分组。然后,将一个函数应用apply
到各个分组并产生一个新值。最后所有这些函数的执行结果会被合并(combine
)到最终的结果对象中。结果对象的形式一般取决于数据上所执行的操作。下图可以大致说明一个简单的分组聚合过程:
分组可以有多种形式,且类型不必相同:
- 列表或数组,其长度与待分组的轴一样。
- 表示
DataFrame
某个列名的值。 - 字典或
Series
,给出待分组轴上的值与分组名之间的对应关系。 - 函数,用于处理轴索引或索引中的各标签。
后三种都是快捷方式而已,其最终目的仍是产生一组用于拆分对象的值。
来个例子:
>>> df = DataFrame({'key1':['a','a','b','b','a'],'key2':['one','two','one','two','one'],'data1':np.random.randn(5),'data2':np.random.randn(5)})>>> df data1 data2 key1 key20 -0.237560 -0.070793 a one1 0.285330 -1.242159 a two2 -0.447567 -0.850921 b one3 2.644768 -1.463350 b two4 -1.062439 0.454708 a one
假设你想要按key1
进行分组,并计算data1
列的平均值。实现该功能的方式有很多,而我们这里要用的是:访问data1
,并根据key1
调用groupby
:
>>> grouped = df['data1'].groupby(df['key1'])>>> grouped<pandas.core.groupby.SeriesGroupBy object at 0x7f35bb45fbd0>
变量grouped
是一个GroupBy
对象。它实际上还没有进行任何的计算,只是含有一些有关分组键df['key1']
的中间数据而已。换句话说,该对象已经有了接下来对各分组执行运算所需的一切信息。例如,我们可以调用GroupBy
的mean
方法来计算分组平均值:
>>> grouped.mean()key1a -0.338223b 1.098601Name: data1, dtype: float64
一会将详细讲解mean
方法,这里最重要的是,数据(Series
)根据分组键进行了聚合,产生了一个新的Series
,其索引为key1
列总的唯一值。之所以结果中索引的名称为key1
,是因为原始的DataFrame
列df['key1']
就叫这个名字。如果我们一次传入多个数组,就会得到不同的结果:
>>> means = df['data1'].groupby([df['key1'],df['key2']]).mean()>>> meanskey1 key2a one -0.650000 two 0.285330b one -0.447567 two 2.644768Name: data1, dtype: float64
这里,通过两个键对数据进行了分组,得到的Series
具有一个层次化索引(由唯一的键对组成):
>>> means.unstack() # 之前讲的unstack方法key2 one twokey1 a -0.650000 0.285330b -0.447567 2.644768
在上面的例子中,分组键均为Series
,实际上,分组键可以是任何长度适当的数组:
>>> 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.285330 2006 -0.447567Ohio 2005 1.203604 2006 -1.062439Name: data1, dtype: float64
此外,还可以将列名当成分组键:
>>> df.groupby('key1').mean() # 未指明哪个数据列就是默认全部(data1和data2) data1 data2key1 a -0.338223 -0.286081b 1.098601 -1.157135
可能已经注意到了,在执行df.groupby('key1').mean()
时,结果中没有key2列
。这是因为df['key2']
不是数值数据(俗称麻烦列),所以被从结果中排除了。默认情况下,所有数值列都会被聚合。
无论准备拿groupby
做什么,都有可能用到GroupBy
的size
方法,它可以返回一个含有分组大小的Series
:
>>> df data1 data2 key1 key20 0.676933 -0.320961 a one1 -0.695488 0.151366 a two2 -1.268873 -1.337475 b one3 -0.369639 1.246996 b two4 1.193304 -0.607347 a one>>> df.groupby(['key1','key2']).size()key1 key2a one 2 two 1b one 1 two 1dtype: int64
对分组进行迭代
GroupBy
对象虽然保存了一些中间数据,但是不可见,不过它却支持迭代,可以产生一组二元元组(由分组名和数据块组成),还是上例子说话再:)
>>> df.groupby('key1')<pandas.core.groupby.DataFrameGroupBy object at 0x7f3cc35f5410>>>> for name,group in df.groupby('key1'): # GroupBy对象迭代... print name... print group... a # 分组名1 data1 data2 key1 key2 # 数据块10 0.676933 -0.320961 a one1 -0.695488 0.151366 a two4 1.193304 -0.607347 a oneb # 分组名2 data1 data2 key1 key2 # 数据块22 -1.268873 -1.337475 b one3 -0.369639 1.246996 b two
对于多重键的情况,元组的第一个元素将会是由键值组成的元组:
>>> for (k1,k2),group in df.groupby(['key1','key2']):... print k1,k2... print group... a one data1 data2 key1 key20 0.676933 -0.320961 a one4 1.193304 -0.607347 a onea two data1 data2 key1 key21 -0.695488 0.151366 a twob one data1 data2 key1 key22 -1.268873 -1.337475 b oneb two data1 data2 key1 key23 -0.369639 1.246996 b two
当然,可以对这些数据片段做任何操作。有一个你可能会觉得有用的运算,将这些数据片段做成一个字典:
>>> pieces = dict(list(df.groupby('key1')))>>> pieces['a'] data1 data2 key1 key20 0.676933 -0.320961 a one1 -0.695488 0.151366 a two4 1.193304 -0.607347 a one>>> pieces['b'] data1 data2 key1 key22 -1.268873 -1.337475 b one3 -0.369639 1.246996 b two
groupby
默认是在axis=0上进行分组的,通过设置也可以在其他任何轴上进行分组。拿上df
例子来说,我们可以根据dtype
对列进行分组:
>>> df.dtypesdata1 float64data2 float64key1 objectkey2 objectdtype: object>>> dict(list(df.groupby(df.dtypes,axis=1))){dtype('O'): key1 key20 a one1 a two2 b one3 b two4 a one, dtype('float64'): data1 data20 0.676933 -0.3209611 -0.695488 0.1513662 -1.268873 -1.3374753 -0.369639 1.2469964 1.193304 -0.607347}
选取一个或一组列
对于由DataFrame
产生的GroupBy
对象,如果用一个(单个字符)或一组(字符串数组)列名对其进行索引,就能实现选取部分列进行聚合的目的。也就是说:
>>>df.groupby('key1')['data1']>>>df.groupby('key1')[['data2']]
是以下代码的语法糖(一种代码的简洁形式):
>>>df['data1'].groupby(df['key1'])>>>df[['data2']].groupby(df['key1'])
尤其对于大数据集,很可能只需要对部分列进行聚合。例如,在前面那个数据集中,如果只需计算data2
列的平均值并以DataFrame
形式得到结果,我们可以编写:
>>> df.groupby(['key1','key2'])[['data2']].mean() data2key1 key2 a one -0.464154 two 0.151366b one -1.337475 two 1.246996
这种索引操作所返回的对象是一个已分组的DataFrame
(如果传入的是列表或数组)或已分组的Series
(如果传入的是标量形式的单个列名):
>>> type(df.groupby(['key1','key2'])['data2']) # 标量,返回Series<class 'pandas.core.groupby.SeriesGroupBy'>>>> type(df.groupby(['key1','key2'])[['data2']]) # 列表,返回DataFrame<class 'pandas.core.groupby.DataFrameGroupBy'>
通过字典或Series进行分组
除数组以外,分组信息还可以其他形式存在。来看另一个例子:
>>> people = DataFrame(np.random.randn(5,5),columns=['a','b','c','d','e'],index=['Joe','Steve','Wes','Jim','Travis'])>>> people a b c d eJoe 1.194226 1.847886 2.063846 1.227742 1.087048Steve -0.950449 -0.176224 -0.646874 0.381825 -1.584446Wes 1.195563 -3.315661 -0.393060 0.590954 -0.428106Jim 1.344869 1.364735 0.227360 0.195137 0.325883Travis -0.232074 1.602136 -0.018983 -0.887190 0.554802>>> people.ix[2:3,['b','c']] = np.nan>>> people a b c d eJoe 1.194226 1.847886 2.063846 1.227742 1.087048Steve -0.950449 -0.176224 -0.646874 0.381825 -1.584446Wes 1.195563 NaN NaN 0.590954 -0.428106Jim 1.344869 1.364735 0.227360 0.195137 0.325883Travis -0.232074 1.602136 -0.018983 -0.887190 0.554802>>> mapping = {'a':'red','b':'red','c':'blue','d':'blue','e':'red','f':'orange'}
现在只需将这个字典传给groupby
即可:
>>> by_column = people.groupby(mapping,axis=1)>>> by_column.sum() blue redJoe 3.291588 4.129159Steve -0.265049 -2.711119Wes 0.590954 0.767457Jim 0.422497 3.035487Travis -0.906173 1.924865
还可以这样
>>> mapping2 = {'Joe':'one','Steve':'two','Wes':'one','Jim':'three','Travis':'two'}>>> by_row = people.groupby(mapping2,axis=0)>>> by_row.sum() a b c d eone 2.389788 1.847886 2.063846 1.818696 0.658942three 1.344869 1.364735 0.227360 0.195137 0.325883two -1.182522 1.425912 -0.665857 -0.505364 -1.029644
可能你也注意到了,我之前添加了几个NA
值,进行sum
后都自动过滤了。
Series
也有同样地功能,它可以被看做是一个固定大小的映射。对于上面的例子,如果用Series
作为分组键,则pandas
会检查Series
以确保其索引跟分组轴是对齐的:
>>> map_series = Series(mapping) # 先将dict转换成Series>>> map_seriesa redb redc blued bluee redf orangedtype: object>>> people.groupby(map_series,axis=1).count() blue redJoe 2 3Steve 2 3Wes 1 2Jim 2 3Travis 2 3
通过函数进行分组
相较于字典或者Series
,python函数在定义分组映射关系时可以更有创意且更为抽象。任何被当做分组键的函数都会在各个索引值上被调用一次,其返回至就会被用作分组名称。来个例子:
>>> people.groupby(len).sum() a b c d e3 3.734657 3.212620 2.291206 2.013833 0.9848265 -0.950449 -0.176224 -0.646874 0.381825 -1.5844466 -0.232074 1.602136 -0.018983 -0.887190 0.554802
将函数跟数组、列表 、字典、Series混合使用也不是问题,因为任何东西最终都会被转换为数组:
>>> key_list = ['one','one','one','two','two']>>> people.groupby([len,key_list]).min() a b c d e3 one 1.194226 1.847886 2.063846 0.590954 -0.428106 two 1.344869 1.364735 0.227360 0.195137 0.3258835 one -0.950449 -0.176224 -0.646874 0.381825 -1.5844466 two -0.232074 1.602136 -0.018983 -0.887190 0.554802
根据索引级别分组
层次化索引数据集最方便的地方就在于它能够根据索引级别进行聚合。要实现该目的通过关键字key
传入级别编号或名称即可:
>>> columns = pd.MultiIndex.from_arrays([['US','US','US','JP','JP'],[1,3,5,1,3]],names=['city','tenor'])>>> hier_df = DataFrame(np.random.randn(4,5),columns=columns)>>> hier_dfcity US JP tenor 1 3 5 1 30 0.844416 -0.746479 0.260580 -0.529815 1.1275441 -0.286859 -0.305911 -0.488367 -1.634609 0.1452592 0.857976 -1.911634 0.732285 1.290351 0.8241453 0.274936 -0.527338 -0.586308 0.996981 0.387922>>> hier_df.groupby(level='city',axis=1).count()city JP US0 2 31 2 32 2 33 2 3
数据聚合
如果你要使用自己的聚合函数,只需要将其传入aggregate
或agg
方法即可:
>>> def peak_to_peak(arr):... return arr.max() - arr.min()... >>> grouped.agg(peak_to_peak)key1a 2.621552b 1.243450Name: data1, dtype: float64
面向列的多函数应用
已经看到了,对Series
和DataFrame
列的聚合运算其实就是使用aggregate
、agg
或者调用诸如mean
、std
之类的方法。然而,我们更希望可以对不同的列使用不同的聚合函数,或一次应用多个函数。
在接下来,将使用一个有关餐馆小费的数据集:
>>> tips = pd.read_csv('下载/book_data/ch08/tips.csv') # 文件存放的路径
添加消费占总额百分比tips_pct列
>>> tips['tip_pct'] = tips['tip'] / tips['total_bill']>>> tips[:6] total_bill tip sex smoker day time size tip_pct0 16.99 1.01 Female No Sun Dinner 2 0.0594471 10.34 1.66 Male No Sun Dinner 3 0.1605422 21.01 3.50 Male No Sun Dinner 3 0.1665873 23.68 3.31 Male No Sun Dinner 2 0.1397804 24.59 3.61 Female No Sun Dinner 4 0.1468085 25.29 4.71 Male No Sun Dinner 4 0.186240
现在根据sex
和smoker
进行分组:
>>> grouped = tips.groupby(['sex','smoker'])>>> grouped_pct = grouped['tip_pct']>>> grouped_pct.agg('mean')sex smokerFemale No 0.156921 Yes 0.182150Male No 0.160669 Yes 0.152771Name: tip_pct, dtype: float64
如果传入一组函数或函数名,得到的DataFrame
的列就会以相应的函数名命名:
>>> grouped_pct.agg(['mean','std']) mean stdsex smoker Female No 0.156921 0.036421 Yes 0.182150 0.071595Male No 0.160669 0.041849 Yes 0.152771 0.090588
并非一定要接受GroupBy
自动给出的那些列名,特别是lambda
函数,它们的名称是<lambda>
,这样的辨识度就很低了,如果传入的是一个(name,function)元组组成的列表,则各元组的第一个元素就会被用作DataFrame
的列名(可以将这种二元元组列表看做一个有序映射):
>>> grouped_pct.agg([('foo','mean'),('bar',np.std)]) foo barsex smoker Female No 0.156921 0.036421 Yes 0.182150 0.071595Male No 0.160669 0.041849 Yes 0.152771 0.090588
对于DataFrame
,你还可以定义一组应用于全部列的函数,或不同的列应用不同的函数,假设我们想要对tip_pct
和total-bill
列计算三个统计信息:
>>> functions = ['count','mean','max']>>> result = grouped['tip_pct','total_bill'].agg(functions)>>> result tip_pct total_bill count mean max count mean maxsex smoker Female No 54 0.156921 0.252672 54 18.105185 35.83 Yes 33 0.182150 0.416667 33 17.977879 44.30Male No 97 0.160669 0.291990 97 19.791237 48.33 Yes 60 0.152771 0.710345 60 22.284500 50.81
如结果所示,DataFrame
拥有层次化的列,这相当于分别对各列进行整合,然后用concat
(连接函数)将结果组装到一起(列名用作keys参数)。
>>> result['tip_pct'] count mean maxsex smoker Female No 54 0.156921 0.252672 Yes 33 0.182150 0.416667Male No 97 0.160669 0.291990 Yes 60 0.152771 0.710345
跟前面一样,这里也可以传入带有自定义名称的元组列表。
>>> ftuples = [('Durchschnitt','mean'),('Abweichung',np.var)]>>> grouped['tip_pct','total_bill'].agg(ftuples) tip_pct total_bill Durchschnitt Abweichung Durchschnitt Abweichungsex smoker Female No 0.156921 0.001327 18.105185 53.092422 Yes 0.182150 0.005126 17.977879 84.451517Male No 0.160669 0.001751 19.791237 76.152961 Yes 0.152771 0.008206 22.284500 98.244673
现在,假设你想要对不同的列应用不同的函数。具体的办法及时向agg
传入一个从列名映射到函数的字典:
>>> grouped.agg({'tip':np.max,'size':'sum'}) tip sizesex smoker Female No 5.2 140 Yes 6.5 74Male No 9.0 263 Yes 10.0 150
以“无索引”形式返回聚合数据
到目前为止,所有实例中的聚合数据都有唯一的分组键组成的索引(可能还是层次化的)。由于并不总是需要如此,所以你可以向groupby
传入as_index=False
以禁用该功能:
>>> tips.groupby(['sex','smoker'],as_index=False).mean() # 禁用 sex smoker total_bill tip size tip_pct0 Female No 18.105185 2.773519 2.592593 0.1569211 Female Yes 17.977879 2.931515 2.242424 0.1821502 Male No 19.791237 3.113402 2.711340 0.1606693 Male Yes 22.284500 3.051167 2.500000 0.152771>>> tips.groupby(['sex','smoker']).mean() # 未禁用 total_bill tip size tip_pctsex smoker Female No 18.105185 2.773519 2.592593 0.156921 Yes 17.977879 2.931515 2.242424 0.182150Male No 19.791237 3.113402 2.711340 0.160669 Yes 22.284500 3.051167 2.500000 0.152771
分组级运算和转换
聚合只不过是分组运算的一种而已。它是数据转换的一个特例。也就是说它接受能够将一维数组简化为标量值的函数。在本节中,将介绍transform
和apply
,它们能够执行更多其他分组运算。
假设我们想要为一个DataFrame
添加一个用于存放各索引分组平均值的列。一个办法是先聚合再合并:
>>> df data1 data2 key1 key20 1.289781 0.439950 a one1 -0.337467 -0.560815 a two2 -0.222298 -1.229548 b one3 0.136132 0.293326 b two4 -1.433943 -2.416655 a one>>> k1_means = df.groupby('key1').mean().add_prefix('mean')>>> k1_means meandata1 meandata2 # 列名加了前缀key1 a -0.160543 -0.845840b -0.043083 -0.468111>>> pd.merge(df,k1_means,left_on='key1',right_index=True) data1 data2 key1 key2 meandata1 meandata20 1.289781 0.439950 a one -0.160543 -0.8458401 -0.337467 -0.560815 a two -0.160543 -0.8458404 -1.433943 -2.416655 a one -0.160543 -0.8458402 -0.222298 -1.229548 b one -0.043083 -0.4681113 0.136132 0.293326 b two -0.043083 -0.468111
merge
函数是进行表的连接操作,如果不明白其中参数的意思可以使用help(pd.merge)
命令进行查询。
虽然这样也行,但是不太灵活。你可以将该过程看做利用np.mean
函数对两个数据列进行行方向广播转换。再以之前用过的那个peopleDataFrame
为例,这次在GroupBy
上使用transform
方法:
>>> key = ['one','two','one','two','one']>>> people.groupby(key).mean() a b c d eone -0.16407 -0.006354 -0.404462 0.611442 -0.206593two -0.06096 0.516483 -0.036140 0.349761 -0.649544>>> people.groupby(key).transform(np.mean) a b c d eJoe -0.16407 -0.006354 -0.404462 0.611442 -0.206593Steve -0.06096 0.516483 -0.036140 0.349761 -0.649544Wes -0.16407 -0.006354 -0.404462 0.611442 -0.206593Jim -0.06096 0.516483 -0.036140 0.349761 -0.649544Travis -0.16407 -0.006354 -0.404462 0.611442 -0.206593
不难看出,transform
会将一个函数应用到各个分组,然后将结果放置到适当的位置上。如果各分组产生的是一个标量值,则该值就会被广播出去。
apply:一般性的“拆分-应用-合并”
回到之前的那个小费的数据集:
>>> tips[:7] total_bill tip sex smoker day time size tip_pct0 16.99 1.01 Female No Sun Dinner 2 0.0594471 10.34 1.66 Male No Sun Dinner 3 0.1605422 21.01 3.50 Male No Sun Dinner 3 0.1665873 23.68 3.31 Male No Sun Dinner 2 0.1397804 24.59 3.61 Female No Sun Dinner 4 0.1468085 25.29 4.71 Male No Sun Dinner 4 0.1862406 8.77 2.00 Male No Sun Dinner 2 0.228050
假设你想要根据分组选出最高的5个tip_pct
值,首先,编写一个选取指定列具有最大值的行的函数:
>>> def top(df,n=5,column='tip_pct'):... return df.sort_values(by=column)[-n:]... >>> top(tips,n=6) total_bill tip sex smoker day time size tip_pct109 14.31 4.00 Female Yes Sat Dinner 2 0.279525183 23.17 6.50 Male Yes Sun Dinner 4 0.280535232 11.61 3.39 Male No Sat Dinner 2 0.29199067 3.07 1.00 Female Yes Sat Dinner 1 0.325733178 9.60 4.00 Female Yes Sun Dinner 2 0.416667172 7.25 5.15 Male Yes Sun Dinner 2 0.710345
现在,如果对smoker
分组并用该函数调用apply
,就会得到:
>>> tips.groupby('smoker').apply(top) total_bill tip sex smoker day time size tip_pctsmoker No 88 24.71 5.85 Male No Thur Lunch 2 0.236746 185 20.69 5.00 Male No Sun Dinner 5 0.241663 51 10.29 2.60 Female No Sun Dinner 2 0.252672 149 7.51 2.00 Male No Thur Lunch 2 0.266312 232 11.61 3.39 Male No Sat Dinner 2 0.291990Yes 109 14.31 4.00 Female Yes Sat Dinner 2 0.279525 183 23.17 6.50 Male Yes Sun Dinner 4 0.280535 67 3.07 1.00 Female Yes Sat Dinner 1 0.325733 178 9.60 4.00 Female Yes Sun Dinner 2 0.416667 172 7.25 5.15 Male Yes Sun Dinner 2 0.710345
top函数在
DataFrame的各个片段上调用,然后结果由
concat组装到一起,并以分组名称进行了标记。于是,最终结果就有了一个层次换索引,其内层索引值来自原
DataFrame`。
如果传给apply
的函数能够接受其他参数或关键字,则可以将这些内容放在函数名后面一并传入:
>>> tips.groupby('smoker').apply(top,n=1,column='total_bill') total_bill tip sex smoker day time size tip_pctsmoker No 212 48.33 9.0 Male No Sat Dinner 4 0.186220Yes 170 50.81 10.0 Male Yes Sat Dinner 3 0.196812
禁止分组键
从上面的例子可以看出,分组键会跟原始对象的索引共同构成结果对象中的层次化苏音。将group_keys=False
传入groupby
即可禁止该效果:
>>> tips.groupby('smoker',group_keys=False).apply(top) total_bill tip sex smoker day time size tip_pct88 24.71 5.85 Male No Thur Lunch 2 0.236746185 20.69 5.00 Male No Sun Dinner 5 0.24166351 10.29 2.60 Female No Sun Dinner 2 0.252672149 7.51 2.00 Male No Thur Lunch 2 0.266312232 11.61 3.39 Male No Sat Dinner 2 0.291990109 14.31 4.00 Female Yes Sat Dinner 2 0.279525183 23.17 6.50 Male Yes Sun Dinner 4 0.28053567 3.07 1.00 Female Yes Sat Dinner 1 0.325733178 9.60 4.00 Female Yes Sun Dinner 2 0.416667172 7.25 5.15 Male Yes Sun Dinner 2 0.710345
透视表
透视表(pivot table)是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个矩形区域。DataFrame
有一个pivot_table
方法,此外还有一个顶级的pandas.pivot_table
函数。除能为groupby
提供便利之外,pivot_table
还可以添加分项小计(也叫做margins
)。
回到消费数据集,假设我想要根据sex
和smoker
计算分组平均数(pivot_table的默认聚合类型是计算平均数,想要修改可以修改参数aggfunc=‘mean’
),并将sex
和smoker
放到行上:
>>> tips.pivot_table(index=['sex','smoker']) # 老版本为rows新版本改为index size tip tip_pct total_billsex smoker Female No 2.592593 2.773519 0.156921 18.105185 Yes 2.242424 2.931515 0.182150 17.977879Male No 2.711340 3.113402 0.160669 19.791237 Yes 2.500000 3.051167 0.152771 22.284500
这对groupby
来说也是很简单的事。
>>> tips.groupby(['sex','smoker']).mean() # 一样的效果 total_bill tip size tip_pctsex smoker Female No 18.105185 2.773519 2.592593 0.156921 Yes 17.977879 2.931515 2.242424 0.182150Male No 19.791237 3.113402 2.711340 0.160669 Yes 22.284500 3.051167 2.500000 0.152771
现在,假设我们只想聚合tips_pct
和size
,而且想根据day
进行分组。将smoker
放到列上,把day
放到行上:
>>> tips.pivot_table(['tip_pct','size'],index=['sex','day'],columns=['smoker']) tip_pct size smoker No Yes No Yessex day Female Fri 0.165296 0.209129 2.500000 2.000000 Sat 0.147993 0.163817 2.307692 2.200000 Sun 0.165710 0.237075 3.071429 2.500000 Thur 0.155971 0.163073 2.480000 2.428571Male Fri 0.138005 0.144730 2.000000 2.125000 Sat 0.162132 0.139067 2.656250 2.629630 Sun 0.158291 0.173964 2.883721 2.600000 Thur 0.165706 0.164417 2.500000 2.300000
但是如果想要利用groupby
来实现此效果就不能行得通了。
还可以对这个表作进一步处理,传入margins-True
添加分项小计。这将会添加标签为All
的行和列,其值对应于单个等级中所有数据的分组统计。在下面这个例子中,All
值为平均数:不单独考虑烟民与非烟民(All列),不单独考虑行分组两个级别中的任何单项(All行):
>>> tips.pivot_table(['tip_pct','size'],index=['sex','day'],columns='smoker',margins=True) tip_pct size smoker No Yes All No Yes Allsex day Female Fri 0.165296 0.209129 0.199388 2.500000 2.000000 2.111111 Sat 0.147993 0.163817 0.156470 2.307692 2.200000 2.250000 Sun 0.165710 0.237075 0.181569 3.071429 2.500000 2.944444 Thur 0.155971 0.163073 0.157525 2.480000 2.428571 2.468750Male Fri 0.138005 0.144730 0.143385 2.000000 2.125000 2.100000 Sat 0.162132 0.139067 0.151577 2.656250 2.629630 2.644068 Sun 0.158291 0.173964 0.162344 2.883721 2.600000 2.810345 Thur 0.165706 0.164417 0.165276 2.500000 2.300000 2.433333All 0.159328 0.163196 0.160803 2.668874 2.408602 2.569672
那么这个All
列是怎么来的呢?什么叫不单独考虑烟民与非烟民(All列)
?我们以第一行的第二列All
来进行举例,其实就进行了如下操作:
>>> tips_Fir = tips[tips['day']=='Fri']>>> tips_result = tips_Fir[tips_Fir['sex']=='Female']>>> tips_result total_bill tip sex smoker day time size tip_pct92 5.75 1.00 Female Yes Fri Dinner 2 0.17391393 16.32 4.30 Female Yes Fri Dinner 2 0.26348094 22.75 3.25 Female No Fri Dinner 2 0.142857100 11.35 2.50 Female Yes Fri Dinner 2 0.220264101 15.38 3.00 Female Yes Fri Dinner 2 0.195059221 13.42 3.48 Female Yes Fri Lunch 2 0.259314223 15.98 3.00 Female No Fri Lunch 3 0.187735225 16.27 2.50 Female Yes Fri Lunch 2 0.153657226 10.09 2.00 Female Yes Fri Lunch 2 0.198216>>> tips_result['size'].mean()2.111111111111111
All
行道理和列一样,不在进行阐述。
要使用其他的聚合函数,将其传给aggfunc
即可。例如,使用count
或len
可以得到有关分组大小的交叉表:
>>> tips.pivot_table('tip_pct',index=['sex','smoker'],columns='day',aggfunc=len,margins=True)day Fri Sat Sun Thur Allsex smoker Female No 2.0 13.0 14.0 25.0 54.0 Yes 7.0 15.0 4.0 7.0 33.0Male No 2.0 32.0 43.0 20.0 97.0 Yes 8.0 27.0 15.0 10.0 60.0All 19.0 87.0 76.0 62.0 244.0
如果存在空的组合(也就是NA),你可能会希望设置一个fill_value
:
>>> tips.pivot_table('tip_pct',index=['time','sex','smoker'],columns='day',aggfunc='sum',fill_value=0)day Fri Sat Sun Thurtime sex smoker Dinner Female No 0.142857 1.923915 2.319939 0.159744 Yes 0.852716 2.457251 0.948299 0.000000 Male No 0.276010 5.188230 6.806499 0.000000 Yes 0.635410 3.754804 2.609457 0.000000Lunch Female No 0.187735 0.000000 0.000000 3.739542 Yes 0.611188 0.000000 0.000000 1.141508 Male No 0.000000 0.000000 0.000000 3.314127 Yes 0.522432 0.000000 0.000000 1.644168
pivot_table
参数说明。
- Pandas入门(中)
- pandas入门
- pandas入门
- Pandas入门
- Pandas入门
- pandas入门
- pandas入门
- Pandas 入门
- pandas入门
- Pandas入门
- 确定你掌握了pandas?进来看看——python中pandas入门基础练习
- pandas 数据分析入门
- pandas入门(持续更新)
- 10分钟入门pandas
- pandas入门-数据结构(1)
- pandas入门-数据结构(2)
- Python pandas 入门
- Pandas入门(上)
- 网络层协议(3):静态选路
- 【机器学习实战】网格搜索--贝叶斯新闻文本分类器调优
- C++ Primer_4th学习笔记(4)- typedef
- 基于spark的车辆分析
- Android6.0后权限适配,参考地址
- Pandas入门(中)
- 2n皇后问题递归法
- 4.1 程序控制
- c++ this指针
- 使用filter过滤xss攻击
- 冒泡排序
- leetcode136~Single Number
- Pandas:merge函数使用注意事项(pandas的merge函数造成大量错误的空值)
- (转)从Fintech到Techfin,未来十年有九大重要挑战