標籤:\n 分析 gre std function count() 依據 gate app
# -*- coding:utf-8 -*-
# 《python for data analysis》第九章
# 資料彙總與分組運算
import pandas as pd
import numpy as np
import time
# 分組運算過程 -> split-apply-combine
# 拆分 應用 合并
start = time.time()
np.random.seed(10)
# 1、GroupBy技術
# 1.1、引文
df = pd.DataFrame({
‘key1‘: [‘a‘, ‘b‘, ‘a‘, ‘b‘, ‘a‘],
‘key2‘: [‘one‘, ‘two‘, ‘two‘, ‘two‘, ‘one‘],
‘data1‘: np.random.randint(1, 10, 5),
‘data2‘: np.random.randn(5)
})
print(df)
print(‘\n‘)
grouped = df[‘data1‘].groupby(df[‘key1‘]) # split,將data1列按照key1列進行分組
res = grouped.mean() # apply and combine, 將各組資料取平均之後匯合成一個DataFrame or Series
print(res)
print(‘\n‘)
# 上面的data[‘key1‘](即傳入groupby的對象)稱為分組鍵,分組鍵可以是多個列
print(df[‘data1‘].groupby([df[‘key1‘], df[‘key2‘]]).mean()) # 結果為一個層次化索引的Series
print(‘\n‘)
# print(df[‘data1‘].groupby([‘key1‘, ‘key2‘])) # 與上一行等價,即可用columns名來替代series作為分組鍵
print(df.groupby(‘key1‘).mean()) # 彙總(split+apply+combine)只對數值列(data1、data2)進行操作,非數值列(key2)會被過濾
print(‘\n‘)
print(df.groupby(‘key1‘).size()) # 各分組的元素個數
print(‘\n‘)
# 1.2、對分組進行迭代
# groupby對象像list、dictionary那樣支援迭代
for name, group in df.groupby(‘key1‘):
print(name)
print(group) # split之後仍然保留非數值列
# 多重鍵用元組(tuple)表示
print(‘\n‘)
for (key1, key2), group in df.groupby([‘key1‘, ‘key2‘]):
print(key1)
print(key2)
print(group)
# groupby預設在axis=0上進行分組,可顯示指定axis=1進行分組
# 可理解為對樣本進行分組和對特徵進行分組
# 1.3、選取一個或一組列
# 實質:groupby對象的索引
# 1.1節中對部分列進行彙總操作寫為df[‘data1‘].groupby(‘key1‘),該操作也可以通過對整個dataframe的groupby對象進行索引獲得
print(‘\n‘)
print(df.groupby(‘key1‘)[‘data1‘].mean())
print(‘\n‘)
# 1.4、通過字典或Series進行分組
df = pd.DataFrame(np.random.randn(5, 5), index=[‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘a‘], columns=[‘zoo‘, ‘zip‘, ‘zero‘, ‘zz‘, ‘zzz‘])
mapping = {‘a‘: ‘red‘, ‘b‘: ‘green‘, ‘c‘: ‘red‘, ‘d‘: ‘green‘, ‘e‘: ‘pink‘}
print(df.groupby(mapping).mean())
print(‘\n‘)
# 1.5、通過函數進行分組
print(df.groupby(len, axis=1).sum()) # 對axis=1上的各列名求取字串長度,長度值作為分組依據
print(‘\n‘)
# 注意,傳入groupby的分組鍵可以是多種類型的混合
key = [‘one‘, ‘two‘, ‘one‘, ‘two‘, ‘one‘]
print(df.groupby([len, key], axis=1).sum())
print(‘\n‘)
# 1.6、根據索引層級分組
# 當index或者columns為層次化的索引時,在groupby中可以指定按照哪一層索引進行分組
columns = pd.MultiIndex.from_arrays([[‘A‘, ‘A‘, ‘A‘, ‘B‘, ‘B‘], [1, 2, 3, 4, 5]], names=[‘level1‘, ‘level2‘])
df = pd.DataFrame(np.random.randn(5, 5), columns=columns)
print(df.groupby(level=‘level1‘, axis=1).sum()) # level關鍵字用於指定需要進行分組的索引層次
print(‘-----------------------------------↑section1‘)
# 2、資料彙總
# 2.1、引文
# groupby方法實現了split(分組),配合.sum()、.mean()等方法又實現了apply和combine,即彙總
# 具體地,彙總的方法可以是Series的各種方法,具體實現過程為
# step1——通過groupby將series進行切片(分組)——split
# step2——應用各種彙總函式,即上文提到的Series的各種方法,對各個切片進行操作——apply
# step3——將各個切片的運算結果進行組裝——combine
# 除了Series已有的各種方法,還可以自己定義彙總函式並應用於groupby對象,通過agg或者aggregate方法傳入
def pk2pk(gb):
return gb.max() - gb.min()
pk2pk_lambda = lambda gb: gb.max() - gb.min()
df = pd.DataFrame({
‘key1‘: [‘a‘, ‘b‘, ‘a‘, ‘b‘, ‘a‘],
‘key2‘: [‘one‘, ‘two‘, ‘two‘, ‘two‘, ‘one‘],
‘data1‘: np.random.randint(1, 10, 5),
‘data2‘: np.random.randn(5)
})
print(df)
print(df.groupby(‘key1‘).agg(pk2pk)) # 普通函數
print(df.groupby(‘key1‘).aggregate(pk2pk_lambda)) # lambda函數,agg和aggregat等價
print(‘\n‘)
# 2.2、面向列的多函數應用
# 2.2與2.3節介紹進階的彙總功能,以某個關於小費的資料集為例。
data = pd.read_csv(‘./data_set/tips.csv‘)
# 新增一列“小費佔總額的比例”
data[‘tip_p‘] = data[‘tip‘] / data[‘total_bill‘]
print(data.head())
print(‘\n‘)
# 進階彙總:對不同的列採用不同的彙總函式,或一次性採用多個彙總函式
grouped = data.groupby([‘smoker‘, ‘day‘]) # groupby object
grouped_p = grouped[‘tip_p‘] # groupby object的一個切片
print(grouped_p.agg(‘mean‘)) # 對切片使用mean方法
print(‘\n‘)
# 注意:agg裡面傳入預設函數以string形式,傳入自訂函數以函數名形式
# 傳入一組函數,則會形成一個以函數名為列名的columns
print(grouped_p.agg([‘mean‘, ‘std‘, pk2pk, pk2pk_lambda]))
print(‘\n‘)
# lambda函數的預設函數名均為<lambda>,無辨識度,需要別的方式來區分,即自訂函數名
# 自訂欄名,以(name,function)的元組形式傳入即可,其中name為自訂的名稱,function為函數名
print(grouped_p.agg([(‘Mean‘, ‘mean‘), (‘Std‘, ‘std‘), (‘Peak2Peak‘, pk2pk), (‘Peak2Peak_2‘, pk2pk_lambda)]))
print(‘\n‘)
# 更一般的情形,可以對dataframe的多個列採用多個彙總函式,此時的彙總結果將是一個層次化索引的dataframe
# 這相當於先對各列進行彙總再concat(axis=1)到一起
print(grouped[‘tip‘, ‘tip_p‘].agg([‘mean‘, ‘std‘, pk2pk, pk2pk_lambda]))
print(‘\n‘)
# 若對不同列採用不同的彙總函式,通過向agg方法傳入一個從列名映射到函數名的字典即可
print(grouped[‘tip‘, ‘tip_p‘].agg({‘tip‘: ‘mean‘, ‘tip_p‘: ‘std‘}))
print(‘‘)
print(grouped[‘tip‘, ‘tip_p‘].agg({‘tip‘: [‘mean‘, ‘std‘, pk2pk], ‘tip_p‘: [‘sum‘, pk2pk_lambda]}))
print(‘\n‘)
# 2.3、以“無索引”的形式返回彙總資料
# 預設情況下,分組鍵會成為結果的索引,通過groupby函數的as_index關鍵字置為False即可以無索引方式返回,分組鍵會轉而成為彙總結果的列(Series)
print(data.groupby([‘smoker‘], as_index=False).mean())
print(‘---------------------------------------↑section2‘)
# 3、分組級運算與轉換
# 分組運算除了上面提到的彙總(各種彙總函式),還可以通過transform和apply實現更多的分組運算
# transform不改變原有dataframe(or series)的index,將分組運算的結果廣播到各分組的各個元素中去,形成一個或多個新列
print(data)
print(‘\n‘)
print(data.groupby(‘day‘).transform(np.mean)) # 可用concat和原dataframe拼接到一起,axis = 1
# 3.1、apply:一般性的‘拆分-應用-合并‘
# apply可傳入任意處理序列的函數,返回的結果完全由apply傳入的函數決定
sort = lambda df, column, n: df.sort_values(by=column)[-n:]
print(data.groupby(‘smoker‘).apply(sort, column=‘tip‘, n=10)) # 返回按tip從大到小排列的前10行
print(‘‘)
# 上例中分組鍵會和原index構成層次化索引,但其實分組鍵的資訊已經包含在原dataframe中了,可在分組時設定group_keys關鍵字為False來禁用分組鍵
print(data.groupby(‘smoker‘, group_keys=False).apply(sort, column=‘tip‘, n=10))
print(‘\n‘)
# 3.2、分位元與桶分析
# 這一節的內容是將qcut和cut的運算結果傳入groupby函數實現按區間分組
df = pd.DataFrame({
‘data1‘: np.random.randn(100),
‘data2‘: np.random.randn(100)})
cut_data1 = pd.cut(df[‘data1‘], 5) # 等區間長度切割成5段
# 按照data1的分段結果對data2進行分組,並統計每個分組的數量、平均值、標準差、最大值與最小值
print(df[‘data2‘].groupby(cut_data1).apply(
lambda gp: {‘count‘: gp.count(), ‘max‘: gp.max(), ‘min‘: gp.min(), ‘std‘: gp.std(), ‘mean‘: gp.mean()}).unstack())
print(‘‘)
# qcut也是同理,將桶由等長度變成了等數量
print(df[‘data2‘].groupby(pd.qcut(df[‘data1‘], 5)).apply(
lambda gp: {‘count‘: gp.count(), ‘max‘: gp.max(), ‘min‘: gp.min(), ‘std‘: gp.std(), ‘mean‘: gp.mean()}).unstack())
print(‘\n‘)
# 3.3、樣本:用特定於分組的值填充缺失值
# 其實就是先分組,再每組apply填充缺失值函數fillna
df = pd.DataFrame({
‘key1‘: [‘a‘, ‘b‘, ‘a‘, ‘b‘, ‘a‘],
‘key2‘: [‘one‘, ‘two‘, ‘two‘, ‘two‘, ‘one‘],
‘data1‘: np.random.randint(1, 10, 5),
‘data2‘: np.random.randn(5)
})
df.ix[2:3,‘data2‘]=np.nan
print(df)
print(‘‘)
df = df.groupby(‘key1‘,group_keys=False).apply(lambda gp:gp.fillna(gp.mean()))
print(df)
print(‘-------------------------------------↑section3‘)
# 其餘執行個體,均為關於apply的應用執行個體,傳入不同函數,包括隨機採樣、取相關係數、線性迴歸等
# 4、透視表與交叉表
# 透視表與交叉表均可通過groupby實現,可以認為是groupby的捷徑
# 4.1、透視表(pivot)
# 以day和time為axis=0方向分組,smoker為axis=1方向分組,透視表方法預設彙總類型為計算各分組的平均數
print(data.pivot_table([‘tip‘,‘size‘], index=[‘day‘, ‘time‘], columns=‘smoker‘))
# 上述過程也可通過groupby實現
print(data.groupby([‘day‘, ‘time‘, ‘smoker‘])[‘size‘, ‘tip‘].mean().unstack())
# pivot_table()函數中關鍵字margins設為True可以添加分項小計,包括行與列
print(‘\n‘)
print(data.pivot_table([‘tip‘,‘size‘], index=[‘day‘, ‘time‘], columns=‘smoker‘, margins=True))
# pivot_table預設的彙總函式是取平均,可通過aggfunc關鍵字進行顯式指定
print(‘‘)
print(data.pivot_table([‘tip‘,‘size‘], index=[‘day‘, ‘time‘], columns=‘smoker‘, margins=True, aggfunc=sum))
# 4.2、交叉表(crosstab)
# 交叉表是一種用於計算分組的頻率(頻數)的特殊透視表
print(‘\n‘)
print(pd.crosstab([data[‘time‘], data[‘day‘]], data[‘smoker‘], margins=True))
print(‘--------------total time is %.5f s‘ % (time.time() - start))
# that‘s all
《python for data analysis》第九章,資料彙總與分組運算