Pandas分組函數(shù)groupby的用法詳解
在數(shù)據(jù)分析時(shí),經(jīng)常需要將數(shù)據(jù)分成不同的群組,pandas中的groupby()函數(shù)可以完美地完成各種分組操作。
分組是根據(jù)DataFrame/Series的某個(gè)字段值,將該字段的值相等的行/列分到同一組中,每一個(gè)小組是一個(gè)新的DataFrame或Series。
groupby()也可以按DataFrame中的多個(gè)字段分組,當(dāng)多個(gè)字段的值都相等時(shí)分到同一組。
groupby()經(jīng)常與批處理函數(shù)apply()、聚合函數(shù)agg()等配合使用,實(shí)現(xiàn)對(duì)數(shù)據(jù)的多元處理。
groupby用法和參數(shù)介紹
groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=no_default, observed=False, dropna=True):
by: 指定根據(jù)哪個(gè)/哪些字段分組,默認(rèn)值是None,按多個(gè)字段分組時(shí)傳入列表。by參數(shù)可以按位置參數(shù)的方式傳入。
axis: 設(shè)置按列分組還是按行分組,0或index表示按列分組,1或columns表示按行分組,默認(rèn)值為0。
level: 當(dāng)DataFrame的索引為多重索引時(shí),level參數(shù)指定用于分組的索引,可以傳入多重索引中索引的下標(biāo)(0,1...)或索引名,多個(gè)用列表傳入。
level參數(shù)不能與by參數(shù)同時(shí)使用,如果兩者同時(shí)存在,當(dāng)by參數(shù)傳入的是多重索引中的索引,則level不生效,當(dāng)by參數(shù)傳入的是DataFrame的列名,則報(bào)錯(cuò)。
as_index: 分組結(jié)果默認(rèn)將分組列的值作為索引,如果按單列分組,結(jié)果默認(rèn)是單索引,如果按多列分組,結(jié)果默認(rèn)是多重索引。將as_index設(shè)置為False可以重置索引(0,1...)。
sort: 結(jié)果按分組列的值升序排列,將sort設(shè)置為False則不排序,可以提升性能。
dropna: 默認(rèn)情況下,分組列的NaN在分組結(jié)果中不保留,將dropna設(shè)置為False,可以保留NaN分組。
其他三個(gè)參數(shù)不用關(guān)注,group_keys參數(shù)在源碼中未使用,squeeze參數(shù)因?yàn)轭愋筒患嫒?,官方已棄用,observed參數(shù)表示重設(shè)索引時(shí),保留創(chuàng)建的全NaN行。
分組對(duì)象的內(nèi)部結(jié)構(gòu)
# coding=utf-8 import pandas as pd import numpy as np vip_df = pd.DataFrame( {'isVip': ['vip', 'svip', 'member', 'vip', 'member', 'vip', 'svip'], 'gender': ['male', 'female', 'female', 'female', 'male', 'female', 'male'], 'age': [25, 30, 40, 25, 40, 18, 30], 'vipLevel': ['LV2', 'LV5', np.nan, 'LV3', 'LV2', 'LV2', 'LV3'], 'growValue': [180, 425, np.nan, 288, 190, 110, 240]} ) print(vip_df) grouped = vip_df.groupby('isVip') print(grouped)
isVip gender age vipLevel growValue
0 vip male 25 LV2 180.0
1 svip female 30 LV5 425.0
2 member female 40 NaN NaN
3 vip female 25 LV3 288.0
4 member male 40 LV2 190.0
5 vip female 18 LV2 110.0
6 svip male 30 LV3 240.0
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001C3E81D1370>
groupby()分組得到的是一個(gè)DataFrameGroupBy對(duì)象,直接打印DataFrameGroupBy對(duì)象只能看到它的內(nèi)存地址,看不到內(nèi)部的結(jié)構(gòu)。
for name, group in grouped: print(name) print(group)
member
isVip gender age vipLevel growValue
2 member female 40 NaN NaN
4 member male 40 LV2 190.0
svip
isVip gender age vipLevel growValue
1 svip female 30 LV5 425.0
6 svip male 30 LV3 240.0
vip
isVip gender age vipLevel growValue
0 vip male 25 LV2 180.0
3 vip female 25 LV3 288.0
5 vip female 18 LV2 110.0
for group in grouped: print(group) print(type(group), type(group[0]), type(group[1]))
('member', isVip gender age vipLevel growValue
2 member female 40 NaN NaN
4 member male 40 LV2 190.0)
<class 'tuple'> <class 'str'> <class 'pandas.core.frame.DataFrame'>
('svip', isVip gender age vipLevel growValue
1 svip female 30 LV5 425.0
6 svip male 30 LV3 240.0)
<class 'tuple'> <class 'str'> <class 'pandas.core.frame.DataFrame'>
('vip', isVip gender age vipLevel growValue
0 vip male 25 LV2 180.0
3 vip female 25 LV3 288.0
5 vip female 18 LV2 110.0)
<class 'tuple'> <class 'str'> <class 'pandas.core.frame.DataFrame'>
DataFrameGroupBy是一個(gè)可迭代對(duì)象,可以轉(zhuǎn)換成list打印,也可以直接遍歷打印出來(lái)。遍歷出來(lái)的是一個(gè)個(gè)元組,每個(gè)元組對(duì)應(yīng)一個(gè)分組,元組的第一個(gè)元素與分組列里的值對(duì)應(yīng),元組的第二個(gè)元素是分到當(dāng)前小組的數(shù)據(jù),是一個(gè)DataFrame。
DataFrameGroupBy對(duì)象的內(nèi)部結(jié)構(gòu)為:[(分組名1, 子DataFrame1), (分組名2, 子DataFrame2), ...],相當(dāng)于groupby()將DataFrame按字段值分成了多個(gè)小的DataFrame,然后將字段值和小的DataFrame用元組的方式保存在DataFrameGroupBy對(duì)象中。
print(grouped.groups) group_name = [gn for gn in grouped.groups.keys()] print(group_name) group = grouped.get_group(group_name[2]) print('-'*40, '\n', group, sep='')
{'member': [2, 4], 'svip': [1, 6], 'vip': [0, 3, 5]}
['member', 'svip', 'vip']
----------------------------------------
isVip gender age vipLevel growValue
0 vip male 25 LV2 180.0
3 vip female 25 LV3 288.0
5 vip female 18 LV2 110.0
分組對(duì)象的groups屬性可以返回分組信息,結(jié)果是一個(gè)形似字典的對(duì)象,由分組名和此分組數(shù)據(jù)在原DataFrame中的行索引組成。
借用groups可以提取出所有分組的分組名,分組對(duì)象的get_group()方法可以返回指定分組名的子DataFrame。
按多重索引分組
vip_multi_df = vip_df.set_index(['isVip', 'gender']) print('-'*40, '\n', vip_multi_df, sep='')
----------------------------------------
age vipLevel growValue
isVip gender
vip male 25 LV2 180.0
svip female 30 LV5 425.0
member female 40 NaN NaN
vip female 25 LV3 288.0
member male 40 LV2 190.0
vip female 18 LV2 110.0
svip male 30 LV3 240.0
# 按多重索引中的指定索引進(jìn)行分組 grouped = vip_multi_df.groupby(level='isVip') print('-'*40, '\n', grouped.mean(), sep='') # 按多重索引中除指定索引之外的索引分組 grouped = vip_multi_df.groupby(level=vip_multi_df.index.names.difference(['gender'])) print('-'*40, '\n', grouped.mean(), sep='') # 按多重索引中的多個(gè)索引分組 grouped = vip_multi_df.groupby(level=[0, 1]) print('-'*40, '\n', grouped.mean(), sep='')
----------------------------------------
age growValue
isVip
member 40.000000 190.000000
svip 30.000000 332.500000
vip 22.666667 192.666667
----------------------------------------
age growValue
isVip
member 40.000000 190.000000
svip 30.000000 332.500000
vip 22.666667 192.666667
----------------------------------------
age growValue
isVip gender
member female 40.0 NaN
male 40.0 190.0
svip female 30.0 425.0
male 30.0 240.0
vip female 21.5 199.0
male 25.0 180.0
level參數(shù)用于設(shè)置按多重索引中的指定索引分組,level傳入的方式可以是索引name,也可以是索引在多重索引中的下標(biāo),還可以是排除某個(gè)索引外的其他索引。指定多個(gè)時(shí)用列表的方式傳入。
grouped = vip_multi_df.groupby('gender') print('-'*40, '\n', grouped.mean(), sep='') grouped = vip_multi_df.groupby(['gender', 'age']) print('-'*40, '\n', grouped.mean(), sep='')
----------------------------------------
age growValue
gender
female 28.250000 274.333333
male 31.666667 203.333333
----------------------------------------
growValue
gender age
female 18 110.0
25 288.0
30 425.0
40 NaN
male 25 180.0
30 240.0
40 190.0
多重索引中的索引也可以傳給groupby()的by參數(shù),分組結(jié)果與將多重索引作為DataFrame的列是一樣的。
如果用DataFrame的列作為分組列,多重索引會(huì)被轉(zhuǎn)換成列保留在結(jié)果中。也可以用多重索引中的索引與列一起組合分組,相當(dāng)于先對(duì)DataFrame重設(shè)索引再分組。
重置結(jié)果的索引
grouped = vip_multi_df.groupby(['isVip', 'gender']) print('-'*40, '\n', grouped.mean(), sep='') # 重設(shè)索引 grouped = vip_multi_df.groupby(['isVip', 'gender'], as_index=False) print('-'*40, '\n', grouped.mean(), sep='')
----------------------------------------
age growValue
isVip gender
member female 40.0 NaN
male 40.0 190.0
svip female 30.0 425.0
male 30.0 240.0
vip female 21.5 199.0
male 25.0 180.0
----------------------------------------
age growValue
0 40.0 NaN
1 40.0 190.0
2 30.0 425.0
3 30.0 240.0
4 21.5 199.0
5 25.0 180.0
分組結(jié)果的索引默認(rèn)是分組列的值,將as_index設(shè)置為False可以重置索引,相當(dāng)于先分組再調(diào)用reset_index()函數(shù)。
結(jié)果是否排序
grouped = vip_df.groupby('isVip') print('-'*40, '\n', grouped.mean(), sep='') grouped = vip_df.groupby('isVip', sort=False) print('-'*40, '\n', grouped.mean(), sep='')
----------------------------------------
age growValue
isVip
member 40.000000 190.000000
svip 30.000000 332.500000
vip 22.666667 192.666667
----------------------------------------
age growValue
isVip
vip 22.666667 192.666667
svip 30.000000 332.500000
member 40.000000 190.000000
groupby()默認(rèn)對(duì)結(jié)果按分組列的值升序排列,如果將sort參數(shù)修改為False,則不排序,保留原DataFrame中的順序,不排序可以提升性能。
是否保留空值
grouped = vip_df.groupby('vipLevel') print('-'*40, '\n', grouped.mean(), sep='') grouped = vip_df.groupby('vipLevel', dropna=False) print('-'*40, '\n', grouped.mean(), sep='')
----------------------------------------
age growValue
vipLevel
LV2 27.666667 160.0
LV3 27.500000 264.0
LV5 30.000000 425.0
----------------------------------------
age growValue
vipLevel
LV2 27.666667 160.0
LV3 27.500000 264.0
LV5 30.000000 425.0
NaN 40.000000 NaN
當(dāng)分組列有空值(NaN)時(shí),默認(rèn)的分組結(jié)果中不保留NaN分組,將dropna參數(shù)修改為False,正常保留NaN分組。
提取分組結(jié)果的指定列
grouped = vip_df.groupby('isVip', dropna=False) print('-'*40, '\n', grouped['gender'], sep='') for name, group in grouped['gender']: print(name) print(group)
----------------------------------------
<pandas.core.groupby.generic.SeriesGroupBy object at 0x00000285CA1B0790>
member
2 female
4 male
Name: gender, dtype: object
svip
1 female
6 male
Name: gender, dtype: object
vip
0 male
3 female
5 female
Name: gender, dtype: object
從groupby()分組結(jié)果中取一列,得到的是一個(gè)SeriesGroupBy對(duì)象,直接打印SeriesGroupBy對(duì)象只能看到它的內(nèi)存地址,看不到內(nèi)部的結(jié)構(gòu)。
SeriesGroupBy的內(nèi)部結(jié)構(gòu)與DataFrameGroupBy相似,SeriesGroupBy對(duì)象的內(nèi)部結(jié)構(gòu)為:[(分組名1, 子Series1), (分組名2, 子Series2), ...],因此SeriesGroupBy也可以轉(zhuǎn)換成list打印,也可以遍歷取出每一個(gè)元素。
grouped = vip_df['gender'].groupby(vip_df['isVip']) for name, group in grouped: print(name) print(group)
member
2 female
4 male
Name: gender, dtype: object
svip
1 female
6 male
Name: gender, dtype: object
vip
0 male
3 female
5 female
Name: gender, dtype: object
也可以先指定需要獲取的列,再按DataFrame的另一個(gè)列進(jìn)行分組,結(jié)果與先分組再獲取指定列相同。
以上就是pandas中g(shù)roupby()函數(shù)的用法介紹和分析,本文都是用DataFrame舉例,Series的用法相似,不重復(fù)了。
相關(guān)文章
Python中if __name__ == "__main__"詳細(xì)解釋
這篇文章主要介紹了Python中if __name__ == "__main__"詳細(xì)解釋,需要的朋友可以參考下2014-10-10Python基礎(chǔ)之畫(huà)圖神器matplotlib
這篇文章主要介紹了python基礎(chǔ)之畫(huà)圖神器matplotlib,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)python的小伙伴們有一定的幫助,需要的朋友可以參考下2021-04-04numpy.linalg.eig() 計(jì)算矩陣特征向量方式
今天小編就為大家分享一篇numpy.linalg.eig() 計(jì)算矩陣特征向量方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11python實(shí)現(xiàn)多線程行情抓取工具的方法
當(dāng)我們實(shí)現(xiàn)了單線程,接下來(lái)就是實(shí)現(xiàn)多線程了,下面這篇文章主要給大家介紹了關(guān)于python實(shí)現(xiàn)多線程行情抓取工具的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-02-02Python調(diào)用PC攝像頭實(shí)現(xiàn)掃描二維碼
PC攝像機(jī)掃描二維碼的應(yīng)用場(chǎng)景很廣泛,可以應(yīng)用于各種需要快速掃描、識(shí)別和管理的場(chǎng)景,本文就來(lái)具體講講如何用Python實(shí)現(xiàn)這一功能吧2023-05-05Python?web實(shí)戰(zhàn)教程之Django文件上傳和處理詳解
Django和Flask都是Python的Web框架,用于開(kāi)發(fā)Web應(yīng)用程序,這篇文章主要給大家介紹了關(guān)于Python?web實(shí)戰(zhàn)教程之Django文件上傳和處理的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12python flask實(shí)現(xiàn)分頁(yè)效果
這篇文章主要為大家詳細(xì)介紹了python flask實(shí)現(xiàn)分頁(yè)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06