数据聚合

对于聚合,这里指的是任何能够从数组产生标量值的数据转换过程。之前的例子中已经用过一些,比如mean、count、min以及sum等。我们可能想知道在GroupBy对象上调用mean()时究竟发生了什么。许多常见的聚合运算(如下表所示)都有就地计算数据集统计信息的优化实现。然而,并不是只能使用这些方法。我们可以使用自己发明的聚合运算,还可以调用分组对象上已经定义好的任何方法。例如,quantile可以计算Series或DataFrame列的样本分位数:

In [1]: import pandas as pd

In [2]: from pandas import Series,DataFrame

In [3]: import numpy as np

In [4]: df=DataFrame({'key1':['a','a','b','b','a'],'key2':['one','two','one','t

...: wo','one'],'data1':np.random.randn(5),'data2':np.random.randn(5)})

In [5]: df

Out[5]:

key1 key2    data1       data2

0    a     one 0.280118 0.195369

1    a     two 1.166245 0.728360

2    b     one 0.701840 1.373510

3    b     two 0.485219 -1.124589

4    a    one -0.555356 0.899907

In [6]: grouped=df.groupby('key1')

In [7]: grouped

Out[7]:

D8>

In [8]: grouped['data1'].quantile(0.9)

Out[8]:

key1

a 0.989020

b 0.680178

Name: data1, dtype: float64

虽然quantile并没有明确地实现于GroupBy,但它是一个Series方法,所以这里是能用的。实际上,GroupBy会高效地对Series进行切片,然后对各片调用piece,quantile(0.9),最后将这些结果组装成最终结果。

如果要使用我们自己的聚合函数,只需将其传入aggregate或agg方法即可:

In [10]: def peak_to_peak(arr):

...: return arr.max()-arr.min()

...:

In [11]: grouped.agg(peak_to_peak)

Out[11]:

data1 data2

key1

a 1.721601 0.704539

b 0.216621 2.498099

注意,有些方法(如descirbe)也是可以用在这里的,即使严格来讲,它们并非聚合运算:

In [12]: grouped.describe()

Out[12]:

data1      ...                           data2

count   mean      std ...           50%    75%        max

key1                                     ...

a           3.0 0.297002 0.860925 ... 0.72836 0.814134 0.899907

b           2.0 0.593530 0.153174 ... 0.12446 0.748985 1.373510

[2 rows x 16 columns]

在后面关于分组级运算和转换的那一节中,我们将详细说明这到底是怎么回事。

表:经过优化的GroupBy的方法

函数名                                 说明

count                      分组中非NA值的数量

sum                         非NA值的和

mean                       非NA值的平均值

median                    非NA值的算术中位数

std、var                   无偏(分母为n-1)标准差和方差

min、max                非NA值的最小值和最大值

prod                         非NA值的积

first、last                 第一个和最后一个非NA值

1.面向列的多函数应用

我们已经看到,对Series或DataFrame列的聚合运算其实就是使用aggregate(使用自定义函数)或调用诸如mean、std之类的方法。然而,我们可能希望对不同的列使用不同的聚合函数,或一次应用多个函数。其实这事也好办,我们将通过一些示例来进行说明。

In [37]: tips=DataFrame({'name':['calvin','kobe','michale','kate','mary'],'scor

...: e':np.random.randn(5),'sex':['Male','Male','Male','Female','Female'],'

...: smoker':['True','False','False','False','False'],'age':[30,36,20,24,8]

...: ,'ss_pct':['0.8','0.9','0.75','0.7','0.6']})

In [38]: grouped=tips.groupby(['sex','smoker'])

注意,对于上表中的那些描述统计,可以将函数名以字符串的形式传入:

In [39]: grouped.agg('mean')

Out[39]:

score    age

sex       smoker

Female  False  -0.039857 16

Male      False  -0.895152  28

True   -0.264493   30

如果传入一组函数或函数名,得到的DataFrame的列就会以相应的函数命名:

In [40]: grouped.agg(['mean','std',peak_to_peak])

Out[40]:

score ...                                     age

mean          std ...                       std    peak_to_peak

sex       smoker ...

Female False -0.039857   0.547300 ...              11.313708      16

Male     False  -0.895152  0.271034 ...              11.313708      16

True -0.264493        NaN ...                          NaN          0

[3 rows x 6 columns]

我们并非一定要接受GroupBy自动给出的那些列名,特别是lambda函数,它们的名称是‘’,这样的辨识度就很低了(通过函数的name属性看看就知道了)。如果传入的是一个由(name,function)元组组成的列表,则各元组的第一个元素就会被用作DataFrame的列名(可以将这种二元元组列表看做一个有序映射):

In [41]: grouped.agg([('foo','mean'),('bar',np.std)])

Out[41]:

score                    age

foo         bar           foo       bar

sex         smoker

Female    False         -0.039857 0.547300 16 11.313708

Male        False         -0.895152 0.271034  28 11.313708

True         -0.264493        NaN   30   NaN

对于DataFrame,我们还可以定义一组应用于全部列的函数,或不同的列应用不同的函数。假设我们想要对score和age列计算三个统计信息:

In [1]: import numpy as np

In [2]: from pandas import Series,DataFrame

In [3]: tips=DataFrame({'name':['calvin','kobe','michale','kate','mary'],'score

...: ':np.random.randn(5),'sex':['Male','Male','Male','Female','Female'],'sm

...: oker':['True','False','False','False','False'],'age':[30,36,20,24,8],'s

...: s_pct':['0.8','0.9','0.75','0.7','0.6']})

In [4]: functions=['count','mean','max']

In [5]: grouped=tips.groupby(['sex','smoker'])

In [9]: result=grouped['score','age'].agg(functions)

In [10]: result

Out[10]:

score                            age

count mean    max        count mean max

sex      smoker

Female False       2 0.537534 1.274737   2       16     24

Male     False       2 0.095328 1.260650    2       28    36

True        1 -1.696528 -1.696528  1       30    30

如我们所见,结果DataFrame拥有层次化的列,这相当于分别对各列进行聚合,然后用concat将结果组装到一起(列名用作keys参数)。

In [11]: result['score']

Out[11]:

count     mean        max

sex       smoker

Female False        2      0.537534 1.274737

Male      False       2      0.095328 1.260650

True        1    -1.696528 -1.696528

跟前面一样,这里也可以传入带有自定义名称的元组列表:

In [12]: ftuples=[('kb24','mean'),('mj23',np.var)]

In [14]: grouped['score','age'].agg(ftuples)

Out[14]:

score                age

kb24       mj23     kb24     mj23

sex     smoker

Female False        0.537534  1.086937 16      128.0

Male     False        0.095328   2.715952 28     128.0

True          -1.696528 NaN         30      NaN

现在,假设我们想要对不同的列应用不同的函数。具体的办法是向agg传入一个从列名映射到函数的字典:

In [15]: grouped.agg({'score':np.max,'age':'sum'})

Out[15]:

score       age

sex       smoker

Female False    1.274737  32

Male     False     1.260650 56

True      -1.696528 30

In [16]: grouped.agg({'score':['min','max','mean','std'],'age':'sum'})

Out[16]:

score                                                        age

min          max           mean        std         sum

sex       smoker

Female False -0.199669 1.274737     0.537534    1.042563  32

Male     False -1.069995 1.260650 0.095328         1.648015 56

True   -1.696528 -1.696528 -1.696528        NaN        30

只有将多个函数应用到至少一列时,DataFrame才会拥有层次化的列。

2.以“无索引”的形式返回聚合数据

到目前为止,所有示例中的聚合数据都有由唯一的分组键组成的索引(可能还是层次化的)。由于并不总是需要如此,所以我们可以向groupby传入as_index=False以禁用该功能:

In [1]: import numpy as np

In [2]: from pandas import Series,DataFrame

In [3]: tips=DataFrame({'name':['calvin','kobe','michale','kate','mary'],'score

...: ':np.random.randn(5),'sex':['Male','Male','Male','Female','Female'],'sm

...: oker':['True','False','False','False','False'],'age':[30,36,20,24,8],'s

...: s_pct':['0.8','0.9','0.75','0.7','0.6']})

In [4]: tips.groupby(['sex','smoker'],as_index=False).mean()

Out[4]:

sex    smoker score      age

0 Female False 0.334399  16

1  Male    False 0.101183   28

2  Male    True  -0.092134  30

分组级运算和转换

聚合只不过是分组运算的其中一种而已。它是数据转换的一个特例,也就是说,它接受能够将一维数组简化为标量值的函数。在本节中,我们将会学到transform和apply方法,它们能够执行更多其他的分组运算。

假设我们想要为一个DataFrame添加一个用于存放各索引分组平均值的列。一个办法是先聚合再合并:

In [5]: df=DataFrame({'key1':['a','a','b','b','a'],'key2':['one','two','one','t

...: wo','one'],'data1':np.random.randn(5),'data2':np.random.randn(5)})

In [6]: df

Out[6]:

key1   key2   data1   data2

0      a       one 0.366926 -0.139338

1      a       two -1.224763 -0.721633

2      b       one -0.130507 -0.246849

3      b       two -1.282988 -1.271910

4      a        one 1.228818 -0.205897

In [7]: k1_means=df.groupby('key1').mean().add_prefix('mean_')

In [8]: k1_means

Out[8]:

mean_data1 mean_data2

key1

a           0.123660    -0.355622

b          -0.706748    -0.759380

In [11]: pd.merge(df,k1_means,left_on='key1',right_index=True)

Out[11]:

key1 key2     data1       data2       mean_data1   mean_data2

0   a     one   0.366926 -0.139338    0.123660        -0.355622

1   a     two   -1.224763 -0.721633   0.123660        -0.355622

4   a     one   1.228818   -0.205897   0.123660        -0.355622

2   b     one   -0.130507 -0.246849  -0.706748        -0.759380

3   b      two  -1.282988 -1.271910  -0.706748         -0.759380

虽然这样也行,但是不太灵活。我们可以将该过程看做利用np.mean函数对两个数据列进行转换。再以本章前面用过的那个people DataFrame为例,这次我们在GroupBy上使用transform方法:

In [12]: people=DataFrame(np.random.randn(5,5),columns=['a','b','c','d','e'],in

...: dex=['Kobe','Michael','Kevin','James','Wade'])

In [13]: key=['one','two','one','two','one']

In [15]: people.groupby(key).mean()

Out[15]:

a             b               c               d             e

one -0.166599 -0.230311 1.063868 -0.296599 0.073681

two -0.809523 -0.448779 -0.207218 0.518750 -0.663468

In [16]: people.groupby(key).transform(np.mean)

Out[16]:

a           b               c             d                 e

Kobe -0.166599 -0.230311 1.063868 -0.296599 0.073681

Michael -0.809523 -0.448779 -0.207218 0.518750 -0.663468

Kevin -0.166599 -0.230311 1.063868 -0.296599 0.073681

James -0.809523 -0.448779 -0.207218 0.518750 -0.663468

Wade -0.166599 -0.230311 1.063868 -0.296599 0.073681

不难看出,transform会将一个函数应用到各个分组,然后将结果放置到适当的位置上。如果各分组产生的是一个标量值,则该值就会被广播出去。现在,假设我们希望从各组中减去平均值。为此,我们先创建一个距平化函数(demeaning function),然后将其传给transform:

In [17]: def deman(arr):

...: return arr-arr.mean()

...:

In [18]: demeaned=people.groupby(key).transform(demean)

In [19]: demeaned

Out[19]:

a               b               c                 d             e

Kobe 0.026677 -0.544615 -0.087396 0.894772 0.465351

Michael -0.943158 -0.171742 -0.323378 0.765667 -0.386289

Kevin -0.542661 -0.411980 0.523509 -1.393987 -0.720833

James 0.943158 0.171742 0.323378 -0.765667 0.386289

Wade 0.515983 0.956595 -0.436113 0.499215 0.255483

我们可以检查一下demeaned现在的分组平均值是否为0:

In [20]: demeaned.groupby(key).mean()

Out[20]:

a                              b ...    d                           e

one 0.000000e+00 7.401487e-17 ... -3.700743e-17 -1.850372e-17

two -5.551115e-17 -2.775558e-17 ... 0.000000e+00 0.000000e+00

[2 rows x 5 columns]

在下一节中我们将会看到,分组距平化操作还可以通过apply实现。

apply:一般性的“拆分-应用-合并”

跟aggregate一样,transform也是一个有着严格条件的特殊函数:传入的函数只能产生两种结果,要么产生一个可以广播的标量值(如np.mean),要么产生一个相同大小的结果数组。最一般化的GroupBy方法是apply,本节剩余部分重点学习它,apply会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试将各片组合到一起。

回到之前那个tips数据集,假设我们想要分组选出最高的5个ss_pct值,首先编写一个指定列找出最大值,并把这个值所在的行选取出来的函数:

In [11]: def top(df,n=5,column='ss_pct'):

...: return df.sort_values(by=column)[-n:]

In [13]: top(tips,n=4)

Out[13]:

name    score            sex   smoker   age ss_pct

3 kate    1.818554      Female False     24    0.7

2 michale  0.063468  Male      False    20   0.75

0 calvin   0.300196     Male     True       30   0.8

1 kobe   -0.512263     Male      False     36   0.9

现在,如果对smoker分组并用该函数调用apply,就会得到:

In [14]: tips.groupby('smoker').apply(top)

Out[14]:

name score sex smoker age ss_pct

smoker

False     4 mary -0.237145 Female False 8 0.6

3 kate 1.818554 Female False 24 0.7

2 michale 0.063468 Male False 20 0.75

1 kobe -0.512263 Male False 36 0.9

True       0 calvin 0.300196 Male True 30 0.8

这里发生了什么?top函数在DataFrame的各个片段上调用,然后结果由pandas.concat组装到一起,并以分组名称进行了标记。于是,最终结果就有了一个层次化索引,其内层索引值来自原DataFrame。

如果传给apply的函数能够接受其他参数或关键字,则可以将这些内容放在函数名后面一并传入:

In [15]: tips.groupby(['smoker','age']).apply(top,n=1,column='score')

Out[15]:

name score sex smoker age ss_pct

smoker  age

False      8   4    mary -0.237145 Female False 8 0.6

20   2 michale 0.063468 Male False 20 0.75

24 3 kate 1.818554 Female False 24 0.7

36 1 kobe -0.512263 Male False 36 0.9

True       30 0 calvin 0.300196 Male True 30 0.8

我们之前在GroupBy对象上调用过describe:

In [21]: result=tips.groupby('smoker')['score'].describe()

In [22]: result

Out[22]:

count mean std ... 50% 75% max

smoker ...

False        4.0 0.283153 1.050256 ... -0.086839 0.502239 1.818554

True         1.0 0.300196 NaN ... 0.300196 0.300196 0.300196

[2 rows x 8 columns]

In [23]: result.unstack('smoker')

Out[23]:

smoker

count False 4.000000

True 1.000000

mean False 0.283153

True 0.300196

std      False 1.050256

True NaN

min    False -0.512263

True 0.300196

25%   False -0.305925

True 0.300196

50%   False -0.086839

True 0.300196

75%   False 0.502239

True 0.300196

max   False 1.818554

True 0.300196

dtype: float64

在GroupBy中,当我们调用诸如describe之类的方法时,实际上只是应用了下面两条代码的快捷方式而已:

f=lambda x:x.describe()

grouped.apply(f)

禁止分组键

从上面的例子中可以看到,分组键会跟原始对象的索引共同构成结果对象中的层次化索引。将group_keys=False传入groupby即可禁止该效果:

In [24]: tips.groupby('smoker',group_keys=False).apply(top)

Out[24]:

name score sex smoker age ss_pct

4 mary -0.237145 Female False 8 0.6

3 kate 1.818554 Female False 24 0.7

2 michale 0.063468 Male False 20 0.75

1 kobe -0.512263 Male False 36 0.9

0 calvin 0.300196 Male True 30 0.8

分位数和桶分析

pandas有一些能根据指定面元或样本分位数将数据拆分成多块的工具(比如cut和qcut)。将这些函数跟groupby结合起来,就能非常轻松地实现对数据集的桶(bucket)或分位数(quantile)分析了。以下面这个简单的随机数据集为例,我们利用cut将其装入长度相等的桶中:

In [26]: frame=DataFrame({'data1':np.random.randn(1000),'data2':np.random.randn

...: (1000)})

In [27]: factor=pd.cut(frame.data1,4)

In [29]: factor[:10]

Out[29]:

0 (-1.661, -0.0197]

1 (-1.661, -0.0197]

2 (-1.661, -0.0197]

3 (-0.0197, 1.622]

4 (-1.661, -0.0197]

5 (-3.309, -1.661]

6 (-1.661, -0.0197]

7 (-1.661, -0.0197]

8 (-0.0197, 1.622]

9 (-1.661, -0.0197]

Name: data1, dtype: category

Categories (4, interval[float64]): [(-3.309, -1.661] < (-1.661, -0.0197] < (-0.0

197, 1.622] <

(1.622, 3.263]]

由cut返回的Factor对象可直接用于groupby。因此,我们可以像下面这样对data2做一些统计计算:

In [30]: def get_stats(group):

...: return {'min':group.min(),'max':group.max(),'count':group.count(),

...: 'mean':group.mean()}

...:

In [31]: grouped=frame.data2.groupby(factor)

In [32]: grouped.apply(get_stats).unstack()

Out[32]:

count      max         mean       min

data1

(-3.309, -1.661] 53.0 1.734561 0.103850 -1.622861

(-1.661, -0.0197] 427.0 2.702454 0.006620 -3.397039

(-0.0197, 1.622] 466.0 2.990147 -0.013584 -3.488040

(1.622, 3.263] 54.0 2.063408 0.133740 -2.509789

这些都是长度相等的桶。要根据样本分位数得到大小相等的桶,使用qcut即可。传入labels=False即可只获取分位数的编号。

#返回分位数编号

In [33]: grouping=pd.qcut(frame.data1,10,labels=False)

In [34]: grouped=frame.data2.groupby(grouping)

In [35]: grouped.apply(get_stats).unstack()

Out[35]:

count max mean min

data1

0        100.0 2.429463 0.069957 -2.255656

1        100.0 2.508677 -0.019191 -2.715692

2        100.0 2.037851 -0.077528 -2.353218

3        100.0 2.702454 0.113666 -3.397039

4        100.0 2.003158 -0.065770 -3.310005

5        100.0 2.153212 0.077802 -1.762410

6        100.0 2.325933 0.092688 -2.946146

7        100.0 2.354909 -0.091635 -3.488040

8        100.0 2.116821 -0.100465 -2.460177

9        100.0 2.990147 0.092702 -2.509789

注:

qcut与cut的主要区别:

qcut:传入参数,要将数据分成多少组,即组的个数,具体的组距是由代码计算

cut:传入参数,是分组依据。

示例:用特定于分组的值填充缺失值

对于缺失数据的清理工作,有时我们会用dropna将其滤除,而有时则可能会希望用一个固定值或有数据集本身所衍生出来的值去填充NA值。这是就得使用fillna这个工具了。在下面这个例子中,我们用平均值去填充NA值:

In [1]: import pandas as pd

In [2]: import numpy as np

In [3]: from pandas import Series,DataFrame

In [4]: s=Series(np.random.randn(6))

In [5]: s[::2]=np.nan

In [6]: s

Out[6]:

0 NaN

1 1.134955

2 NaN

3 -0.665837

4 NaN

5 -1.250660

dtype: float64

In [7]: s.fillna(s.mean())

Out[7]:

0 -0.260514

1 1.134955

2 -0.260514

3 -0.665837

4 -0.260514

5 -1.250660

dtype: float64

假设我们需要对不同的分组填充不同的值。只需将数据分组,并使用apply和一个能够对各数据块调用fillna的函数即可。下面是一些有关美国几个州的示例数据,这些州又被分为东部和西部:

In [11]: states=['Ohio','New York','Vermont','Florida','Oregon','Nevada','Calif

...: ornia','Idaho']

In [12]: group_key=['East']*4+['West']*4

In [13]: group_key

Out[13]: ['East', 'East', 'East', 'East', 'West', 'West', 'West', 'West']

In [14]: data=Series(np.random.randn(8),index=states)

In [15]: data[['Vermont','Nevada','Idaho']]=np.nan

In [16]: data

Out[16]:

Ohio 1.412328

New York 0.720072

Vermont NaN

Florida -0.691600

Oregon 1.282029

Nevada NaN

California 0.766514

Idaho NaN

dtype: float64

In [17]: data.groupby(group_key).mean()

Out[17]:

East 0.480267

West 1.024271

dtype: float64

我们可以用分组平均值去填充NA值,如下所示:

In [18]: fill_mean=lambda g:g.fillna(g.mean())

In [19]: data.groupby(group_key).apply(fill_mean)

Out[19]:

Ohio 1.412328

New York 0.720072

Vermont 0.480267

Florida -0.691600

Oregon 1.282029

Nevada 1.024271

California 0.766514

Idaho 1.024271

dtype: float64

此外,也可以在代码中预定义各组的填充值,由于分组具有一个name属性,所以我们可以拿来用一下:

In [20]: fill_values={'East':0.5,'West':-1}

In [21]: fill_func=lambda g:g.fillna(fill_values[g.name])

In [22]: data.groupby(group_key).apply(fill_func)

Out[22]:

Ohio 1.412328

New York 0.720072

Vermont 0.500000

Florida -0.691600

Oregon 1.282029

Nevada -1.000000

California 0.766514

Idaho -1.000000

dtype: float64

示例:分组加权平均数

根据groupby的“拆分-应用-合并”范式,DataFrame的列与列之间或两个Series之间的运算(比如分组加权平均)成为一种标准作业。以下面这个数据集为例,它含有分组键、值以及一些权重值:

In [25]: df=DataFrame({'category':['a','a','a','a','b','b','b','b'],'data':np.r

...: andom.randn(8),'weights':np.random.rand(8)})

In [26]: df

Out[26]:

category    data      weights

0    a         1.840410 0.624775

1    a        -0.612739 0.207124

2    a        -0.051466 0.085688

3    a         0.786270 0.383966

4    b        -1.517201 0.063853

5    b        0.692989 0.751790

6    b        0.492041 0.794091

7    b       -0.137448 0.719158

然后可以利用category计算分组加权平均数:

In [27]: grouped=df.groupby('category')

In [28]: get_wavg=lambda g:np.average(g['data'],weights=g['weights'])

In [29]: grouped.apply(get_wavg)

Out[29]:

category

a 1.014496

b 0.307435

dtype: float64

透视表和交叉表

透视表(pivot table)是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个矩形区域中,在Python和pandas中,可以通过本章所学习的groupby功能以及(能够利用层次化索引的)重塑运算制作透视表。DataFrame有一个pivot_table方法,此外还有一个顶级的pandas.pivot_table函数。除能为groupby提供便利之外,pivot_table还可以添加分项小计(也叫做margins)。

回到小费数据集,假设我们想要根据sex和smoker计算分组平均数(pivot_table的默认聚合类型),并将sex和smoker放到行上:

In [1]: import pandas as pd

In [2]: import numpy as np

In [3]: from pandas import Series,DataFrame

In [4]: tips=DataFrame({'name':['calvin','kobe','michale','kate','mary'],'score

...: ':np.random.randn(5),'sex':['Male','Male','Male','Female','Female'],'sm

...: oker':['True','False','False','False','False'],'age':[30,36,20,24,8],'s

...: s_pct':['0.8','0.9','0.75','0.7','0.6']})

In [6]: tips.pivot_table(index=['sex','smoker'])

Out[6]:

age     score

sex       smoker

Female False      16   -0.600976

Male     False      28    0.318842

True        30   -1.030795

这对groupby来说也是很简单的事情。现在,假设我们只想聚合score,而且想根据name进行分组。我将smoker放到列上,把name放到行上:

In [8]: tips.pivot_table(['score'],index=['sex','name'],columns='smoker')

Out[8]:

score

smoker                                False       True

sex           name

Female      kate              -2.037238     NaN

mary             0.835287      NaN

Male         calvin             NaN         -1.030795

kobe            -0.360520       NaN

michale         0.998203       NaN

还可以对这个表作进一步的处理,传入margins=True添加分项小计,这将会添加标签为ALL的行和列,其值对应于单个等级中所有数据的分组统计。在下面这个例子中,ALL值为平均数:不单独考虑烟民与非烟民(ALL列),不单独考虑行分组两个级别中的任何单项(ALL行)。

In [9]: tips.pivot_table(['score'],index=['sex','name'],columns='smoker',margin

...: s=True)

Out[9]:

score

smoker                                      False          True           All

sex               name

Female         kate                     -2.037238   NaN      -2.037238

mary                     0.835287   NaN       0.835287

Male             calvin                      NaN    -1.030795   -1.030795

kobe                      -0.360520 NaN        -0.360520

michale                   0.998203 NaN          0.998203

All                                              -0.141067 -1.030795 -0.319013

要使用其他的聚合函数,将其传给aggfunc即可。例如,使用count或len可以得到有关分组大小的交叉表:

In [10]: tips.pivot_table(['score'],index=['sex','name'],columns='smoker',aggfu

...: nc=len,margins=True)

Out[10]:

score

smoker              False     True    All

sex          name

Female     kate  1.0         NaN    1.0

mary 1.0         NaN    1.0

Male         calvin NaN      1.0      1.0

kobe 1.0         NaN     1.0

michale 1.0      NaN     1.0

All                         4.0      1.0        5.0

如果存在空的组合(也就是NA),我们可能会希望设置一个fill_value:

In [11]: tips.pivot_table(['score'],index=['sex','name'],columns='smoker',aggfu

...: nc=sum,fill_value=0)

Out[11]:

score

smoker             False        True

sex        name

Female  kate  -2.037238  0.000000

mary 0.835287 0.000000

Male     calvin 0.000000 -1.030795

kobe -0.360520  0.000000

michale 0.998203 0.000000

pivot_table的参数说明请参见下表。

pivot_table的参数

参数名                      说明

values         待聚合的列的名称。默认聚合所有数值列

index           用于分组的列名或其他分组键,出现在结果透视表的行

columns      用于分组的列名或其他分组键,出现在结果透视表的列

aggfunc       聚合函数或函数列表,默认为‘mean’。可以是任何对groupby有效的函数

fill_value     用于替换结果表中的缺失值

margins        添加行/列小计和总计,默认为False

交叉表:crosstab

交叉表(cross-tabulation,简称crosstab)是一种用于计算分组频率的特殊透视表。下面这个范例数据很典型:

In [16]: data=DataFrame({'Sample':range(1,11),'Gender':['Female','Male','Female

...: ','Male','Male','Male','Female','Female','Male','Female'],'Handedness'

...: :['Right-handed','Left-handed','Right-handed','Right-handed','Left-han

...: ded','Right-handed','Right-handed','Left-handed','Right-handed','Right

...: -handed']})

In [17]: data

Out[17]:

Sample    Gender     Handedness

0        1            Female    Right-handed

1        2            Male        Left-handed

2        3           Female    Right-handed

3        4           Male        Right-handed

4        5          Male         Left-handed

5        6          Male         Right-handed

6        7          Female     Right-handed

7        8          Female     Left-handed

8        9          Male         Right-handed

9       10         Female     Right-handed

假设我们想要根据性别和用手习惯对这段数据进行统计汇总。虽然可以用pivot_table实现该功能,但是pandas.crosstab函数会更方便:

In [18]: pd.crosstab(data.Gender,data.Handedness,margins=True)

Out[18]:

Handedness     Left-handed      Right-handed       All

Gender

Female                     1                       4                    5

Male                         2                       3                    5

All                             3                       7                   10

crosstab的前两个参数可以是数组、Series或数组列表。再比如对小费数据集:

In [19]: pd.crosstab([tips.name,tips.sex],tips.smoker,margins=True)

Out[19]:

smoker              False    True     All

name        sex

calvin      Male      0          1         1

kate        Female  1           0         1

kobe       Male       1          0         1

mary       Female   1         0         1

michale   Male      1         0         1

All                         4         1          5

python中数据分组计算_python3数据聚合与分组运算(二)相关推荐

  1. python数据科学包第三天(索引、分组计算、数据聚合、分组运算和转换、载入数据、日期范围、数据可视化)

    索引 行索引 列索引 索引的分类 重复索引的处理 s = pd.Series(np.random.rand(5), index=list('abcde')) s a 0.566924 b 0.6034 ...

  2. 在python中使用json格式存储数据

    在python中使用json格式存储数据 代码如下: import jsonlist1 = [{'A': [1, 2, 3, 4, 5, 6], 'B': [3, 4, 5, 6, 7]},{'C': ...

  3. python应用中调用spark_在python中使用pyspark读写Hive数据操作

    1.读Hive表数据 pyspark读取hive数据非常简单,因为它有专门的接口来读取,完全不需要像hbase那样,需要做很多配置,pyspark提供的操作hive的接口,使得程序可以直接使用SQL语 ...

  4. Python中通过索引名称提取数据loc()函数Python中通过行和列下标提取数据iloc()函数

    [小白从小学Python.C.Java] [Python全国计算机等级考试] [Python数据分析考试必会题] ● 标题与摘要 Python中通过索引名称提取数据 loc()函数 Python中通过 ...

  5. python读取网络端口数据_在Python中从SNMP端口获取数据

    我专门尝试使用PySNMP库从python中的SNMP端口读取数据.我有兴趣仅通过此库获取数据.这是因为我正在从NetSNMP迁移到PySNMP. 这是我为NetSNMP编写的工作代码,它实际上为我提 ...

  6. Python中的乘方计算

    [小白从小学Python.C.Java] [Python-计算机等级考试二级] [Python-数据分析] Python中的乘方计算 power()函数 选择题 以下python代码输出什么? imp ...

  7. R语言根据日历周期处理时间序列数据:计算时间序列数据的日对数逐次差分值、使用apply.monthly函数逐月计算日对数逐次差分值的标准差、根据调整的天数来计算年化波动率

    R语言根据日历周期处理时间序列数据:计算时间序列数据的日对数逐次差分值.使用apply.monthly函数逐月计算日对数逐次差分值的标准差.根据调整的天数来计算年化波动率 目录

  8. python中的复数计算

    python中的复数计算 在python中,有时需要对复数进行计算,比如-1开方运算,普通的math模块难以实现计算结果,此时可以使用cmath模块进行. 例如 import cmath cmath. ...

  9. python中的取余运算符是_python取余运算

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! python中对负数求余的计算方法和求幂运算注意点python中对负数求余的计算 ...

最新文章

  1. html5 graphics with svg css3,HTML5 GRAPHICS WITH SVG AND CSS3
  2. 免费云服务器无限流量,云服务器弄无限流量
  3. c语言绘制路面图形代码,道路纵断面绘图程序的开发.pdf
  4. ASP.NET MVC下的四种验证编程方式
  5. 我的AWS开发人员考试未通过。 现在怎么办?
  6. apt-get install php5-redis,Ubuntu14-04安装redis和php5-redis扩展
  7. linux下的C语言开发(线程等待)
  8. Bitmap缩放(二)
  9. R-CNN学习笔记3:Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition(SPP-net)
  10. 前端实现用户自定义建表_Excel、SQL、Python分别实现行列转换
  11. NOIP2013 试做总结
  12. 「 Ansys 」仿真调试错误汇总
  13. 明翰英语教学系列之音标篇V0.2(持续更新)
  14. CMake工程从入门到进阶完整版,可以完成简单的工程创建(完结)
  15. 地图,GPS位置地图坐标系:WGS-84(GPS)、GCJ-02(Google地图)、BD-09(百度地图),OpenGIS
  16. 【C++】C++ 内存分配(new,operator new)详解
  17. JSON简介与解析方法(超级详细)
  18. python大众点评霸王餐_划重点:如何报名大众点评霸王餐?怎么做才能中奖?
  19. C语言下划线开头的函数
  20. CentOS网络配置文件中UUID参数释疑

热门文章

  1. 在Linux系统中安装和使用VNC
  2. 动态加载js如何保证顺序执行?
  3. java实现上传图片代码_Java图片上传实现代码
  4. JavaWeb项目表格页面
  5. mui开发app之多图压缩与上传(仿qq空间说说发表)
  6. 【ES6】数组方法--查找元素find()、findIndex()
  7. spark3.3.1通过hbase-connectors连接CDH6.3.2自带hbase
  8. 硬盘显示损坏打不开修复方法
  9. vim 打开文件 gb2312 (Linux下为cp936)
  10. JToken,JObject取值