前言
Python的pandas包提供的數據聚合與分組運算功能很強大,也很靈活。《Python for Data Analysis》這本書第9章詳細的介紹了這方面的用法,但是有些細節不常用就容易忘記,遂打算把書中這部分內容總結在博客里,以便復習查看。根據書中的章節,這部分知識包括以下四部分:
1.GroupBy Mechanics(groupby技術)
2.Data Aggregation(數據聚合)
3.Group-wise Operation and Transformation(分組級運算和轉換)
4.Pivot Tables and Cross-Tabulation(透視表和交叉表)
本文是第一部分,介紹groupby技術。
一、分組原理
核心:
1.不論分組鍵是數組、列表、字典、Series、函數,只要其與待分組變量的軸長度一致都可以傳入groupby進行分組。
2.默認axis=0按行分組,可指定axis=1對列分組。
對數據進行分組操作的過程可以概括為:split-apply-combine三步:
1.按照鍵值(key)或者分組變量將數據分組。
2.對於每組應用我們的函數,這一步非常靈活,可以是python自帶函數,可以是我們自己編寫的函數。
3.將函數計算后的結果聚合。
圖1:分組聚合原理(圖片來自《Python for Data Analysis》page 252)
import pandas as pd import numpy as np df = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'], 'key2' : ['one', 'two', 'one', 'two', 'one'], 'data1' : np.random.randn(5), 'data2' : np.random.randn(5)})
我們將key1當做我們的分組鍵值,對data1進行分組,再求每組的均值:
grouped = df['data1'].groupby(df['key1'])
語法很簡單,但是這里需要注意grouped的數據類型,它不在是一個數據框,而是一個GroupBy對象。
grouped
實際上,在這一步,我們並沒有進行任何計算僅僅是創建用key1分組后創建了一個GroupBy對象,我們后面函數的任何操作都是基於這個對象的。
求均值:
grouped.mean()
剛剛我們只是用了key1進行了分組,我們也可以使用兩個分組變量,並且通過unstack方法進行結果重塑:
means = df['data1'].groupby([df['key1'], df['key2']]).mean()
means
means.unstack
以上我們的分組變量都是df內部的Series,實際上只要是和key1等長的數組也可以:
states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio']) years = np.array([2005, 2005, 2006, 2005, 2006]) df['data1'].groupby([states, years]).mean()
二、對分組進行迭代
GroupBy對象支持迭代操作,會產生一個由分組變量名和數據塊組成的二元元組:
for name, group in df.groupby('key1'): print name print group
如果分組變量有兩個:
for (k1,k2), group in df.groupby(['key1','key2']): print k1,k2 print group
我們可以將上面的結果轉化為list或者dict,來看看結果是什么樣的:
list(df.groupby(['key1','key2']))
看不太清楚,我們來看看這個列表的第一個元素:
list(df.groupby(['key1','key2']))[0]
同樣,我們也可以將結果轉化為dict(字典):
dict(list(df.groupby(['key1','key2'])))
dict(list(df.groupby(['key1','key2'])))[('a','one')]
以上都是基於行進行分組,因為默認情況下groupby是在axis=0方向(行方向)進行分組,我們可以指定axis=1方向(列方向)進行分組:
grouped=df.groupby(df.dtypes,axis=1)
list(grouped)[0]
dict(list(grouped))
注意,
'''下面兩段語句功能一樣''' df.groupby('key1')['data1'] df.data1.groupby(df.key1)
三、通過字典進行分組
people = pd.DataFrame(np.random.randn(5, 5), columns=['a', 'b', 'c', 'd', 'e'], index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis']) people.ix[2:3, ['b', 'c']] = np.nan # 添加缺失值 people
假如,我們想按列進行聚合,該怎么操作呢?
我們根據實際情況,對列名建立字典,然后將此字典傳入groupby,切記指定axis=1,因為我們是對列進行分組聚合:
mapping = {'a': 'red', 'b': 'red', 'c': 'blue',
'd': 'blue', 'e': 'red', 'f' : 'orange'}
by_columns=people.groupby(mapping,axis=1)
by_columns.mean()
既然我們可以通過傳入字典來對列進行分組,那么肯定也可以通過傳入Series來對列進行分組了(Series中的index就相當字典中的key嘛):
map_series = pd.Series(mapping)
people.groupby(map_series,axis=1).count()
四、通過函數進行分組
剛剛我們分組時利用了dict和series建立映射,對於一些復雜的需求,我們可以直接對groupby函數傳遞函數名來進行分組,以剛才的people數據為例,如果我們想按行分組,分組的key是每個人名的字母長度,該怎么做呢?比較直接的想法是相對每個名字求長度,建立一個數組,然后將這個數組傳入groupby,我們來試驗一下:
l=[len(x) for x in people.index] people.groupby(l).count()
方案可行,那么有沒有更快捷更優美的方法呢?當然有啦,我們只需將len這個函數名傳給groupby即可:
people.groupby(len).count()
除了傳遞函數,我們也可以將函數和dict,series,array一起使用,畢竟最后都會統統轉化為數組:
key_list = ['one', 'one', 'one', 'two', 'two'] people.groupby([len, key_list]).min()
五、根據索引級別分組
剛剛我們的數據索引只有一級,當數據有多級索引時,可以通過level指定我們想要分組的索引,注意要使用axis=1表示按列:
columns = pd.MultiIndex.from_arrays([['Asian', 'Asian', 'Asian', 'America', 'America'], ['China','Japan','Singapore','United States','Canada']], names=['continent', 'country']) hier_df = pd.DataFrame(np.random.randn(4, 5), columns=columns) hier_df
我們按洲進行分組求和:






















