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ù)慨亲。