pandas100個(gè)騷操作:groupby 8 個(gè)常用技巧!

pandas的groupby是數(shù)據(jù)處理中一個(gè)非常強(qiáng)大的功能。雖然很多同學(xué)已已經(jīng)非常熟悉了,但有些小技巧還是要和大家普及一下的。import pandas as pd
iris = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')
隨機(jī)采樣5條,數(shù)據(jù)是長(zhǎng)這樣子的。
>>> iris.sample(5)
sepal_length sepal_width petal_length petal_width species
95 5.7 3.0 4.2 1.2 versicolor
71 6.1 2.8 4.0 1.3 versicolor
133 6.3 2.8 5.1 1.5 virginica
4 5.0 3.6 1.4 0.2 setosa
33 5.5 4.2 1.4 0.2 setosa
因?yàn)槭欠纸M功能,所以被分的對(duì)象肯定是類別型的。在這個(gè)數(shù)據(jù)里,這里我們就以species進(jìn)行分組舉例。
首先,以species分組創(chuàng)建一個(gè)groupby的object。這里單獨(dú)生成groupby對(duì)象是因?yàn)楹竺鏁?huì)反復(fù)用到,其實(shí)用的熟練了直接鏈接起來(lái)就可以了。
iris_gb = iris.groupby('species')
一、創(chuàng)建頻率表
假如我想知道每個(gè)species類中的數(shù)量有多少,那么直接使用groupby的size函數(shù)即可,如下。
>>> iris_gb.size()
species
setosa 50
versicolor 50
virginica 50
dtype: int64
二、計(jì)算常用的描述統(tǒng)計(jì)量
比如,我想要按組計(jì)算均值,那么就用mean()函數(shù)。
>>> # 計(jì)算均值
>>> iris_gb.mean()
sepal_length sepal_width petal_length petal_width
species
setosa 5.006 3.428 1.462 0.246
versicolor 5.936 2.770 4.260 1.326
virginica 6.588 2.974 5.552 2.026
默認(rèn)情況下如果沒(méi)有限制,那么mean()函數(shù)將對(duì)所有變量特征計(jì)算均值。如果我希望只計(jì)算某一個(gè)變量的均值,可以指定該變量,如下所示。
>>> # 單列
>>> iris_gb['sepal_length'].mean()
species
setosa 5.006
versicolor 5.936
virginica 6.588
Name: sepal_length, dtype: float64
>>> # 雙列
>>> iris_gb[['sepal_length', 'petal_length']].mean()
sepal_length petal_length
species
setosa 5.006 1.462
versicolor 5.936 4.260
virginica 6.588 5.552
同理,其它描述性統(tǒng)計(jì)量min、max()、medianhe和std都是一樣的用法。
三、查找最大值(最小值)索引
如果我們要查找每個(gè)組的最大值或最小值的索引時(shí),有一個(gè)方便的功能可以直接使用。
>>> iris_gb.idxmax()
sepal_length sepal_width petal_length petal_width
species
setosa 14 15 24 43
versicolor 50 85 83 70
virginica 131 117 118 100
如何應(yīng)用呢?
比如我們想查找每組sepal_length最大值對(duì)應(yīng)的整條記錄時(shí),就可以這樣用。注意,這里是整條記錄,相當(dāng)于按sepal_length最大值這個(gè)條件進(jìn)行了篩選。
>>> sepal_largest = iris.loc[iris_gb['sepal_length'].idxmax()]
>>> sepal_largest
sepal_length sepal_width petal_length petal_width species
14 5.8 4.0 1.2 0.2 setosa
50 7.0 3.2 4.7 1.4 versicolor
131 7.9 3.8 6.4 2.0 virginica
四、Groupby之后重置索引
很多時(shí)候,我們?cè)?code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(8, 138, 41);font-size: 13px;">groupby處理后還要進(jìn)行其他操作。也就是說(shuō),我們想重置分組索引以使其成為正常的行和列。
第一種方法可能大家常用,就是通過(guò)reset_index()讓亂序索引重置。
>>> iris_gb.max().reset_index()
species sepal_length sepal_width petal_length petal_width
0 setosa 5.8 4.4 1.9 0.6
1 versicolor 7.0 3.4 5.1 1.8
2 virginica 7.9 3.8 6.9 2.5
但其實(shí),還有一個(gè)看上去更加友好的用法。可以在groupby的時(shí)候就設(shè)置as_index參數(shù),也可以達(dá)到同樣效果。
>>> iris.groupby('species', as_index=False).max()
species sepal_length sepal_width petal_length petal_width
0 setosa 5.8 4.4 1.9 0.6
1 versicolor 7.0 3.4 5.1 1.8
2 virginica 7.9 3.8 6.9 2.5
五、多種統(tǒng)計(jì)量匯總
上面都是單個(gè)統(tǒng)計(jì)量的操作,那如果我想同時(shí)操作好幾個(gè)呢?
groupby還有一個(gè)超級(jí)棒的用法就是和聚合函數(shù)agg連起來(lái)使用。
>>> iris_gb[['sepal_length', 'petal_length']].agg(["min", "mean"])
sepal_length petal_length
min mean min mean
species
setosa 4.3 5.006 1.0 1.462
versicolor 4.9 5.936 3.0 4.260
virginica 4.9 6.588 4.5 5.552
在agg里面,我們只要列出統(tǒng)計(jì)量的名稱即可,便可同時(shí)對(duì)每個(gè)列進(jìn)行多維度統(tǒng)計(jì)。
六、特定列的聚合
我們也看到了,上面是的多個(gè)操作對(duì)于每個(gè)列都是一樣的。實(shí)際使用過(guò)程中,我們可能對(duì)于每個(gè)列的需求都是不一樣的。
所以在這種情況下,我們可以通過(guò)為不同的列單獨(dú)設(shè)置不同的統(tǒng)計(jì)量。
>>> iris_gb.agg({"sepal_length": ["min", "max"], "petal_length": ["mean", "std"]})
sepal_length petal_length
min max mean std
species
setosa 4.3 5.8 1.462 0.173664
versicolor 4.9 7.0 4.260 0.469911
virginica 4.9 7.9 5.552 0.551895
7、NamedAgg命名統(tǒng)計(jì)量
現(xiàn)在我又有新的想法了。上面的多級(jí)索引看起來(lái)有點(diǎn)不太友好,我想把每個(gè)列下面的統(tǒng)計(jì)量和列名分別合并起來(lái)。可以使用NamedAgg來(lái)完成列的命名。
>>> iris_gb.agg(
... sepal_min=pd.NamedAgg(column="sepal_length", aggfunc="min"),
... sepal_max=pd.NamedAgg(column="sepal_length", aggfunc="max"),
... petal_mean=pd.NamedAgg(column="petal_length", aggfunc="mean"),
... petal_std=pd.NamedAgg(column="petal_length", aggfunc="std")
... )
sepal_min sepal_max petal_mean petal_std
species
setosa 4.3 5.8 1.462 0.173664
versicolor 4.9 7.0 4.260 0.469911
virginica 4.9 7.9 5.552 0.551895
因?yàn)?code style="font-size: 13px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(8, 138, 41);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">NamedAgg是一個(gè)元組,所以我們也可以直接賦值元組給新的命名,效果一樣,但看上去更簡(jiǎn)潔。
iris_gb.agg(
sepal_min=("sepal_length", "min"),
sepal_max=("sepal_length", "max"),
petal_mean=("petal_length", "mean"),
petal_std=("petal_length", "std")
)八、使用自定義函數(shù)
上面agg聚合函數(shù)中我們都是通過(guò)添加一個(gè)統(tǒng)計(jì)量名稱來(lái)完成操作的,除此之外我們也可直接給一個(gè)功能對(duì)象。
>>> iris_gb.agg(pd.Series.mean)
sepal_length sepal_width petal_length petal_width
species
setosa 5.006 3.428 1.462 0.246
versicolor 5.936 2.770 4.260 1.326
virginica 6.588 2.974 5.552 2.026
不僅如此,名稱和功能對(duì)象也可一起使用。
iris_gb.agg(["min", pd.Series.mean])
更騷的是,我們還可以自定義函數(shù),也都是可以的。
>>> def double_length(x):
... return 2*x.mean()
...
>>> iris_gb.agg(double_length)
sepal_length sepal_width petal_length petal_width
species
setosa 10.012 6.856 2.924 0.492
versicolor 11.872 5.540 8.520 2.652
virginica 13.176 5.948 11.104 4.052
當(dāng)然如果想更簡(jiǎn)潔,也可以使用lambda函數(shù)。總之,用法非常靈活,可以自由組合搭配。
iris_gb.agg(lambda x: x.mean())
groupby過(guò)程中可能會(huì)用到的8個(gè)操作,如果你熟練使用起來(lái)會(huì)發(fā)現(xiàn)這個(gè)功能是真的很強(qiáng)大。
