Pandas數據分析——超好用的Groupby詳解

微信公衆號:「Python讀財」
若有問題或建議,請公衆號留言

在平常的數據分析中,常常須要將數據根據某個(多個)字段劃分爲不一樣的羣體(group)進行分析,如電商領域將全國的總銷售額根據省份進行劃分,分析各省銷售額的變化狀況,社交領域將用戶根據畫像(性別、年齡)進行細分,研究用戶的使用狀況和偏好等。在Pandas中,上述的數據處理操做主要運用groupby完成,這篇文章就介紹一下groupby的基本原理及對應的aggtransformapply操做。python

爲了後續圖解的方便,採用模擬生成的10個樣本數據,代碼和數據以下:微信

company=["A","B","C"]

data=pd.DataFrame({
    "company":[company[x] for x in np.random.randint(0,len(company),10)],
    "salary":np.random.randint(5,50,10),
    "age":np.random.randint(15,50,10)
}
)
company salary age
0 C 43 35
1 C 17 25
2 C 8 30
3 A 20 22
4 B 10 17
5 B 21 40
6 A 23 33
7 C 49 19
8 B 8 30

1、Groupby的基本原理

在pandas中,實現分組操做的代碼很簡單,僅需一行代碼,在這裏,將上面的數據集按照company字段進行劃分:app

In [5]: group = data.groupby("company")

將上述代碼輸入ipython後,會獲得一個DataFrameGroupBy對象dom

In [6]: group
Out[6]: <pandas.core.groupby.generic.DataFrameGroupBy object at 0x000002B7E2650240>

那這個生成的DataFrameGroupBy是啥呢?對data進行了groupby後發生了什麼?ipython所返回的結果是其內存地址,並不利於直觀地理解,爲了看看group內部到底是什麼,這裏把group轉換成list的形式來看一看:函數

In [8]: list(group)
Out[8]:
[('A',   company  salary  age
  3       A      20   22
  6       A      23   33), 
 ('B',   company  salary  age
  4       B      10   17
  5       B      21   40
  8       B       8   30), 
 ('C',   company  salary  age
  0       C      43   35
  1       C      17   25
  2       C       8   30
  7       C      49   19)]

轉換成列表的形式後,能夠看到,列表由三個元組組成,每一個元組中,第一個元素是組別(這裏是按照company進行分組,因此最後分爲了A,B,C),第二個元素的是對應組別下的DataFrame,整個過程能夠圖解以下:工具

groupby原理.png

總結來講,groupby的過程就是將原有的DataFrame按照groupby的字段(這裏是company),劃分爲若干個分組DataFrame,被分爲多少個組就有多少個分組DataFrame因此說,在groupby以後的一系列操做(如aggapply等),均是基於子DataFrame的操做。理解了這點,也就基本摸清了Pandas中groupby操做的主要原理。下面來說講groupby以後的常見操做。學習

2、agg 聚合操做

聚合操做是groupby後很是常見的操做,會寫SQL的朋友對此應該是很是熟悉了。聚合操做能夠用來求和、均值、最大值、最小值等,下面的表格列出了Pandas中常見的聚合操做。spa

函數 用途
min 最小值
max 最大值
sum 求和
mean 均值
median 中位數
std 標準差
var 方差
count 計數

針對樣例數據集,若是我想求不一樣公司員工的平均年齡和平均薪水,能夠按照下方的代碼進行:code

In [12]: data.groupby("company").agg('mean')
Out[12]:
         salary    age
company
A         21.50  27.50
B         13.00  29.00
C         29.25  27.25

若是想對針對不一樣的列求不一樣的值,好比要計算不一樣公司員工的平均年齡以及薪水的中位數,能夠利用字典進行聚合操做的指定:orm

In [17]: data.groupby('company').agg({'salary':'median','age':'mean'})
Out[17]:
         salary    age
company
A          21.5  27.50
B          10.0  29.00
C          30.0  27.25

agg聚合過程能夠圖解以下(第二個例子爲例):

agg圖解.png

3、transform

transform是一種什麼數據操做?和agg有什麼區別呢?爲了更好地理解transformagg的不一樣,下面從實際的應用場景出發進行對比。

在上面的agg中,咱們學會了如何求不一樣公司員工的平均薪水,若是如今須要在原數據集中新增一列avg_salary,表明員工所在的公司的平均薪水(相同公司的員工具備同樣的平均薪水),該怎麼實現呢?若是按照正常的步驟來計算,須要先求得不一樣公司的平均薪水,而後按照員工和公司的對應關係填充到對應的位置,不用transform的話,實現代碼以下:

In [21]: avg_salary_dict = data.groupby('company')['salary'].mean().to_dict()

In [22]: data['avg_salary'] = data['company'].map(avg_salary_dict)

In [23]: data
Out[23]:
  company  salary  age  avg_salary
0       C      43   35       29.25
1       C      17   25       29.25
2       C       8   30       29.25
3       A      20   22       21.50
4       B      10   17       13.00
5       B      21   40       13.00
6       A      23   33       21.50
7       C      49   19       29.25
8       B       8   30       13.00

若是使用transform的話,僅須要一行代碼:

In [24]: data['avg_salary'] = data.groupby('company')['salary'].transform('mean')

In [25]: data
Out[25]:
  company  salary  age  avg_salary
0       C      43   35       29.25
1       C      17   25       29.25
2       C       8   30       29.25
3       A      20   22       21.50
4       B      10   17       13.00
5       B      21   40       13.00
6       A      23   33       21.50
7       C      49   19       29.25
8       B       8   30       13.00

仍是以圖解的方式來看看進行groupbytransform的實現過程(爲了更直觀展現,圖中加入了company列,實際按照上面的代碼只有salary列):

transform圖解.png

圖中的大方框是transformagg所不同的地方,對agg而言,會計算獲得ABC公司對應的均值並直接返回,但對transform而言,則會對每一條數據求得相應的結果,同一組內的樣本會有相同的值,組內求完均值後會按照原索引的順序返回結果,若是有不理解的能夠拿這張圖和agg那張對比一下。

4、apply

apply應該是你們的老朋友了,它相比aggtransform而言更加靈活,可以傳入任意自定義的函數,實現複雜的數據操做。在Pandas數據處理三板斧——map、apply、applymap詳解
)中,介紹了apply的使用,那在groupby後使用apply和以前所介紹的有什麼區別呢?

區別是有的,可是整個實現原理是基本一致的。二者的區別在於,對於groupby後的apply,以分組後的子DataFrame做爲參數傳入指定函數的,基本操做單位是DataFrame,而以前介紹的apply的基本操做單位是Series。仍是以一個案例來介紹groupby後的apply用法。

假設我如今須要獲取各個公司年齡最大的員工的數據,該怎麼實現呢?能夠用如下代碼實現:

In [38]: def get_oldest_staff(x):
    ...:     df = x.sort_values(by = 'age',ascending=True)
    ...:     return df.iloc[-1,:]
    ...:

In [39]: oldest_staff = data.groupby('company',as_index=False).apply(get_oldest_staff)

In [40]: oldest_staff
Out[40]:
  company  salary  age  
0       A      23   33       
1       B      21   40       
2       C      43   35

這樣便獲得了每一個公司年齡最大的員工的數據,整個流程圖解以下:

apply過程.png

能夠看到,此處的apply和上篇文章中所介紹的做用原理基本一致,只是傳入函數的參數由Series變爲了此處的分組DataFrame

最後,關於apply的使用,這裏有個小建議,雖說apply擁有更大的靈活性,但apply的運行效率會比aggtransform更慢。因此,groupby以後能用aggtransform解決的問題仍是優先使用這兩個方法,實在解決不了了才考慮使用apply進行操做。

掃碼關注公衆號「Python讀財」,第一時間獲取乾貨,還能夠加Python學習交流羣!!
公衆號二維碼.jpg

相關文章
相關標籤/搜索