1. 程式人生 > >python處理數據的風騷操作[pandas 之 groupby&agg]

python處理數據的風騷操作[pandas 之 groupby&agg]

ebo 一段 插入 date 模塊 就會 交互工具 1.3 3.x

https://segmentfault.com/a/1190000012394176

介紹

每隔一段時間我都會去學習、回顧一下python中的新函數、新操作。這對於你後面的工作是有一定好處的。
本文重點介紹了pandas中groupby、Grouper和agg函數的使用。這2個函數作用類似,都是對數據集中的一類屬性進行聚合操作,比如統計一個用戶在每個月內的全部花銷,統計某個屬性的最大、最小、累和、平均等數值。

其中,agg是pandas 0.20新引入的功能

groupby && Grouper

首先,我們從網上把數據下載下來,後面的操作都是基於這份數據的:

import pandas as pd

df = pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/sample-salesv3.xlsx?raw=True")
df["date"] = pd.to_datetime(df[‘date‘])
df.head()

技術分享圖片
(圖片來自於jupyter notebook,強烈推薦使用它作為python的交互工具)

下面,我們統計‘ext price‘這個屬性在每個月的累和(sum)值,resample 只有在index為date類型的時候才能用:

df.set_index(‘date‘).resample(‘M‘)[‘ext price‘].sum()
date
2014-01-31    185361.66
2014-02-28    146211.62
2014-03-31    203921.38
2014-04-30    174574.11
2014-05-31    165418.55
2014-06-30    174089.33
2014-07-31    191662.11
2014-08-31    153778.59
2014-09-30    168443.17
2014-10-31    171495.32
2014-11-30    119961.22
2014-12-31    163867.26
Freq: M, Name: ext price, dtype: float64

進一步的,我們想知道每個用戶每個月的sum值,那麽就需要一個groupby了:

df.set_index(‘date‘).groupby(‘name‘)[‘ext price‘].resample("M").sum()
name                             date      
Barton LLC                       2014-01-31     6177.57
                                 2014-02-28    12218.03
                                 2014-03-31     3513.53
                                 2014-04-30    11474.20
                                 2014-05-31    10220.17
                                 2014-06-30    10463.73
                                 2014-07-31     6750.48
                                 2014-08-31    17541.46
                                 2014-09-30    14053.61
                                 2014-10-31     9351.68
                                 2014-11-30     4901.14
                                 2014-12-31     2772.90
Cronin, Oberbrunner and Spencer  2014-01-31     1141.75
                                 2014-02-28    13976.26
                                 2014-03-31    11691.62
                                 2014-04-30     3685.44
                                 2014-05-31     6760.11
                                 2014-06-30     5379.67
                                 2014-07-31     6020.30
                                 2014-08-31     5399.58
                                 2014-09-30    12693.74
                                 2014-10-31     9324.37
                                 2014-11-30     6021.11
                                 2014-12-31     7640.60
Frami, Hills and Schmidt         2014-01-31     5112.34
                                 2014-02-28     4124.53
                                 2014-03-31    10397.44
                                 2014-04-30     5036.18
                                 2014-05-31     4097.87
                                 2014-06-30    13192.19
                                                 ...   
Trantow-Barrows                  2014-07-31    11987.34
                                 2014-08-31    17251.65
                                 2014-09-30     6992.48
                                 2014-10-31    10064.27
                                 2014-11-30     6550.10
                                 2014-12-31    10124.23
White-Trantow                    2014-01-31    13703.77
                                 2014-02-28    11783.98
                                 2014-03-31     8583.05
                                 2014-04-30    19009.20
                                 2014-05-31     5877.29
                                 2014-06-30    14791.32
                                 2014-07-31    10242.62
                                 2014-08-31    12287.21
                                 2014-09-30     5315.16
                                 2014-10-31    19896.85
                                 2014-11-30     9544.61
                                 2014-12-31     4806.93
Will LLC                         2014-01-31    20953.87
                                 2014-02-28    13613.06
                                 2014-03-31     9838.93
                                 2014-04-30     6094.94
                                 2014-05-31    11856.95
                                 2014-06-30     2419.52
                                 2014-07-31    11017.54
                                 2014-08-31     1439.82
                                 2014-09-30     4345.99
                                 2014-10-31     7085.33
                                 2014-11-30     3210.44
                                 2014-12-31    12561.21
Name: ext price, Length: 240, dtype: float64

結果肯定是對的,但是不夠完美。我們可以使用Grouper寫得更加簡潔:

# df.set_index(‘date‘).groupby(‘name‘)[‘ext price‘].resample("M").sum()
df.groupby([‘name‘, pd.Grouper(key=‘date‘, freq=‘M‘)])[‘ext price‘].sum()

結果和上面??一樣,就不列出來了。
顯然,這種寫法多敲了很多次鍵盤,那麽它的好處是啥呢?
首先,邏輯上更加直接,當你敲代碼完成以上統計的時候,你首先想到的就是groupby操作,而set_index, resample好像不會立馬想到。想到了groupby這個‘動作‘之後,你就會緊接著想按照哪個key來操作,此時
你只需要用字符串,或者Grouper把key定義好就行了。最後使用聚合函數,就得到了結果。所以,從人類的
思考角度看,後者更容易記憶。

另外,Grouper裏的freq可以方便的改成其他周期參數(resample也可以),比如:

# 按照年度,且截止到12月最後一天統計ext price的sum值
df.groupby([‘name‘, pd.Grouper(key=‘date‘, freq=‘A-DEC‘)])[‘ext price‘].sum()
name                             date      
Barton LLC                       2014-12-31    109438.50
Cronin, Oberbrunner and Spencer  2014-12-31     89734.55
Frami, Hills and Schmidt         2014-12-31    103569.59
Fritsch, Russel and Anderson     2014-12-31    112214.71
Halvorson, Crona and Champlin    2014-12-31     70004.36
Herman LLC                       2014-12-31     82865.00
Jerde-Hilpert                    2014-12-31    112591.43
Kassulke, Ondricka and Metz      2014-12-31     86451.07
Keeling LLC                      2014-12-31    100934.30
Kiehn-Spinka                     2014-12-31     99608.77
Koepp Ltd                        2014-12-31    103660.54
Kuhn-Gusikowski                  2014-12-31     91094.28
Kulas Inc                        2014-12-31    137351.96
Pollich LLC                      2014-12-31     87347.18
Purdy-Kunde                      2014-12-31     77898.21
Sanford and Sons                 2014-12-31     98822.98
Stokes LLC                       2014-12-31     91535.92
Trantow-Barrows                  2014-12-31    123381.38
White-Trantow                    2014-12-31    135841.99
Will LLC                         2014-12-31    104437.60
Name: ext price, dtype: float64

agg

從0.20.1開始,pandas引入了agg函數,它提供基於列的聚合操作。而groupby可以看做是基於行,或者說index的聚合操作。

從實現上看,groupby返回的是一個DataFrameGroupBy結構,這個結構必須調用聚合函數(如sum)之後,才會得到結構為Series的數據結果。
而agg是DataFrame的直接方法,返回的也是一個DataFrame。當然,很多功能用sum、mean等等也可以實現。但是agg更加簡潔, 而且傳給它的函數可以是字符串,也可以自定義,參數是column對應的子DataFrame

舉個栗子??吧:

df[["ext price", "quantity", "unit price"]].agg([‘sum‘, ‘mean‘])

技術分享圖片

怎麽樣,是不是比使用

df[["ext price", "quantity"]].sum()
df[‘unit price‘].mean()

簡潔多了?

上例中,你還可以針對不同的列使用不同的聚合函數:

df.agg({‘ext price‘: [‘sum‘, ‘mean‘], ‘quantity‘: [‘sum‘, ‘mean‘], ‘unit price‘: [‘mean‘]})

技術分享圖片

另外,自定義函數怎麽用呢,也是so easy.
比如,我想統計sku中,購買次數最多的產品編號,可以這樣做:

# 這裏的x是sku對應的column
get_max = lambda x: x.value_counts(dropna=False).index[0]
df.agg({‘ext price‘: [‘sum‘, ‘mean‘], 
        ‘quantity‘: [‘sum‘, ‘mean‘], 
        ‘unit price‘: [‘mean‘], 
        ‘sku‘: [get_max]})

技術分享圖片

<lambda>看起來很不協調,把它去掉:

get_max = lambda x: x.value_counts(dropna=False).index[0]
# python就是靈活啊。
get_max.__name__ = "most frequent"
df.agg({‘ext price‘: [‘sum‘, ‘mean‘], 
        ‘quantity‘: [‘sum‘, ‘mean‘], 
        ‘unit price‘: [‘mean‘], 
        ‘sku‘: [get_max]})

另外,還有一個小問題,那就是如果你希望輸出的列按照某個順序排列,可以使用collections的OrderedDict:

get_max = lambda x: x.value_counts(dropna=False).index[0]
get_max.__name__ = "most frequent"
import collections

agg_dict = {
        ‘ext price‘: [‘sum‘, ‘mean‘], 
        ‘quantity‘: [‘sum‘, ‘mean‘], 
        ‘unit price‘: [‘mean‘], 
        ‘sku‘: [get_max]}
# 按照列名的長度排序。 OrderedDict的順序是跟插入順序一致的
df.agg(collections.OrderedDict(sorted(agg_dict.items(), key = lambda x: len(x[0]))))

技術分享圖片

總結

每隔一段時間我都會去學習、回顧一下python中的新函數、新操作。這對於你後面的工作是有一定好處的。
本文重點介紹了pandas中groupby、Grouper和agg函數的使用。這2個函數作用類似,都是對數據集中的一類屬性進行聚合操作,比如統計一個用戶在每個月內的全部花銷,統計某個屬性的最大、最小、累和、平均等數值。

其中,agg是pandas 0.20新引入的功能

groupby && Grouper

首先,我們從網上把數據下載下來,後面的操作都是基於這份數據的:

import pandas as pd

df = pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/sample-salesv3.xlsx?raw=True")
df["date"] = pd.to_datetime(df[‘date‘])
df.head()

技術分享圖片
(圖片來自於jupyter notebook,強烈推薦使用它作為python的交互工具)

下面,我們統計‘ext price‘這個屬性在每個月的累和(sum)值,resample 只有在index為date類型的時候才能用:

df.set_index(‘date‘).resample(‘M‘)[‘ext price‘].sum()
date
2014-01-31    185361.66
2014-02-28    146211.62
2014-03-31    203921.38
2014-04-30    174574.11
2014-05-31    165418.55
2014-06-30    174089.33
2014-07-31    191662.11
2014-08-31    153778.59
2014-09-30    168443.17
2014-10-31    171495.32
2014-11-30    119961.22
2014-12-31    163867.26
Freq: M, Name: ext price, dtype: float64

進一步的,我們想知道每個用戶每個月的sum值,那麽就需要一個groupby了:

df.set_index(‘date‘).groupby(‘name‘)[‘ext price‘].resample("M").sum()
name                             date      
Barton LLC                       2014-01-31     6177.57
                                 2014-02-28    12218.03
                                 2014-03-31     3513.53
                                 2014-04-30    11474.20
                                 2014-05-31    10220.17
                                 2014-06-30    10463.73
                                 2014-07-31     6750.48
                                 2014-08-31    17541.46
                                 2014-09-30    14053.61
                                 2014-10-31     9351.68
                                 2014-11-30     4901.14
                                 2014-12-31     2772.90
Cronin, Oberbrunner and Spencer  2014-01-31     1141.75
                                 2014-02-28    13976.26
                                 2014-03-31    11691.62
                                 2014-04-30     3685.44
                                 2014-05-31     6760.11
                                 2014-06-30     5379.67
                                 2014-07-31     6020.30
                                 2014-08-31     5399.58
                                 2014-09-30    12693.74
                                 2014-10-31     9324.37
                                 2014-11-30     6021.11
                                 2014-12-31     7640.60
Frami, Hills and Schmidt         2014-01-31     5112.34
                                 2014-02-28     4124.53
                                 2014-03-31    10397.44
                                 2014-04-30     5036.18
                                 2014-05-31     4097.87
                                 2014-06-30    13192.19
                                                 ...   
Trantow-Barrows                  2014-07-31    11987.34
                                 2014-08-31    17251.65
                                 2014-09-30     6992.48
                                 2014-10-31    10064.27
                                 2014-11-30     6550.10
                                 2014-12-31    10124.23
White-Trantow                    2014-01-31    13703.77
                                 2014-02-28    11783.98
                                 2014-03-31     8583.05
                                 2014-04-30    19009.20
                                 2014-05-31     5877.29
                                 2014-06-30    14791.32
                                 2014-07-31    10242.62
                                 2014-08-31    12287.21
                                 2014-09-30     5315.16
                                 2014-10-31    19896.85
                                 2014-11-30     9544.61
                                 2014-12-31     4806.93
Will LLC                         2014-01-31    20953.87
                                 2014-02-28    13613.06
                                 2014-03-31     9838.93
                                 2014-04-30     6094.94
                                 2014-05-31    11856.95
                                 2014-06-30     2419.52
                                 2014-07-31    11017.54
                                 2014-08-31     1439.82
                                 2014-09-30     4345.99
                                 2014-10-31     7085.33
                                 2014-11-30     3210.44
                                 2014-12-31    12561.21
Name: ext price, Length: 240, dtype: float64

結果肯定是對的,但是不夠完美。我們可以使用Grouper寫得更加簡潔:

# df.set_index(‘date‘).groupby(‘name‘)[‘ext price‘].resample("M").sum()
df.groupby([‘name‘, pd.Grouper(key=‘date‘, freq=‘M‘)])[‘ext price‘].sum()

結果和上面??一樣,就不列出來了。
顯然,這種寫法多敲了很多次鍵盤,那麽它的好處是啥呢?
首先,邏輯上更加直接,當你敲代碼完成以上統計的時候,你首先想到的就是groupby操作,而set_index, resample好像不會立馬想到。想到了groupby這個‘動作‘之後,你就會緊接著想按照哪個key來操作,此時
你只需要用字符串,或者Grouper把key定義好就行了。最後使用聚合函數,就得到了結果。所以,從人類的
思考角度看,後者更容易記憶。

另外,Grouper裏的freq可以方便的改成其他周期參數(resample也可以),比如:

# 按照年度,且截止到12月最後一天統計ext price的sum值
df.groupby([‘name‘, pd.Grouper(key=‘date‘, freq=‘A-DEC‘)])[‘ext price‘].sum()
name                             date      
Barton LLC                       2014-12-31    109438.50
Cronin, Oberbrunner and Spencer  2014-12-31     89734.55
Frami, Hills and Schmidt         2014-12-31    103569.59
Fritsch, Russel and Anderson     2014-12-31    112214.71
Halvorson, Crona and Champlin    2014-12-31     70004.36
Herman LLC                       2014-12-31     82865.00
Jerde-Hilpert                    2014-12-31    112591.43
Kassulke, Ondricka and Metz      2014-12-31     86451.07
Keeling LLC                      2014-12-31    100934.30
Kiehn-Spinka                     2014-12-31     99608.77
Koepp Ltd                        2014-12-31    103660.54
Kuhn-Gusikowski                  2014-12-31     91094.28
Kulas Inc                        2014-12-31    137351.96
Pollich LLC                      2014-12-31     87347.18
Purdy-Kunde                      2014-12-31     77898.21
Sanford and Sons                 2014-12-31     98822.98
Stokes LLC                       2014-12-31     91535.92
Trantow-Barrows                  2014-12-31    123381.38
White-Trantow                    2014-12-31    135841.99
Will LLC                         2014-12-31    104437.60
Name: ext price, dtype: float64

agg

從0.20.1開始,pandas引入了agg函數,它提供基於列的聚合操作。而groupby可以看做是基於行,或者說index的聚合操作。

從實現上看,groupby返回的是一個DataFrameGroupBy結構,這個結構必須調用聚合函數(如sum)之後,才會得到結構為Series的數據結果。
而agg是DataFrame的直接方法,返回的也是一個DataFrame。當然,很多功能用sum、mean等等也可以實現。但是agg更加簡潔, 而且傳給它的函數可以是字符串,也可以自定義,參數是column對應的子DataFrame

舉個栗子??吧:

df[["ext price", "quantity", "unit price"]].agg([‘sum‘, ‘mean‘])

技術分享圖片

怎麽樣,是不是比使用

df[["ext price", "quantity"]].sum()
df[‘unit price‘].mean()

簡潔多了?

上例中,你還可以針對不同的列使用不同的聚合函數:

df.agg({‘ext price‘: [‘sum‘, ‘mean‘], ‘quantity‘: [‘sum‘, ‘mean‘], ‘unit price‘: [‘mean‘]})

技術分享圖片

另外,自定義函數怎麽用呢,也是so easy.
比如,我想統計sku中,購買次數最多的產品編號,可以這樣做:

# 這裏的x是sku對應的column
get_max = lambda x: x.value_counts(dropna=False).index[0]
df.agg({‘ext price‘: [‘sum‘, ‘mean‘], 
        ‘quantity‘: [‘sum‘, ‘mean‘], 
        ‘unit price‘: [‘mean‘], 
        ‘sku‘: [get_max]})

技術分享圖片

<lambda>看起來很不協調,把它去掉:

get_max = lambda x: x.value_counts(dropna=False).index[0]
# python就是靈活啊。
get_max.__name__ = "most frequent"
df.agg({‘ext price‘: [‘sum‘, ‘mean‘], 
        ‘quantity‘: [‘sum‘, ‘mean‘], 
        ‘unit price‘: [‘mean‘], 
        ‘sku‘: [get_max]})

另外,還有一個小問題,那就是如果你希望輸出的列按照某個順序排列,可以使用collections的OrderedDict:

get_max = lambda x: x.value_counts(dropna=False).index[0]
get_max.__name__ = "most frequent"
import collections

agg_dict = {
        ‘ext price‘: [‘sum‘, ‘mean‘], 
        ‘quantity‘: [‘sum‘, ‘mean‘], 
        ‘unit price‘: [‘mean‘], 
        ‘sku‘: [get_max]}
# 按照列名的長度排序。 OrderedDict的順序是跟插入順序一致的
df.agg(collections.OrderedDict(sorted(agg_dict.items(), key = lambda x: len(x[0]))))

技術分享圖片

總結

Python random模塊

http://www.cnblogs.com/learnC/p/5981638.html

利用python進行數據分析之數據聚合和分組運算

https://www.cnblogs.com/splended/p/5278078.html

標準差

https://baike.baidu.com/item/%E6%A0%87%E5%87%86%E5%B7%AE/1415772?fr=aladdin

python處理數據的風騷操作[pandas 之 groupby&agg]