2020-08-08--Pandas-09--分組聚合

import numpy as np
import pandas as pd

index = pd.Index(data=["Tom", "Bob", "Mary", "James", "Andy", "Alice"], name="name")

data = {
    "age": [18, 30, 35, 18, np.nan, 30],
    "city": ["Bei Jing ", "Shang Hai ", "Guang Zhou", "Shen Zhen", np.nan, " "],
    "sex": ["male", "male", "female", "male", np.nan, "female"],
    "income": [3000, 8000, 8000, 4000, 6000, 7000]
}

user_info = pd.DataFrame(data=data, index=index)
print(user_info)
#         age        city     sex  income
# name                                   
# Tom    18.0   Bei Jing     male    3000
# Bob    30.0  Shang Hai     male    8000
# Mary   35.0  Guang Zhou  female    8000
# James  18.0   Shen Zhen    male    4000
# Andy    NaN         NaN     NaN    6000
# Alice  30.0              female    7000

將對(duì)象分割成組

在進(jìn)行分組統(tǒng)計(jì)前谐区,首先要做的就是進(jìn)行分組。既然是分組菩貌,就需要依賴于某個(gè)信息卢佣。
比如,依據(jù)性別來分組箭阶。直接調(diào)用
user_info.groupby(user_info["sex"])或者user_info.groupby("sex")即可完成按照性別分組虚茶。

在該分組的過程中,分組值會(huì)忽略NaN仇参,None嘹叫,NaT。

grouped  = user_info.groupby(user_info["sex"])
print(grouped.groups)
# {'female': ['Mary', 'Alice'], 'male': ['Tom', 'Bob', 'James']}

grouped  = user_info.groupby("sex")
print(grouped.groups)
# {'female': ['Mary', 'Alice'], 'male': ['Tom', 'Bob', 'James']}
print(type(grouped.groups))
# <class 'pandas.io.formats.printing.PrettyDict'>
連續(xù)分組

你可能會(huì)想诈乒,能不能先按照性別來分組罩扇,再按照年齡進(jìn)一步分組呢?答案是可以的

grouped  = user_info.groupby(["sex","age"])
print(grouped.groups)
# {
# ('male', 18.0): ['Tom', 'James'], 
# ('male', 30.0): ['Bob'],
#  ('female', 35.0): ['Mary'], 
# (nan, nan): ['Andy'], 
# ('female', 30.0): ['Alice']
}

grouped是一個(gè)DataFrame分組對(duì)象

關(guān)閉排序

默認(rèn)情況下怕磨,groupby 會(huì)在操作過程中對(duì)數(shù)據(jù)進(jìn)行排序喂饥。如果為了更好的性能,可以設(shè)置 sort=False肠鲫。

grouped  = user_info.groupby(["sex","age"],sort=False)
print(grouped.groups)
# {('male', 18.0): ['Tom', 'James'], ('male', 30.0): ['Bob'], ('female', 35.0): ['Mary'], (nan, nan): ['Andy'], ('female', 30.0): ['Alice']}

選擇列

在使用 groupby 進(jìn)行分組后员帮,可以使用切片 [] 操作來完成對(duì)某一列的選擇。

c = user_info.groupby('sex')
print(c)
# <pandas.core.groupby.generic.DataFrameGroupBy object at 0x000002423A2C9D88>
print(c['city'])
# <pandas.core.groupby.generic.SeriesGroupBy object at 0x000002423A2C9A48>

遍歷分組

在對(duì)數(shù)據(jù)進(jìn)行分組后导饲,可以進(jìn)行遍歷捞高。

grouped  = user_info.groupby("sex")

for name, group in grouped:
    print("name: {}".format(name))
    print("group: {}".format(group))
    print("--------------")
# name: female
# group:         age        city     sex  income
# name
# Mary   35.0  Guang Zhou  female    8000
# Alice  30.0              female    7000
# --------------
# name: male
# group:         age        city   sex  income
# name
# Tom    18.0   Bei Jing   male    3000
# Bob    30.0  Shang Hai   male    8000
# James  18.0   Shen Zhen  male    4000
# --------------

遍歷后每一項(xiàng)的group都是一個(gè)DataFrame對(duì)象,name是分組的名稱渣锦。

如果是根據(jù)多個(gè)字段來分組的硝岗,每個(gè)組的名稱是一個(gè)元組。

grouped  = user_info.groupby(["sex", "age"])

for name, group in grouped:
    print("name: {}".format(name))
    print("group: {}".format(group))
    print("--------------")
# name: ('female', 30.0)
# group:         age city     sex  income
# name                            
# Alice  30.0       female    7000
# --------------
# name: ('female', 35.0)
# group:        age        city     sex  income
# name                                  
# Mary  35.0  Guang Zhou  female    8000
# --------------
# name: ('male', 18.0)
# group:         age       city   sex  income
# name                                
# Tom    18.0  Bei Jing   male    3000
# James  18.0  Shen Zhen  male    4000
# --------------
# name: ('male', 30.0)
# group:        age        city   sex  income
# name                                
# Bob   30.0  Shang Hai   male    8000
# --------------

選擇一個(gè)組

分組后袋毙,我們可以通過 get_group 方法來選擇其中的某一個(gè)組型檀。

1.獲取male分組的數(shù)據(jù),返回DataFrame對(duì)象

grouped  = user_info.groupby("sex")
c = grouped.get_group("male")
print(c)
#         age        city   sex  income
# name                                 
# Tom    18.0   Bei Jing   male    3000
# Bob    30.0  Shang Hai   male    8000
# James  18.0   Shen Zhen  male    4000

2.多個(gè)分組條件時(shí)
獲取male分組以及age=18分組數(shù)據(jù)听盖。

c = user_info.groupby(["sex", "age"]).get_group(("male", 18))
print(c)
#         age       city   sex  income
# name
# Tom    18.0  Bei Jing   male    3000
# James  18.0  Shen Zhen  male    4000

多個(gè)分組條件時(shí)贱除,在選擇一個(gè)分組的時(shí)候,參數(shù)必須完整媳溺,(每個(gè)分組條件的值都要有)

聚合

分組的目的是為了統(tǒng)計(jì)月幌,統(tǒng)計(jì)的時(shí)候需要聚合,所以我們需要在分完組后來看下如何進(jìn)行聚合悬蔽。常見的一些聚合操作有:計(jì)數(shù)扯躺、求和、最大值蝎困、最小值录语、平均值等。

agg()

想要實(shí)現(xiàn)聚合操作禾乘,一種方式就是調(diào)用 agg 方法澎埠。
1.獲取不同分類下所包含的數(shù)量

c = user_info.groupby('sex')
# print(c)
x = c.agg(len)
print(x)
#         age  city  income
# sex
# female  2.0     2       2
# male    3.0     3       3


# 獲取不同性別下所包含的人數(shù)
grouped = user_info.groupby("sex")
c = grouped.agg(len).age
print(c)
# sex
# female    2.0
# male      3.0
# Name: age, dtype: float64

2.獲取不同分類下age的最大值

c = grouped.agg(max).age
print(c)
# sex
# female    35.0
# male      30.0
# Name: age, dtype: float64

3.如果是根據(jù)多個(gè)鍵來進(jìn)行聚合,默認(rèn)情況下得到的結(jié)果是一個(gè)多層索引結(jié)構(gòu)始藕。

grouped = user_info.groupby(["sex", "age"])
rs = grouped.agg(len)
print(rs)
#              city  income
# sex    age
# female 30.0     1       1
#        35.0     1       1
# male   18.0     2       2
#        30.0     1       1
grouped = user_info.groupby(["sex", "age"])
rs = grouped.agg(len)
print(rs)
#              city  income
# sex    age
# female 30.0     1       1
#        35.0     1       1
# male   18.0     2       2
#        30.0     1       1
print(rs.reset_index())
#       sex   age  city  income
# 0  female  30.0     1       1
# 1  female  35.0     1       1
# 2    male  18.0     2       2
# 3    male  30.0     1       1

有兩種方式可以避免出現(xiàn)多層索引蒲稳,上邊是第一種氮趋。對(duì)包含多層索引的對(duì)象調(diào)用 reset_index 方法。

另外一種方式是在分組時(shí)江耀,設(shè)置參數(shù) as_index=False剩胁。

grouped = user_info.groupby(["sex", "age"], as_index=False)
c = grouped.agg(len)
print(c)
#       sex   age  city  income
# 0  female  30.0     1       1
# 1  female  35.0     1       1
# 2    male  18.0     2       2
# 3    male  30.0     1       1

4.Series 和 DataFrame 都包含了 describe 方法,我們分組后一樣可以使用 describe 方法來查看數(shù)據(jù)的情況祥国。

grouped = user_info.groupby("sex")
c = grouped.describe()
print(c)
#          age                        ...  income                        
#        count  mean       std   min  ...     25%     50%     75%     max
# sex                                 ...                                
# female   2.0  32.5  3.535534  30.0  ...  7250.0  7500.0  7750.0  8000.0
# male     3.0  22.0  6.928203  18.0  ...  3500.0  4000.0  6000.0  8000.0

一次應(yīng)用多個(gè)聚合操作

有時(shí)候進(jìn)行分組后昵观,不單單想得到一個(gè)統(tǒng)計(jì)結(jié)果,有可能是多個(gè)舌稀。比如想統(tǒng)計(jì)出不同性別下的一個(gè)收入的總和和平均值啊犬。

grouped = user_info.groupby("sex")
c = grouped['income'].agg([np.sum, np.mean])
print(c)
#           sum  mean
# sex
# female  15000  7500
# male    15000  5000
c = grouped.agg([np.sum, np.mean])
print(c)
#          age       income      
#          sum  mean    sum  mean
# sex                            
# female  65.0  32.5  15000  7500
# male    66.0  22.0  15000  5000

如果想將統(tǒng)計(jì)結(jié)果進(jìn)行重命名,可以傳入字典壁查。

grouped = user_info.groupby("sex")
c = grouped["income"].agg([np.sum, np.mean]).rename(columns={"sum": "income_sum", "mean": "income_mean"})
print(c)
#         income_sum  income_mean
# sex                            
# female       15000         7500
# male         15000         5000

對(duì)DataFrame列應(yīng)用不同的聚合操作

有時(shí)候可能需要對(duì)不同的列使用不同的聚合操作觉至。例如,想要統(tǒng)計(jì)不同性別下人群的年齡的均值以及收入的總和潮罪。

grouped = user_info.groupby("sex")
c = grouped.agg({"age": np.mean, "income": np.sum}).rename(columns={"age": "age_mean", "income": "income_sum"})
print(c)
#         age_mean  income_sum
# sex                         
# female      32.5       15000
# male        22.0       15000

transform 操作

前面進(jìn)行聚合運(yùn)算的時(shí)候康谆,得到的結(jié)果是一個(gè)以分組名作為索引的結(jié)果對(duì)象。雖然可以指定 as_index=False ,但是得到的索引也并不是元數(shù)據(jù)的索引嫉到。如果我們想使用原數(shù)組的索引的話沃暗,就需要進(jìn)行 merge 轉(zhuǎn)換。
transform方法簡(jiǎn)化了這個(gè)過程何恶,它會(huì)把 func 參數(shù)應(yīng)用到所有分組孽锥,然后把結(jié)果放置到原數(shù)組的索引上(如果結(jié)果是一個(gè)標(biāo)量,就進(jìn)行廣播)


# 通過 agg 得到的結(jié)果的索引是分組名
grouped = user_info.groupby("sex")
c = grouped["income"].agg(np.mean)
print(c)
# sex
# female    7500
# male      5000
# Name: income, dtype: int64

# 不改變數(shù)據(jù)的索引细层,但是數(shù)據(jù)的值為平均值
c = grouped['income'].transform(np.mean)
print(c)
# name
# Tom      5000.0
# Bob      5000.0
# Mary     7500.0
# James    5000.0
# Andy        NaN
# Alice    7500.0
# Name: income, dtype: float64

apply 操作

除了 transform 操作外惜辑,還有更神奇的 apply 操作。

apply 會(huì)將待處理的對(duì)象拆分成多個(gè)片段疫赎,然后對(duì)各片段調(diào)用傳入的函數(shù)盛撑,最后嘗試用 pd.concat() 把結(jié)果組合起來。func 的返回值可以是 Pandas 對(duì)象或標(biāo)量捧搞,并且數(shù)組對(duì)象的大小不限抵卫。

1.比如想要統(tǒng)計(jì)不同性別最高收入的前n個(gè)值,可以通過下面這種方式實(shí)現(xiàn)胎撇。

grouped = user_info.groupby("sex")
 
def f1(ser, num=2):
    return ser.nlargest(num).tolist()

c = grouped["income"].apply(f1)
print(c)
# sex
# female    [8000, 7000]
# male      [8000, 4000]
# Name: income, dtype: object

另外介粘,如果想要獲取不同性別下的年齡的均值,通過 apply 可以如下實(shí)現(xiàn)晚树。


apply()是對(duì)其他操作的擴(kuò)展姻采,因?yàn)槭褂胊gg()時(shí),只能使用其內(nèi)置的函數(shù)爵憎,而apply()可以自定義函數(shù)慨亲。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末婚瓜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子巡雨,更是在濱河造成了極大的恐慌闰渔,老刑警劉巖席函,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铐望,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡茂附,警方通過查閱死者的電腦和手機(jī)正蛙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來营曼,“玉大人乒验,你說我怎么就攤上這事〉仝澹” “怎么了锻全?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)录煤。 經(jīng)常有香客問我鳄厌,道長(zhǎng),這世上最難降的妖魔是什么妈踊? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任了嚎,我火速辦了婚禮,結(jié)果婚禮上廊营,老公的妹妹穿的比我還像新娘歪泳。我一直安慰自己,他們只是感情好露筒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布呐伞。 她就那樣靜靜地躺著,像睡著了一般慎式。 火紅的嫁衣襯著肌膚如雪伶氢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天瞬捕,我揣著相機(jī)與錄音鞍历,去河邊找鬼。 笑死肪虎,一個(gè)胖子當(dāng)著我的面吹牛劣砍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扇救,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刑枝,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼香嗓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起装畅,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤靠娱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后掠兄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體像云,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蚂夕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迅诬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侈贷,死狀恐怖等脂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搏屑,我是刑警寧澤露该,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布解幼,位于F島的核電站撵摆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏暑中。R本人自食惡果不足惜鲫剿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一灵莲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧枚抵,春花似錦、人聲如沸汽摹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)圾旨。三九已至魏蔗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廓鞠,已是汗流浹背谣旁。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工榄审, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留搁进,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像莱革,于是被迫代替她去往敵國(guó)和親盅视。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355