準(zhǔn)備
這個(gè)博客是用 Jupyter Notebook 寫的, 如果你沒有用過也不影響閱讀哦.
這里只要電腦裝了python和pandas就好, 我們會(huì)先讀入一個(gè)數(shù)據(jù)集.
# 讀入一個(gè)數(shù)據(jù)集, 我使用了美國警方擊斃數(shù)據(jù)集.
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
plt.style.use('ggplot')
path = 'https://raw.githubusercontent.com/HoijanLai/dataset/master/PoliceKillingsUS.csv'
data = pd.read_csv(path, encoding ='latin1')
data.sample(3)
| name | date | race | age | signs_of_mental_illness | flee | |
|---|---|---|---|---|---|---|
| 683 | Tyrone Holman | 09/09/15 | B | 37.0 | True | Not fleeing |
| 1941 | Michael Alan Altice | 25/12/16 | W | 61.0 | True | Not fleeing |
| 652 | Manuel Soriano | 27/08/15 | H | 29.0 | False | Not fleeing |
什么是group by
groupby就是按xx分組, 它也確實(shí)是用來實(shí)現(xiàn)這樣功能的. 比如, 將一個(gè)數(shù)據(jù)集按A進(jìn)行分組, 效果是這樣

我們嘗試使用groupby來嘗試實(shí)現(xiàn)這樣的功能, 不過我們不用A列, 我們將用我們數(shù)據(jù)集里面的"種族"嘗試分組:
data.groupby('race')
<pandas.core.groupby.DataFrameGroupBy object at 0x104fa2208>
這里我們得到了一個(gè)叫DataFrameGroupBy的東西, 雖然 pandas 不讓我們直接看它長啥樣, 但是你將它想象成上面那幅分組后的圖(我手繪的)是完全沒有問題的.
這篇稿主要介紹如何鼓搗這個(gè)
DataFrameGroupBy, 這個(gè)DataFrameGroupBy主要的功能能是允許你在不額外寫循環(huán)的情況下, 快速對(duì)每一組數(shù)據(jù)進(jìn)行操作
基本操作
最基本的就是組內(nèi)計(jì)數(shù), 求和, 求均值, 求方差, 求blablabla...
比如, 要求被不同種族內(nèi)被擊斃人員年齡的均值:
data.groupby('race')['age'].mean()
race
A 36.605263
B 31.635468
H 32.995157
N 30.451613
O 33.071429
W 40.046980
Name: age, dtype: float64
上面我們求得了各個(gè)種族中被擊斃的人員的平均年齡, 得到的是一個(gè)Series, 每一行對(duì)應(yīng)了每一組的mean, 除此之外你還可以換成std, median, min, max這些基本的統(tǒng)計(jì)數(shù)據(jù)
上面age是連續(xù)屬性, 我們還可以操作離散屬性, 比如對(duì)不同取值的計(jì)數(shù): .value_counts()
以下嘗試求不同種族內(nèi), 是否有精神異常跡象的分別有多少人
data.groupby('race')['signs_of_mental_illness'].value_counts()
race signs_of_mental_illness
A False 29
True 10
B False 523
True 95
H False 338
True 85
N False 23
True 8
O False 21
True 7
W False 819
True 382
Name: signs_of_mental_illness, dtype: int64
注: 這時(shí), 組內(nèi)操作的結(jié)果不是單個(gè)值, 是一個(gè)序列, 我們可以用.unstack()將它展開
data.groupby('race')['signs_of_mental_illness'].value_counts().unstack()
| signs_of_mental_illness | False | True |
|---|---|---|
| race | ||
| A | 29 | 10 |
| B | 523 | 95 |
| H | 338 | 85 |
| N | 23 | 8 |
| O | 21 | 7 |
| W | 819 | 382 |
方法總結(jié)
- 首先通過groupby得到
DataFrameGroupBy對(duì)象, 比如data.groupby('race')- 然后選擇需要研究的列, 比如
['age'], 這樣我們就得到了一個(gè)SeriesGroupby, 它代表每一個(gè)組都有一個(gè)Series- 對(duì)
SeriesGroupby進(jìn)行操作, 比如.mean(), 相當(dāng)于對(duì)每個(gè)組的Series求均值注: 如果不選列, 那么第三步的操作會(huì)遍歷所有列, pandas會(huì)對(duì)能成功操作的列進(jìn)行操作, 最后返回的一個(gè)由操作成功的列組成的
DataFrame
更多基本操作
選擇一個(gè)組
不細(xì)講啦, 我自己覺得跟篩選數(shù)據(jù)差不多
可視化
這是我非常喜歡Groupby的一個(gè)地方, 它能夠幫你很輕松地分組畫圖, 免去手寫每個(gè)組的遍歷的煩惱, 還能為你每個(gè)組分好顏色.
場景一: 不同種族中, 逃逸方式分別是如何分布的?
(屬性A的不同分組中, 離散屬性B的情況是怎么樣的 )
- 一種傳統(tǒng)做法是:
- 遍歷每個(gè)組
- 然后篩選不同組的數(shù)據(jù)
- 逐個(gè)子集畫條形圖 (或者其他表示)
races = np.sort(data['race'].dropna().unique())
fig, axes = plt.subplots(1, len(races), figsize=(24, 4), sharey=True)
for ax, race in zip(axes, races):
data[data['race']==race]['flee'].value_counts().sort_index().plot(kind='bar', ax=ax, title=race)

還不錯(cuò), 但是使用Groupby能讓我們直接免去循環(huán), 而且不需要煩人的篩選, 一行就完美搞定
data.groupby('race')['flee'].value_counts().unstack().plot(kind='bar', figsize=(20, 4))

方法總結(jié)
- 首先, 得到分組操作后的結(jié)果
data.groupby('race')['flee'].value_counts()- 這里有一個(gè)之前介紹的
.unstack操作, 這會(huì)讓你得到一個(gè)DateFrame, 然后調(diào)用條形圖, pandas就會(huì)遍歷每一個(gè)組(unstack后為每一行), 然后作各組的條形圖
場景二: 按不同逃逸類型分組, 組內(nèi)的年齡分布是如何的?
(屬性A的不同分組中, 連續(xù)屬性B的情況是怎么樣的)
data.groupby('flee')['age'].plot(kind='kde', legend=True, figsize=(20, 5))

方法總結(jié)
這里
data.groupby('flee')['age']是一個(gè)SeriesGroupby對(duì)象, 顧名思義, 就是每一個(gè)組都有一個(gè)Series. 因?yàn)閯澐至瞬煌右蓊愋偷慕M, 每一組包含了組內(nèi)的年齡數(shù)據(jù), 所以直接plot相當(dāng)于遍歷了每一個(gè)逃逸類型, 然后分別畫分布圖.pandas 會(huì)為不同組的作圖分配顏色, 非常方便
高級(jí)操作
場景三: 有時(shí)我們需要對(duì)組內(nèi)不同列采取不同的操作
比如說, 我們按flee分組, 但是我們需要對(duì)每一組中的年齡求中位數(shù), 對(duì)是否有精神問題求占比
這時(shí)我們可以這樣做
data.groupby('race').agg({'age': np.median, 'signs_of_mental_illness': np.mean})
| age | signs_of_mental_illness | |
|---|---|---|
| race | ||
| A | 35.0 | 0.256410 |
| B | 30.0 | 0.153722 |
| H | 31.0 | 0.200946 |
| N | 29.0 | 0.258065 |
| O | 29.5 | 0.250000 |
| W | 38.0 | 0.318068 |
方法總結(jié)
這里我們操作的data.groupby('race')是一個(gè)DataFrameGroupby, 也就是說, 每一組都有一個(gè)DataFrame我們把對(duì)這些
DataFrame的操作計(jì)劃寫成了了一個(gè)字典{'age': np.median, 'signs_of_mental_illness': np.mean}, 然后進(jìn)行agg, (aggragate, 合計(jì))然后我們得到了一個(gè)
DataFrame, 每行對(duì)應(yīng)一個(gè)組, 沒列對(duì)應(yīng)各組DataFrame的合計(jì)信息, 比如第二行第一列表示, 黑人被擊斃者中, 年齡的中位數(shù)是30, 第二行第二列表示, 黑人被擊斃者中, 有精神疾病表現(xiàn)的占15%
場景四: 我們需要同時(shí)求不同組內(nèi), 年齡的均值, 中位數(shù), 方差
data.groupby('flee')['age'].agg([np.mean, np.median, np.std])
| mean | median | std | |
|---|---|---|---|
| flee | |||
| Car | 33.911765 | 33.0 | 11.174234 |
| Foot | 30.972222 | 30.0 | 10.193900 |
| Not fleeing | 38.334753 | 36.0 | 13.527702 |
| Other | 33.239130 | 33.0 | 9.932043 |
方法總結(jié)
現(xiàn)在我們對(duì)一個(gè)
SeriesGroupby同時(shí)進(jìn)行了多種操作. 相當(dāng)于同時(shí)得到了這三行的結(jié)果:
data.groupby('flee')['age'].mean()
data.groupby('flee')['age'].median()
data.groupby('flee')['age'].std()
所以這其實(shí)是基本操作部分的進(jìn)階
場景五: 結(jié)合場景三和場景四可以嗎?
答案是肯定的, 請看
data.groupby('flee').agg({'age': [np.median, np.mean], 'signs_of_mental_illness': np.mean})
| age | signs_of_mental_illness_mean | ||
|---|---|---|---|
| flee | median | mean | mean |
| Car | 33.0 | 33.911765 | 0.114286 |
| Foot | 30.0 | 30.972222 | 0.115646 |
| Not fleeing | 36.0 | 38.334753 | 0.319174 |
| Other | 33.0 | 33.239130 | 0.072917 |
但是這里有一個(gè)問題, 這個(gè)列名分了很多層級(jí), 我們可以進(jìn)行重命名:
agg_df = data.groupby('flee').agg({'age': [np.median, np.mean], 'signs_of_mental_illness': np.mean})
agg_df.columns = ['_'.join(col).strip() for col in agg_df.columns.values]
agg_df
| age_median | age_mean | signs_of_mental_illness_mean | |
|---|---|---|---|
| flee | |||
| Car | 33.0 | 33.911765 | 0.114286 |
| Foot | 30.0 | 30.972222 | 0.115646 |
| Not fleeing | 36.0 | 38.334753 | 0.319174 |
| Other | 33.0 | 33.239130 | 0.072917 |
方法總結(jié)
注意這里agg接受的不一定是np.mean這些函數(shù), 你還可以進(jìn)行自定義函數(shù)哦
總結(jié)
Groupby 可以簡單總結(jié)為 split, apply, combine, 也就是說:
-
split : 先將數(shù)據(jù)按一個(gè)屬性分組 (得到
DataFrameGroupby/SeriesGroupby) - apply : 對(duì)每一組數(shù)據(jù)進(jìn)行操作 (取平均 取中值 取方差 或 自定義函數(shù))
-
combine: 將操作后的結(jié)果結(jié)合起來 (得到一個(gè)
DataFrame或Series或可視化圖像)
希望看完本文你已經(jīng)對(duì)groupby的使用有清晰的印象, 并充滿信心, 如果你需要更細(xì)致的微操作, 多屬性Groupby等, 可以進(jìn)一步閱讀文檔
??~