Python數據聚合和分組運算(1)-GroupBy Mechanics


前言

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.將函數計算后的結果聚合。

figure1

圖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)})

figure2

我們將key1當做我們的分組鍵值,對data1進行分組,再求每組的均值:

grouped = df['data1'].groupby(df['key1'])

語法很簡單,但是這里需要注意grouped的數據類型,它不在是一個數據框,而是一個GroupBy對象。

grouped

figure3

實際上,在這一步,我們並沒有進行任何計算僅僅是創建用key1分組后創建了一個GroupBy對象,我們后面函數的任何操作都是基於這個對象的。

求均值:

grouped.mean()

figure4

剛剛我們只是用了key1進行了分組,我們也可以使用兩個分組變量,並且通過unstack方法進行結果重塑:

means = df['data1'].groupby([df['key1'], df['key2']]).mean()
means

figure5

means.unstack

figure6

以上我們的分組變量都是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()

figure6

二、對分組進行迭代

GroupBy對象支持迭代操作,會產生一個由分組變量名和數據塊組成的二元元組:

for name, group in df.groupby('key1'):
    print name
    print group

figure7

如果分組變量有兩個:

for (k1,k2), group in df.groupby(['key1','key2']):
    print k1,k2
    print group

figure8

我們可以將上面的結果轉化為list或者dict,來看看結果是什么樣的:

list(df.groupby(['key1','key2']))

figure9

看不太清楚,我們來看看這個列表的第一個元素:

list(df.groupby(['key1','key2']))[0]

figure10

同樣,我們也可以將結果轉化為dict(字典):

dict(list(df.groupby(['key1','key2'])))

figure11

dict(list(df.groupby(['key1','key2'])))[('a','one')]

figure12

以上都是基於行進行分組,因為默認情況下groupby是在axis=0方向(行方向)進行分組,我們可以指定axis=1方向(列方向)進行分組:

grouped=df.groupby(df.dtypes,axis=1)
list(grouped)[0]

figure13

dict(list(grouped))

figure14

注意,

'''下面兩段語句功能一樣'''
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

figure15

假如,我們想按列進行聚合,該怎么操作呢?

我們根據實際情況,對列名建立字典,然后將此字典傳入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()

figure16

既然我們可以通過傳入字典來對列進行分組,那么肯定也可以通過傳入Series來對列進行分組了(Series中的index就相當字典中的key嘛):

map_series = pd.Series(mapping)
people.groupby(map_series,axis=1).count()

figure17

四、通過函數進行分組

剛剛我們分組時利用了dict和series建立映射,對於一些復雜的需求,我們可以直接對groupby函數傳遞函數名來進行分組,以剛才的people數據為例,如果我們想按行分組,分組的key是每個人名的字母長度,該怎么做呢?比較直接的想法是相對每個名字求長度,建立一個數組,然后將這個數組傳入groupby,我們來試驗一下:

l=[len(x) for x in people.index]
people.groupby(l).count()

figure18

方案可行,那么有沒有更快捷更優美的方法呢?當然有啦,我們只需將len這個函數名傳給groupby即可:

people.groupby(len).count()

figure18

除了傳遞函數,我們也可以將函數和dict,series,array一起使用,畢竟最后都會統統轉化為數組:

key_list = ['one', 'one', 'one', 'two', 'two']
people.groupby([len, key_list]).min()

figure19

五、根據索引級別分組

剛剛我們的數據索引只有一級,當數據有多級索引時,可以通過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

figure19

我們按洲進行分組求和:

figure20


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM