天天看点

数据集合与分组操作:数据聚合,逐列及多函数应用,返回不含行索引的聚合数据, 数据透视表与交叉表(crosstab)

2.1 数据聚合

聚合是指所有根据数组产生标量值的数据转换过程。就像mean count min sum等,

函数 描述
count 分组中的非NA 值数量
sum 非NA值的累计和
mean 非NA 值的均值
median 非NA 值的算术中位数
std,var 无偏的(n-1 分母)标准差和方差
min max 非NA 值的最大值最小值
prod 非NA 值 的乘积
first,last 非NA 值 的第一个和最后一个值。

你可以自定义聚合,并在调用已经在分组对象上定义好的方法。例如,quantile 可用计算series 或 dataframe 列的样本分位数

分位数(Quantile),亦称分位点,是指将一个随机变量的概率分布范围分为几个等份的数值点,常用的有中位数(即二分位数)、四分位数、百分位数等。

尽管quantile并不是显式的为GroupBy 对象的函数,但是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)})
df
	key1	key2	data1	data2
0	a	one	-0.603123	1.810936
1	a	two	0.462399	0.172707
2	b	one	0.998915	-1.275746
3	b	two	2.649937	-0.188244
4	a	one	-1.637420	0.921537

grouped=df.groupby('key1')
grouped['data1'].quantile(0.9)
key1
a    0.249294
b    2.484835
Name: data1, dtype: float64


def peak_to_peak(arr):             # 用自己的聚合函数,要把函数传递给aggregate or agg 方法。
    return arr.max()-arr.min()
grouped.agg(peak_to_peak)

data1	data2
key1		
a	2.099819	1.638229
b	1.651022	1.087502


grouped.describe()      # 一些方法比如这个也是有效的,尽管不是聚合函数
	data1	data2
count	mean	std	min	25%	50%	75%	max	count	mean	std	min	25%	50%	75%	max
key1																
a	3.0	-0.592715	1.049948	-1.637420	-1.120271	-0.603123	-0.070362	0.462399	3.0	0.968393	0.820119	0.172707	0.547122	0.921537	1.366236	1.810936
b	2.0	1.824426	1.167449	0.998915	1.411671	1.824426	2.237182	2.649937	2.0	-0.731995	0.768980	-1.275746	-1.003870	-0.731995	-0.460120	-0.188244
           

2.1.1 逐列及多函数应用

tips=pd.read_csv('tips.csv',sep='\t')
tips['tip_pct']=tips['tip']/tips['total_bill']        # 增加一个小费比例
tips.head()
	total_bill	tip	smoker	day	time	size	tip_pct
0	16.99	1.01	No	Sun	Dinner	2	0.059447
1	10.34	1.66	No	Sun	Dinner	3	0.160542
2	21.01	3.50	No	Sun	Dinner	3	0.166587
3	23.68	3.31	No	Sun	Dinner	2	0.139780
4	24.59	3.61	No	Sun	Dinner	4	0.146808

grouped=tips.groupby(['day','smoker'])
grouped_pct=grouped['tip_pct']     # 将函数名以字符串的形式传入
grouped_pct.agg('mean')
day   smoker
Fri   No        0.151650
      Yes       0.174783
Sat   No        0.158048
      Yes       0.147906
Sun   No        0.160113
      Yes       0.187250
Thur  No        0.160298
      Yes       0.163863
Name: tip_pct, dtype: float64

grouped_pct.agg(['mean','std',peak_to_peak])      # 传递的是函数或函数名的列表,获得列名是这些函数名的dataframe 
		mean	std	peak_to_peak
day	smoker			
Fri	No	0.151650	0.028123	0.067349
    Yes	0.174783	0.051293	0.159925
Sat	No	0.158048	0.039767	0.235193
    Yes	0.147906	0.061375	0.290095
Sun	No	0.160113	0.042347	0.193226
    Yes	0.187250	0.154134	0.644685
Thur	No	0.160298	0.038774	0.193350
    Yes	0.163863	0.039389	0.151240

grouped_pct.agg([('foo','mean'),('bar',np.std)])    # (name.function) 第一个元素作为dataframe的列名
             foo	bar
day	smoker		
Fri	No	0.151650	0.028123
    Yes	0.174783	0.051293
Sat	No	0.158048	0.039767
    Yes	0.147906	0.061375
Sun	No	0.160113	0.042347
    Yes	0.187250	0.154134
Thur	No	0.160298	0.038774
    Yes	0.163863	0.039389


functions=['count','mean','max']                             # 应用到所有列上的函数列表,
result=grouped['tip_pct','total_bill'].agg(functions)
result
     	tip_pct	                     total_bill
           count	mean	max	               count	mean	max
day	smoker						
Fri	No	4	0.151650	0.187735	4	18.420000	22.75
    Yes	15	0.174783	0.263480	15	16.813333	40.17
Sat	No	45	0.158048	0.291990	45	19.661778	48.33
    Yes	42	0.147906	0.325733	42	21.276667	50.81
Sun	No	57	0.160113	0.252672	57	20.506667	48.17
    Yes	19	0.187250	0.710345	19	24.120000	45.35
Thur	No	45	0.160298	0.266312	45	17.113111	41.19
    Yes	17	0.163863	0.241255	17	19.190588	43.11

result['tip_pct']                          # 产生的dataframe 有分层列,这与聚合每一列在通过concat将结果拼接在一起是相同的
		count	mean	max
day	smoker			
Fri	No	4	0.151650	0.187735
    Yes	15	0.174783	0.263480
Sat	No	45	0.158048	0.291990
    Yes	42	0.147906	0.325733
Sun	No	57	0.160113	0.252672
     Yes	19	0.187250	0.710345
Thur	No	45	0.160298	0.266312
       Yes	17	0.163863	0.241255


ftuples=[('Durchschnitt','mean'),('Abweichung',np.var)]         # 就是传递有自定义名称的元组列表。
grouped['tip_pct','total_bill'].agg(ftuples)
	tip_pct	total_bill
Durchschnitt	Abweichung	Durchschnitt	Abweichung
day	smoker				
Fri	No	0.151650	0.000791	18.420000	25.596333
    Yes	0.174783	0.002631	16.813333	82.562438
Sat	No	0.158048	0.001581	19.661778	79.908965
    Yes	0.147906	0.003767	21.276667	101.387535
Sun	No	0.160113	0.001793	20.506667	66.099980
    Yes	0.187250	0.023757	24.120000	109.046044
Thur	No	0.160298	0.001503	17.113111	59.625081
    Yes	0.163863	0.001551	19.190588	69.808518


grouped.agg({'tip_pct':['min','max','mean','std'],              # 将不同的函数应用到一个或多个列上的字典就像
            'size':'sum'})                                      # ,传递有列名与函数对应关系

               	tip_pct	                          size
             min	max	        mean	    std	       sum
day	smoker					
Fri	No	0.120385	0.187735	0.151650	0.028123	9
    Yes	0.103555	0.263480	0.174783	0.051293	31
Sat	No	0.056797	0.291990	0.158048	0.039767	115
    Yes	0.035638	0.325733	0.147906	0.061375	104
Sun	No	0.059447	0.252672	0.160113	0.042347	167
    Yes	0.065660	0.710345	0.187250	0.154134	49
Thur	No	0.072961	0.266312	0.160298	0.038774	112
    Yes	0.090014	0.241255	0.163863	0.039389	40
           

2.1.2 返回不含行索引的聚合数据

前面的例子中,聚合数据返回时都是带有索引的,有时索引是分层的,由唯一的分组件联合形成。因为不是所有的情况都要索引,通过传递 ax_index = False 来禁用分组键作为索引的行为。

tips.groupby(['day','smoker'],as_index=False).mean() # == tips.groupby(['day','smoker']).mean().reset_index()
     day	smoker	total_bill	tip	size	tip_pct
0	Fri	No	18.420000	2.812500	2.250000	0.151650
1	Fri	Yes	16.813333	2.714000	2.066667	0.174783
2	Sat	No	19.661778	3.102889	2.555556	0.158048
3	Sat	Yes	21.276667	2.875476	2.476190	0.147906
4	Sun	No	20.506667	3.167895	2.929825	0.160113
5	Sun	Yes	24.120000	3.516842	2.578947	0.187250
6	Thur	No	17.113111	2.673778	2.488889	0.160298
7	Thur	Yes	19.190588	3.030000	2.352941	0.163863
           

3.1 数据透视表与交叉表

数据透视表是电子表格程序和其他数据分析软件中常见的数据汇总工具。更加一个或多个键聚合一张表的数据,将数据在矩形格式中排列,其中一些分组件是沿着行的,另一些沿着列。

在pandas中透视表透过groupby 工具以及使用分层索引的重塑操作实现。dataframe 有一个pivot_table 方法,并且还有一个顶层的pandas.pivot_table 函数,为groupby提供了一个方便的接口,还可以添加部分总计,也叫边距。。。啥啥啥啥啥

tips.pivot_table(index=['day','smoker'])  # 计算一张在行方向上按day and smoker排列的分组平均值
size	tip	tip_pct               	total_bill    # tips.groupby(['day','smoker']).mean() 跟这个一样欸
day	smoker				
Fri	No	2.250000	2.812500	0.151650	18.420000
    Yes	2.066667	2.714000	0.174783	16.813333
Sat	No	2.555556	3.102889	0.158048	19.661778
    Yes	2.476190	2.875476	0.147906	21.276667
Sun	No	2.929825	3.167895	0.160113	20.506667
    Yes	2.578947	3.516842	0.187250	24.120000
Thur	No	2.488889	2.673778	0.160298	17.113111
    Yes	2.352941	3.030000	0.163863	19.190588


tips.pivot_table(['tip_pct','size'],index=['time','day'],columns='smoker',margins=True) 
																		 # z在tip_pct,size 列聚合,根据time分组
	                            size	t                    ip_pct       # 将 smoker放到表的列,将day 放到表的行。
smoker	          No	Yes	All	                         No	Yes	All     # 添加margins=True, 来扩充这个表来包含部分统计
time	day						
Dinner	Fri	2.000000	2.222222	2.166667	0.139622	0.165347	0.158916   # 这里All的值是均值,就是不考虑分组了
        Sat	2.555556	2.476190	2.517241	0.158048	0.147906	0.153152
          Sun	2.929825	2.578947	2.842105	0.160113	0.187250	0.166897
        Thur	2.000000	NaN	2.000000	0.159744	NaN	0.159744
Lunch	Fri	3.000000	1.833333	2.000000	0.187735	0.188937	0.188765
        Thur	2.500000	2.352941	2.459016	0.160311	0.163863	0.161301
All		2.668874	2.408602	2.569672	0.159328	0.163196	0.160803
 
tips.pivot_table('tip_pct',index=['time','smoker'],columns='day',aggfunc=len,margins=True,fill_value=0)
         day	Fri	Sat	Sun	Thur	All        # 使用不同聚合函数时,将函数传递给aggfunc就行,传递len获得一张分组大小的交叉表
time	smoker					
Dinner	No	3	45	57	1	106.0                 
        Yes	9	42	19	0	70.0
Lunch	No	1	0	0	44	45.0
        Yes	6	0	0	17	23.0
All		19	87	76	62	244.0
           
pivot_table参数 描述
values 需要聚合的列,默认聚合所有数值型列
index 在结果透视表的行上进行分组的列名或其他分组键
columns 在结果透视表的列上进行分组的列名或其他分组键。
aggfunc 聚合函数或函数列表,默认时mean 可以是groupby的任意有效函数
fill_value 替换缺失值
dropna 如果为True,不含所有条目都为NA的列
margins 添加ALL,默认False

3.2 交叉表:crosstab

这个是数据透视表的一个特殊情况,计算的是分组中的频率,

from io import StringIO
data = """\
Sample  Nationality  Handedness
1   USA  Right-handed
2   Japan    Left-handed
3   USA  Right-handed
4   Japan    Right-handed
5   Japan    Left-handed
6   Japan    Right-handed
7   USA  Right-handed
8   USA  Left-handed
9   Japan    Right-handed
10  USA  Right-handed"""
data = pd.read_table(StringIO(data), sep='\s+')
data
Sample	Nationality	Handedness
0	1	USA	Right-handed
1	2	Japan	Left-handed
2	3	USA	Right-handed
3	4	Japan	Right-handed
4	5	Japan	Left-handed
5	6	Japan	Right-handed
6	7	USA	Right-handed
7	8	USA	Left-handed
8	9	Japan	Right-handed
9	10	USA	Right-handed

pd.crosstab(data.Nationality, data.Handedness, margins=True)   # 虽然可以使用pivot_table,但crosstab更加方便
Handedness	Left-handed	Right-handed	All
Nationality			
Japan	2	3	5
USA	    1	4	5
All	    3	7	10
                                         # corsstab 的前两个参数可以是数值,series或数组的列表
pd.crosstab([tips.time, tips.day], tips.smoker, margins=True)  # 额,,又是这个
smoker     	No	Yes	All
time	day			
Dinner	Fri	3	9	12
        Sat	45	42	87
        Sun	57	19	76
        Thur	1	0	1
Lunch	Fri	 1	6	7
        Thur	44	17	61
All		151	93	244

           

继续阅读