- 注:本文演示基于Python 3.9.7和Pandas 1.3.4
(2022.05.12 Thur)
Python的Pandas工具包提供了類似于SQL中g(shù)roup的操作指令groupby
谬擦,但功能更為強(qiáng)大。本文介紹基于groupby
的數(shù)據(jù)分組和聚合操作朽缎。此外惨远,還介紹了pandas.transform
的使用。
GroupBy機(jī)制
GroupBy機(jī)制可以簡單的描述為split-appy-combine過程话肖。pandas對象中的數(shù)據(jù)北秽,e.g., Series/DataFrame or others,根據(jù)用戶提供的一個或多個key最筒,被切分(split)成多個group贺氓。切分操作按照特定軸進(jìn)行,即行(axis=0)或列(axis=1)床蜘。之后應(yīng)用(apply)某個函數(shù)在切分的group上辙培,生成新值諸如此類。最后將處理后的數(shù)據(jù)組合(combine)成新的數(shù)據(jù)對象邢锯。
用于分組的鍵(key)可以有多種形式:
- 與分組的軸有相同數(shù)據(jù)長度的list/array
- DataFrame中的列名
- 提供了被分組的值和分組名的字典/Series
- a function to be invoked on the axis index or the individual labels in the index
案例如下
import pandas as pd
import numpy as np
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
>> df
key1 key2 data1 data2
0 a one 0.162224 -0.497908
1 a two 0.497138 0.195447
2 b one 1.158331 -0.580097
3 b two 1.639932 -0.997953
4 a one -0.303192 0.525740
下面根據(jù)key1
列的值對data1
列做分組扬蕊,注意groupby接收的參數(shù)是df['key1'],而非'key1'丹擎,事實(shí)上如果被分組的對象是一個Series尾抑,則groupby的參數(shù)是一個Series,如果被分組對象是一個DataFrame蒂培,則groupby的參數(shù)可以是一個Series再愈,也可以是該Series的名字,參考下面示例护戳。返回結(jié)果grouped
的類型是SeriesGroupBy對象践磅。
>> grouped = df['data1'].groupby(df['key1'])
>> grouped
<pandas.core.groupby.generic.SeriesGroupBy object at 0x117a01370>
該分組結(jié)果計算sum
>> grouped.sum()
key1
a 0.356170
b 2.798262
Name: data1, dtype: float64
同時也可以對整個DataFrame df
做分組,再計算其中列data1
的聚合值
>> df.groupby('key1')['data1'].sum() # or df.groupby('key1')[['data1']].sum()
key1
a 0.356170
b 2.798262
Name: data1, dtype: float64
可以根據(jù)多個列做分組灸异,比如下面用key1
和key2
對DataFrame做分組府适。
>> g2 = df.groupby(['key1', 'key2'])
>> g2.sum()
data1 data2
key1 key2
a one -0.140968 0.027832
two 0.497138 0.195447
b one 1.158331 -0.580097
two 1.639932 -0.997953
上面的g2
結(jié)果也可以通過unstack
方法表示為包含獨(dú)一無二鍵值對的分級索引(hierarchical index)
>> g2.sum.unstack()
data1 data2
key2 one two one two
key1
a -0.140968 0.497138 0.027832 0.195447
b 1.158331 1.639932 -0.580097 -0.997953
分組的依據(jù)也可以是相同長度的其他數(shù)組,比如
>> states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio'])
>> years = np.array([2005, 2005, 2006, 2005, 2006])
>> df['data1'].groupby([states, years]).sum()
California 2005 0.497138
2006 1.158331
Ohio 2005 1.802156
2006 -0.303192
Name: data1, dtype: float64
我們再來看有哪些列參與了分組肺樟。下面的這個例子檐春,用key1
列對DataFrame做分組,結(jié)果中只顯示了data1
和data2
而沒有key2
么伯。因為key2
列是非數(shù)值列疟暖,被排除在分組聚合的結(jié)果中。
>> df.groupby('key1').sum()
data1 data2
key1
a 0.356170 0.223279
b 2.798262 -1.578050
size
方法返回分組的數(shù)字
>> df.groupby(['key1', 'key2']).size()
key1 key2
a one 2
two 1
b one 1
two 1
dtype: int64
(2022.05.13 Fri)
迭代groupby對象
>> for k,v in df.groupby('key1'):
... print(k,'\n')
... print(v)
a
key1 key2 data1 data2
0 a one 0.162224 -0.497908
1 a two 0.497138 0.195447
4 a one -0.303192 0.525740
b
key1 key2 data1 data2
2 b one 1.158331 -0.580097
3 b two 1.639932 -0.997953
也可以轉(zhuǎn)化成list對象迭代
>> list(df.groupby('key1'))
[('a',
key1 key2 data1 data2
0 a one 0.162224 -0.497908
1 a two 0.497138 0.195447
4 a one -0.303192 0.525740),
('b',
key1 key2 data1 data2
2 b one 1.158331 -0.580097
3 b two 1.639932 -0.997953)]
注意田柔,當(dāng)使用多個key做分組時俐巴,key的不同元素的組合保存為tuple
>> for k, g in df.groupby(['key1', 'key2']):
... print(k, '\n')
... print(g)
('a', 'one')
key1 key2 data1 data2
0 a one 0.162224 -0.497908
4 a one -0.303192 0.525740
('a', 'two')
key1 key2 data1 data2
1 a two 0.497138 0.195447
('b', 'one')
key1 key2 data1 data2
2 b one 1.158331 -0.580097
('b', 'two')
key1 key2 data1 data2
3 b two 1.639932 -0.997953
按字典和Series分組
除了數(shù)組之外,DataFrame還可按字典和Series分組硬爆。
考慮如下DataFrame
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 1.317179 -0.209542 0.943849 -2.226055 -0.430271
Steve -1.757563 -1.531038 -0.519030 -0.533200 0.447961
Wes 0.407967 NaN NaN 0.658021 0.440425
Jim -0.320255 -1.165006 0.275818 1.276247 -1.346920
Travis -1.678433 -1.272230 1.391566 -0.006185 -0.109339
定義一個字典
>> mapping = {'a': 'red', 'b': 'red', 'c': 'blue',
'd': 'blue', 'e': 'red', 'f' : 'orange'}
可以字典為key欣舵,對people
進(jìn)行分組
>> by_column = people.groupby(mapping, axis=1)
>> by_column.sum()
blue red
Joe -1.282207 0.677367
Steve -1.052230 -2.840639
Wes 0.658021 0.848391
Jim 1.552065 -2.832181
Travis 1.385381 -3.060002
也可以將字典轉(zhuǎn)化為Series并以此為依據(jù)對數(shù)據(jù)做分組
>> ms = pd.Series(mapping)
>> ms
a red
b red
c blue
d blue
e red
f orange
dtype: object
>> people.groupby(ms, axis=1).sum()
blue red
Joe -1.282207 0.677367
Steve -1.052230 -2.840639
Wes 0.658021 0.848391
Jim 1.552065 -2.832181
Travis 1.385381 -3.060002
根據(jù)函數(shù)分組
函數(shù)也可作為group key。一旦函數(shù)做為group key缀磕,將index值作為函數(shù)的輸入缘圈,而返回值作為組名。比如上面的people
數(shù)組袜蚕,調(diào)用len
作為group key糟把,則返回的index值的長度將作為組名。
>> people.groupby(len).sum()
a b c d e
3 1.404891 -1.374547 1.219667 -0.291787 -1.336766
5 -1.757563 -1.531038 -0.519030 -0.533200 0.447961
6 -1.678433 -1.272230 1.391566 -0.006185 -0.109339
另一個例子牲剃,一個函數(shù)檢測string的結(jié)尾字母遣疯,如果是e或s則分別返回,其他情況返回others凿傅。將這個函數(shù)作為group key缠犀,查看結(jié)果。
def endswith(k):
if k.endswith('e'):
return 'e'
elif k.endswith('s'):
return 's'
else:
return 'others'
>> people.groupby(endswith).sum()
a b c d e
e -0.440383 -1.740579 0.424819 -2.759255 0.017690
others -0.320255 -1.165006 0.275818 1.276247 -1.346920
s -1.270466 -1.272230 1.391566 0.651836 0.331085
亦可混合使用
>> key_list = ['one', 'one', 'one', 'two', 'two']
>> people.groupby([len, key_list]).min()
a b c d e
3 one 0.407967 -0.209542 0.943849 -2.226055 -0.430271
two -0.320255 -1.165006 0.275818 1.276247 -1.346920
5 one -1.757563 -1.531038 -0.519030 -0.533200 0.447961
6 two -1.678433 -1.272230 1.391566 -0.006185 -0.109339
根據(jù)分級索引(hierarchical index)分組
>> 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
cty US JP
tenor 1 3 5 1 3
0 0.560145 -1.265934 0.119827 -1.063512 0.332883
1 -2.359419 -0.199543 -1.541996 -0.970736 -1.307030
2 0.286350 0.377984 -0.753887 0.331286 1.349742
3 0.069877 0.246674 -0.011862 1.004812 1.327195
>> hier_df.groupby(level='cty', axis=1).count()
cty JP US
0 2 3
1 2 3
2 2 3
3 2 3
Data Aggregation數(shù)據(jù)聚合
數(shù)據(jù)聚合指的是從數(shù)組生成標(biāo)量(scalar)值的數(shù)據(jù)變換過程狭归。常見的數(shù)據(jù)聚合包括前面出現(xiàn)的幾種夭坪,比如mean
,sum
过椎,count
室梅,min
/max
,median
疚宇,std, var
亡鼠,prod
,first
敷待,last
等间涵。還包括quantile
用于計算分位點(diǎn)。比如前面的根據(jù)key1
分組的data1
數(shù)據(jù)榜揖,計算其0.9分位點(diǎn)
>> grouped.quantile(0.9)
key1
a 0.430156
b 1.591772
Name: data1, dtype: float64
也可以自定義聚合函數(shù)勾哩,并用agg
方法引入抗蠢。比如定義一個80分位點(diǎn)與20分位點(diǎn)的差值函數(shù)q82
,并用agg
方法調(diào)用該聚合函數(shù)思劳。
def q82(arr):
return np.percentile(arr, 80) - np.percentile(arr, 20)
>> grouped.agg(q82)
key1
a 0.480198
b 0.288961
Name: data1, dtype: float64
注意迅矛,如果是自定義的函數(shù),在被agg
方法調(diào)用時直接寫函數(shù)名即可潜叛,如果是內(nèi)置聚合函數(shù)如上面提到的秽褒,則需要在名字上加引號,且與不用agg
方法的直接調(diào)用返回相同的結(jié)果威兜。
>> grouped.agg('mean')
key1
a 0.118723
b 1.399131
Name: data1, dtype: float64
>> grouped.mean()
key1
a 0.118723
b 1.399131
Name: data1, dtype: float64
可通過describe
方法驗證上面的結(jié)果
>> grouped.describe()
count mean std min 25% 50% 75%
max
key1
a 3.0 0.118723 0.401935 -0.303192 -0.070484 0.162224 0.329681 0.497138
b 2.0 1.399131 0.340543 1.158331 1.278731 1.399131 1.519531 1.639932
當(dāng)然也可以用實(shí)現(xiàn)其他功能的函數(shù)销斟,比如對分組結(jié)果排序
def sf(arr):
arr = list(map(lambda x: round(x, 2), arr))
return sorted(arr)
>> grouped.agg(sf)
key1
a [-0.3, 0.16, 0.5]
b [1.16, 1.64]
Name: data1, dtype: object
列級(colume-wise)應(yīng)用和多函數(shù)應(yīng)用
對單獨(dú)一個列做多個函數(shù)操作,參考如下案例
>> tips = pd.read_csv('/Users/.../pydata-book/examples/tips.csv')
>> tips.shape
(244, 6)
>> tips.columns
Index(['total_bill', 'tip', 'smoker', 'day', 'time', 'size'], dtype='object')
>> tips.head(5)
total_bill tip smoker day time size
0 16.99 1.01 No Sun Dinner 2
1 10.34 1.66 No Sun Dinner 3
2 21.01 3.50 No Sun Dinner 3
3 23.68 3.31 No Sun Dinner 2
4 24.59 3.61 No Sun Dinner 4
>> tips['tip_pct'] = tips['tip']/tips['total_bill'] # 生成小費(fèi)比例字段
>> grouped = tips.groupby(['day', 'smoker']) #根據(jù)day和smoker兩個字段對數(shù)據(jù)分組
>> grouped_pct = grouped['tip_pct'] # 取得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
計算一列的多個統(tǒng)計量/聚合值椒舵,用agg
方法蚂踊,統(tǒng)計量用list保存
>> grouped_pct.agg(['mean', 'max', q82])
mean max q82
day smoker
Fri No 0.151650 0.187735 0.034600
Yes 0.174783 0.263480 0.097666
Sat No 0.158048 0.291990 0.061659
Yes 0.147906 0.325733 0.112116
Sun No 0.160113 0.252672 0.072194
Yes 0.187250 0.710345 0.144385
Thur No 0.160298 0.266312 0.060163
Yes 0.163863 0.241255 0.061858
此外,你可以為作用于列的統(tǒng)計量/聚合函數(shù)命名逮栅,并以新命名顯示悴势,以tuple in list為表示。
>> rn = [('func_mean', 'mean'), ('func_q82', q82)]
>> grouped_pct.agg(rn)
func_mean func_q82
day smoker
Fri No 0.151650 0.034600
Yes 0.174783 0.097666
Sat No 0.158048 0.061659
Yes 0.147906 0.112116
Sun No 0.160113 0.072194
Yes 0.187250 0.144385
Thur No 0.160298 0.060163
Yes 0.163863 0.061858
此方法也可用于多個列使用相同的聚合方法措伐,注意多列的名字表示成一個list特纤,作為grouped的index。注意到對多個列使用不同的聚合方法侥加,DataFrame將返回分級列(hierarchical column)捧存。
>> tmp = grouped[['tip_pct', 'total_bill']].agg(['mean', 'max', q82])
>> tmp
tip_pct total_bill
mean max q82 mean max q82
day smoker
Fri No 0.151650 0.187735 0.034600 18.420000 22.75 8.022
Yes 0.174783 0.263480 0.097666 16.813333 40.17 11.166
Sat No 0.158048 0.291990 0.061659 19.661778 48.33 7.724
Yes 0.147906 0.325733 0.112116 21.276667 50.81 15.168
Sun No 0.160113 0.252672 0.072194 20.506667 48.17 11.844
Yes 0.187250 0.710345 0.144385 24.120000 45.35 17.224
Thur No 0.160298 0.266312 0.060163 17.113111 41.19 11.706
Yes 0.163863 0.241255 0.061858 19.190588 43.11 7.284
上面結(jié)果可按列讀取
>> tmp['tip_pct']
mean max q82
day smoker
Fri No 0.151650 0.187735 0.034600
Yes 0.174783 0.263480 0.097666
Sat No 0.158048 0.291990 0.061659
Yes 0.147906 0.325733 0.112116
Sun No 0.160113 0.252672 0.072194
Yes 0.187250 0.710345 0.144385
Thur No 0.160298 0.266312 0.060163
Yes 0.163863 0.241255 0.061858
而對不同的列采用不同的聚合函數(shù),需要使用字典担败。比如對tip
列要計算最大值昔穴,對size
列計算求和,可用列名做key提前,聚合函數(shù)名或聚合函數(shù)形成的列做value
>> ac = {'tip': ['max', 'mean'], 'size': 'sum'}
>> grouped.agg(ac)
tip size
max mean sum
day smoker
Fri No 3.50 2.812500 9
Yes 4.73 2.714000 31
Sat No 9.00 3.102889 115
Yes 10.00 2.875476 104
Sun No 6.00 3.167895 167
Yes 6.50 3.516842 49
Thur No 6.70 2.673778 112
Yes 5.00 3.030000 40
聚合結(jié)果取消index
對分組后的聚合結(jié)果取消index吗货,只需要在做groupby
操作時加入as_index = False
選項
>> tips.groupby(['day', 'smoker'], as_index=False).mean()
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
使用聚合函數(shù)的agg
方法與apply
的差別
agg
方法應(yīng)用于列、Series
apply
方法應(yīng)用于這個DataFrame
(待驗證)
Apply-一般性的Split-Apply-Combine
Groupby方法里面最常用的部分是Apply狈网,即對數(shù)據(jù)分組之后宙搬,再對每個分組piece進(jìn)行的操作。Apply操作之后即是對數(shù)據(jù)做concatenate拓哺。注意Apply作用的對象是group piece勇垛,也是一個DataFrame。
仍然考慮上面的數(shù)據(jù)框tips
士鸥,找出每天的中晚飯小費(fèi)收入占比最高的前五位闲孤。首先寫個函數(shù)返回一個數(shù)據(jù)框按某一列排序最高的前5位所有數(shù)據(jù)。
def top(df, n=5, column='tip_pct'):
return df.sort_value(name=column)[-n:]
下面按day
和time
分組烤礁,查看每組有多少筆小費(fèi)讼积,并對分組執(zhí)行top
函數(shù)肥照。可以看出周六周日兩天人們普遍支付更多比例的小費(fèi)勤众。
>> tips.groupby(['day', 'time']).size()
day time
Fri Dinner 12
Lunch 7
Sat Dinner 87
Sun Dinner 76
Thur Dinner 1
Lunch 61
dtype: int64
>> tips.groupby(['day', 'time']).apply(top)
total_bill tip smoker day time size tip_pct
day time
Fri Dinner 91 22.49 3.50 No Fri Dinner 2 0.155625
92 5.75 1.00 Yes Fri Dinner 2 0.173913
101 15.38 3.00 Yes Fri Dinner 2 0.195059
100 11.35 2.50 Yes Fri Dinner 2 0.220264
93 16.32 4.30 Yes Fri Dinner 2 0.263480
Lunch 220 12.16 2.20 Yes Fri Lunch 2 0.180921
223 15.98 3.00 No Fri Lunch 3 0.187735
226 10.09 2.00 Yes Fri Lunch 2 0.198216
222 8.58 1.92 Yes Fri Lunch 1 0.223776
221 13.42 3.48 Yes Fri Lunch 2 0.259314
Sat Dinner 20 17.92 4.08 No Sat Dinner 2 0.227679
214 28.17 6.50 Yes Sat Dinner 3 0.230742
109 14.31 4.00 Yes Sat Dinner 2 0.279525
232 11.61 3.39 No Sat Dinner 2 0.291990
67 3.07 1.00 Yes Sat Dinner 1 0.325733
Sun Dinner 181 23.33 5.65 Yes Sun Dinner 2 0.242177
51 10.29 2.60 No Sun Dinner 2 0.252672
183 23.17 6.50 Yes Sun Dinner 4 0.280535
178 9.60 4.00 Yes Sun Dinner 2 0.416667
172 7.25 5.15 Yes Sun Dinner 2 0.710345
Thur Dinner 243 18.78 3.00 No Thur Dinner 2 0.159744
Lunch 200 18.71 4.00 Yes Thur Lunch 3 0.213789
87 18.28 4.00 No Thur Lunch 2 0.218818
88 24.71 5.85 No Thur Lunch 2 0.236746
194 16.58 4.00 Yes Thur Lunch 2 0.241255
149 7.51 2.00 No Thur Lunch 2 0.266312
...
在apply
中調(diào)用的函數(shù)可以通過如下方式傳遞參數(shù)建峭。比如查看每天每個時段最高價的bill,可指定n=1
和columns=total_bill
决摧。
>> tips.groupby(['day', 'time']).apply(top, n=1, columns='total_bill')
total_bill tip smoker day time size tip_pct
day time
Fri Dinner 95 40.17 4.73 Yes Fri Dinner 4 0.117750
Lunch 225 16.27 2.50 Yes Fri Lunch 2 0.153657
Sat Dinner 170 50.81 10.00 Yes Sat Dinner 3 0.196812
Sun Dinner 156 48.17 5.00 No Sun Dinner 6 0.103799
Thur Dinner 243 18.78 3.00 No Thur Dinner 2 0.159744
Lunch 197 43.11 5.00 Yes Thur Lunch 4 0.115982
再比如,查看分組后某個數(shù)據(jù)的describe
信息凑兰,除了對分組結(jié)果調(diào)用內(nèi)置的describe
方法掌桩,也可將describe
寫成lambda函數(shù)通過apply
調(diào)用
>> tips.groupby('smoker')['tip_pct'].describe()
count mean std min 25% 50% 75% max
smoker
No 151.0 0.159328 0.039910 0.056797 0.136906 0.155625 0.185014 0.291990
Yes 93.0 0.163196 0.085119 0.035638 0.106771 0.153846 0.195059 0.710345
>> f = lambda x: x.describe()
>> tips.groupby('smoker')['tip_pct'].apply(f)
smoker
No count 151.000000
mean 0.159328
std 0.039910
min 0.056797
25% 0.136906
50% 0.155625
75% 0.185014
max 0.291990
Yes count 93.000000
mean 0.163196
std 0.085119
min 0.035638
25% 0.106771
50% 0.153846
75% 0.195059
max 0.710345
Name: tip_pct, dtype: float64
(2022.05.16 Mon)
不顯示分組鍵Suppressing the group key:
上面的例子中結(jié)果數(shù)據(jù)在初始對象的索引之上保存了分級索引hierachical index,可使用group_keys=False
指令取消分級索引:
>> tips.groupby(['day', 'time'], group_keys=False).apply(top, n=1, columns='total_bill')
total_bill tip smoker day time size tip_pct
95 40.17 4.73 Yes Fri Dinner 4 0.117750
225 16.27 2.50 Yes Fri Lunch 2 0.153657
170 50.81 10.00 Yes Sat Dinner 3 0.196812
156 48.17 5.00 No Sun Dinner 6 0.103799
243 18.78 3.00 No Thur Dinner 2 0.159744
197 43.11 5.00 Yes Thur Lunch 4 0.115982
分位數(shù)和桶分析Quantile and Bucket Analysis
可使用pandas.cut
指令對一個Series做分位分析姑食,并結(jié)合groupby
指令對數(shù)組做分析波岛。
>> frame = pd.DataFrame({'data1': np.random.randn(1000),
'data2': np.random.randn(1000)})
>> frame.head()
data1 data2
0 -0.754650 -0.085965
1 -0.922789 0.523089
2 -1.001486 0.306347
3 1.169146 -1.424664
4 2.311694 0.280592
>> quartiles = pd.cut(frame.data1, 4)
>> quartiles.head()
0 (-1.563, 0.154]
1 (-1.563, 0.154]
2 (-1.563, 0.154]
3 (0.154, 1.871]
4 (1.871, 3.588]
Name: data1, dtype: category
Categories (4, interval[float64, right]): [(-3.287, -1.563] < (-1.563, 0.154] < (0.154, 1.871] < (1.871, 3.588]]
cut
方法返回的Categorial
對象可傳遞給groupby
方法。我們可以以此為依據(jù)對data2
字段做分組音半。
def get_stats(df):
return {'mean': df.mean(), 'min': df.min(), 'max': df.max(), 'count': df.count()}
>> q = frame.data2.groupby(quartiles).apply(get_stats).unstack()
mean min max count
data1
(-3.287, -1.563] 0.069223 -2.676886 2.390891 57.0
(-1.563, 0.154] -0.089421 -3.099386 2.872714 514.0
(0.154, 1.871] -0.001327 -3.319630 3.122984 394.0
(1.871, 3.588] 0.093478 -2.111966 2.091880 35.0
注意這里的cut
得到的分組是范圍的長度尺度上相同/相似的結(jié)果则拷。如果要每個分組內(nèi)值的數(shù)量相同/相似,則需要使用qcut
指令曹鸠。
GroupBy應(yīng)用
Case 1:按組填充NA值
按組填充該組中的空值煌茬。有下面數(shù)據(jù),保存美國若干州的某項數(shù)據(jù)彻桃。這些州分屬東西兩部分坛善。我們設(shè)置其中一些值為空。
>> states = ['Ohio', 'New York', 'Vermont', 'Florida',\
'Oregon', 'Nevada', 'California', 'Idaho']
>> group_key = ['East'] * 4 + ['West'] * 4
>> data = pd.Series(np.random.randn(8), index=states)
>> data
Ohio 0.869617
New York -1.113901
Vermont -0.225091
Florida -0.010972
Oregon -0.312229
Nevada -0.122721
California -1.171598
Idaho 0.361301
dtype: float64
>> data[::2] = np.nan
>> data
Ohio NaN
New York -1.113901
Vermont NaN
Florida -0.010972
Oregon NaN
Nevada -0.122721
California NaN
Idaho 0.361301
dtype: float64
如果此時分組邻眷,計算得到的是非空值的統(tǒng)計信息眠屎,不包含空值部分
>> data.groupby(group_key).mean()
East -0.562437
West 0.119290
dtype: float64
下面按組填充數(shù)據(jù),比如對每組中的空值填充該組其他值的mean
>> f_mean = lambda x: x.fillna(x.mean())
>> data.groupby(group_key).apply(f_mean)
Ohio -0.562437
New York -1.113901
Vermont -0.562437
Florida -0.010972
Oregon 0.119290
Nevada -0.122721
California 0.119290
Idaho 0.361301
dtype: float64
也可針對某個組填充特定值
>> fill_values = {'East': 5, 'West': 10}
>> fill_func = lambda g: g.fillna(fill_values[g.name])
>> data.groupby(group_key).apply(fill_func)
Ohio 5.000000
New York -1.113901
Vermont 5.000000
Florida -0.010972
Oregon 10.000000
Nevada -0.122721
California 10.000000
Idaho 0.361301
dtype: float64
Case 2:分組計算均值
df = pd.DataFrame({'category': ['a', 'a', 'a', 'a',\
'b', 'b', 'b', 'b'],\
'data': np.random.randn(8),\
'weights': np.random.rand(8)})
>> df
category data weights
0 a 1.561587 0.957515
1 a 1.219984 0.347267
2 a -0.482239 0.581362
3 a 0.315667 0.217091
4 b -0.047852 0.894406
5 b -0.454145 0.918564
6 b -0.556774 0.277825
7 b 0.253321 0.955905
加權(quán)平均的函數(shù)get_wa
get_wa = lambda x: np.average(x.data, weights=x.weights)
對df
這個dataframe按分組執(zhí)行加權(quán)平均操作
>> df.groupby('category').apply(get_wa)
category
a 0.111150
b -1.091834
dtype: float64
基于pandas.groupby
的pandas.transform
指令
前面已經(jīng)提到肆饶,用pandas.groupby
指令對DataFrame分組之后改衩,可針對各組做操作。比如對各組求其mean操作
>> dd = pd.DataFrame({'keys': ['a', 'b', 'c', 'c','c','a','b','b','a'], 'values': np.arange(9,)})
>> dd.groupby('keys').values.mean()
keys
a 4.333333
b 4.666667
c 3.000000
Name: values, dtype: float64
假定我們想要得到一個Series和原序列長度相同驯镊,但是新Series中的值用原序列分組后的mean值代替葫督。這種情況下pandas提供了transform
方法執(zhí)行這種操作。下面計算對根據(jù)key keys
做分組的values
的值做歸一化操作的過程阿宅,歸一化的結(jié)果寫入原DataFrame中候衍。
>> f = lambda x: (x - x.mean())/x.std()
>> dd['mean'] = dd.groupby('keys').values.transform('mean')
>> dd['std'] = dd.groupby('keys').values.transform('std')
>> dd['norm'] = dd.groupby('keys').values.transform(f)
>> dd
keys values mean std norm
0 a 0 4.333333 4.041452 -1.072222
1 b 1 4.666667 3.214550 -1.140647
2 c 2 3.000000 1.000000 -1.000000
3 c 3 3.000000 1.000000 0.000000
4 c 4 3.000000 1.000000 1.000000
5 a 5 4.333333 4.041452 0.164957
6 b 6 4.666667 3.214550 0.414781
7 b 7 4.666667 3.214550 0.725866
8 a 8 4.333333 4.041452 0.907265
Reference
1 Python for Data Analysis, Wes McKinney