一、groupby
類似excel的數據透視表,一般是按照行進行分組,使用方法如下。
df.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True,
squeeze=False, observed=False, **kwargs)
分組得到的直接結果是一個DataFrameGroupBy對象。
df = pd.DataFrame({'A':['zhao','li','wang','li','zhao'],
'B':['one','one','two','three','two'],
'C':np.arange(1,6),
'D':np.arange(6,11)})
print(df)
print(df.groupby('A'))
print(type(df.groupby('A')))
# A B C D
# 0 zhao one 1 6
# 1 li one 2 7
# 2 wang two 3 8
# 3 li three 4 9
# 4 zhao two 5 10
# <pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000000001E6C550>
# <class 'pandas.core.groupby.generic.DataFrameGroupBy'>
分組后的直接結果是一個可迭代對象,可迭代對象中的每一個元素都是一個元組,元組的第一個值為分組的名稱,第二個值為DataFrame。可通過for或轉換為list、元組查看每一個元素。
for n,p in df.groupby('A'): print(type(p)) print(n) print(p) print('-------------------------') # <class 'pandas.core.frame.DataFrame'> # li # A B C D # 1 li one 2 7 # 3 li three 4 9 # ------------------------- # <class 'pandas.core.frame.DataFrame'> # wang # A B C D # 2 wang two 3 8 # ------------------------- # <class 'pandas.core.frame.DataFrame'> # zhao # A B C D # 0 zhao one 1 6 # 4 zhao two 5 10 # -------------------------
通過get_group('分組名稱')獲取某一個分組的內容
groups是一個字典,字典的鍵為各分組名稱,值為列表包含分組所在的索引行,可通過groups['分組名稱']查看某一個分組所在的行
print(df.groupby('A').get_group('zhao')) #獲取分組后的zhao組 # A B C D # 0 zhao one 1 6 # 4 zhao two 5 10 print(df.groupby(['A','B']).groups) print(df.groupby(['A','B']).groups[('li', 'one')]) # {('li', 'one'): Int64Index([1], dtype='int64'), ('li', 'three'): Int64Index([3], dtype='int64'), ('wang', 'two'): Int64Index([2], dtype='int64'), ('zhao', 'one'): Int64Index([0], dtype='int64'), ('zhao', 'two'): Int64Index([4], dtype='int64')} # Int64Index([1], dtype='int64')
size( )統計每個分組的長度
print(df.groupby('A').size()) print(type(df.groupby('A').size())) # A # li 2 # wang 1 # zhao 2 # dtype: int64 # <class 'pandas.core.series.Series'>
分組可對單列或者多列進行,如果對多列進行分組,需要寫在一個列表內。
df = pd.DataFrame({'A':['zhao','li','wang','li','zhao'],
'B':['one','one','two','three','two'],
'C':np.arange(1,6),
'D':np.arange(6,11)})
print(df.groupby('A').sum()) #以A列分組,對其他元素為數值的列進行求和,忽略非數值元素的列
print('---------------------')
print(df.groupby(['A','B']).sum()) #以A列和B列分組,對其他列求和,忽略非數值元素的列
print('---------------------')
print(df.groupby('A')['D'].sum()) #以A列分組,再對D列求和
C D
# A
# li 6 16
# wang 3 8
# zhao 6 16
# ---------------------
# C D
# A B
# li one 2 7
# three 4 9
# wang two 3 8
# zhao one 1 6
# two 5 10
# ---------------------
# A
# li 16
# wang 8
# zhao 16
# Name: D, dtype: int32
按照index分組,將index相同的分為一組,分組依據level=0
df = pd.DataFrame({'data1':[1,2,3,4],'data2':[3,4,5,6],'A':[5,6,7,8],'B':[7,8,9,0]},index=[1,2,3,1])
print(df) #groupby(level=0)表示將索引相同的行分為一組
print(df.groupby(level=0).first()) #分組后組內第一個值
print(df.groupby(level=0).last()) #分組后組內最后一個值
print(df.groupby(level=0).max()) #分組后組內最大值
print(df.groupby(level=0).min()) #分組后組內最小值
print(df.groupby(level=0).sum()) #分組后組內元素的和
print(df.groupby(level=0).mean()) #分組后組內元素的平均值
print(df.groupby(level=0).median())#分組后組內元素的中位數
print(df.groupby(level=0).count()) #分組后組內元素的個數
print(df.groupby(level=0).std()) #分組后組內元素的方差
print(df.groupby(level=0).prod()) #分組后組內元素的乘積
print(df.groupby(level=0).describe())#分組后組內元素的count、mean、std、min、25%、50%、75%。max
按照index長度分組
df = pd.DataFrame({'A':[1,2,3,4],'B':[3,4,5,6],'D':[5,6,7,8],'D':[7,8,9,0]},index=['a','ab','cd','e'])
print(df)
print(df.groupby(len).sum())
# A B D
# a 1 3 7
# ab 2 4 8
# cd 3 5 9
# e 4 6 0
# A B D
# 1 5 9 7
# 2 5 9 17
按照數據類型進行分組,df.dtypes可獲得每個數據列的數據類型,數據類型是對列而言,因此按數據類型分組需指明axis=1
df = pd.DataFrame({'data1':[1,2],'data2':[3,4],'A':['a','b'],'B':['c','d']})
print(df)
print(df.groupby(df.dtypes,axis=1).sum())
for n,p in df.groupby(df.dtypes,axis=1):
print(n)
print(p)
print('-------')
# data1 data2 A B
# 0 1 3 a c
# 1 2 4 b d
# int64 object
# 0 4 ac
# 1 6 bd
# int64
# data1 data2
# 0 1 3
# 1 2 4
# -------
# object
# A B
# 0 a c
# 1 b d
# -------
按照字典分組,需定義一個字典,鍵為列名稱,值為對應的分組名稱,按照列分組需要指明axis=1
例如下面例子中的map,定義data1列和A列屬於分組key1,data2列數組分組key2,B列屬於分組key3
df = pd.DataFrame({'data1':[1,2],'data2':[3,4],'A':['a','b'],'B':['c','d']})
map = {'data1':'key1','data2':'key2','A':'key1','B':'key3'}
for i,p in df.groupby(map,axis=1):
print(i)
print(p)
print('----------')
# key1
# data1 A
# 0 1 a
# 1 2 b
# ----------
# key2
# data2
# 0 3
# 1 4
# ----------
# key3
# B
# 0 c
# 1 d
# ----------
多函數計算agg(函數1,函數2)
對分組后的每個組既進行第一個函數的計算,又進行第二個函數的計算。
df = pd.DataFrame({'a':[1,2,3,1],'b':['a','b','a','b'],'c':np.arange(3,7),'d':np.arange(2,6)})
print(df)
print(df.groupby('a').agg(['mean','sum'])) #b列為非數值,忽略
print(df.groupby('b').agg([np.sum,np.mean]))
# a b c d
# 0 1 a 3 2
# 1 2 b 4 3
# 2 3 a 5 4
# 3 1 b 6 5
# c d
# mean sum mean sum
# a
# 1 4.5 9 3.5 7
# 2 4.0 4 3.0 3
# 3 5.0 5 4.0 4
# a c d
# sum mean sum mean sum mean
# b
# a 4 2.0 8 4 6 3
# b 3 1.5 10 5 8 4
多函數計算后的默認column為函數名稱,也可以通過字典自定義column。
df = pd.DataFrame({'a':[1,2,3,1],'b':['a','b','a','b'],'c':np.arange(3,7),'d':np.arange(2,6)})
print(df.groupby('b')['c'].agg({'result_sum':np.sum,'result_mean':np.mean}))
# result_sum result_mean
# b
# a 8 4
# b 10 5
二、transform
上述groupby如果通過行分組再進行求和、均值等,會出現結果與原對象的行數不一致的情況,而通過transform得到的結果與原對象的結果行數一致。
df = pd.DataFrame({'A':['zhao','li','wang','li','zhao'],
'B':['one','one','two','two','three'],
'C':np.arange(1,6),
'D':np.arange(5,10)})
print(df)
print(df.groupby('B').mean()) #只包含one、two、three三行
print(df.groupby('B').transform(np.mean)) #結果為5行,B列內容相同的行的結果相同
# A B C D
# 0 zhao one 1 5
# 1 li one 2 6
# 2 wang two 3 7
# 3 li two 4 8
# 4 zhao three 5 9
# C D
# B
# one 1.5 5.5
# three 5.0 9.0
# two 3.5 7.5
# C D
# 0 1.5 5.5
# 1 1.5 5.5
# 2 3.5 7.5
# 3 3.5 7.5
# 4 5.0 9.0
三、applay
上述groupby分組后都是使用python定義好的sum、mean、max等進行統計計算,apply可以自定義統計方法。
def func(df,n): return *** df.groupby('*').apply(func,n) #自定義函數func,第一個參數為pandas對象,並返回值 #分組后使用apply函數,將自定義函數的名稱作為第一個參數,第二個參數傳遞給自定義函數的第二個參數
df = pd.DataFrame({'A':['zhao','li','wang','li','zhao','zhao'],
'B':['one','one','two','two','three','two'],
'C':np.arange(1,7),
'D':np.arange(4,10)})
def f(d,n):
return d.sort_index()[:n] #按照索引排序,並返回前n行
print(df)
print(df.groupby('A').apply(f,2)) #按照A分組,對結果按索引排序,並返回每組的前n行
A B C D
# 0 zhao one 1 4
# 1 li one 2 5
# 2 wang two 3 6
# 3 li two 4 7
# 4 zhao three 5 8
# 5 zhao two 6 9
# A B C D
# A
# li 1 li one 2 5
# 3 li two 4 7
# wang 2 wang two 3 6
# zhao 0 zhao one 1 4
# 4 zhao three 5 8
四、透視表pivot_table()、pivot()
pivot_table(self, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
可類比excel的數據透視表進行理解,使用方法pd.pivot_table( df , ...),或直接使用df.pivot_table( ... )
- values:透視后對哪一列進行計算
- index:按照哪一列進行分組
- columns:透視后除了values,還包含哪些列
- aggfunc:對values進行計算的方法,默認為平均值
- fill_value:對空值使用fill_value指定的值填充,默認為NaN
import numpy as np import pandas as pd date = ['2019/5/1','2019/5/2','2019/5/3']*3 df = pd.DataFrame({'date':pd.to_datetime(date),'key':list('abcbacbca'),'value':np.arange(1,10)}) print(df) print(df.pivot_table(index='date',values='value',columns='key',aggfunc=np.sum)) print(df.pivot_table(index=['date','key'],values='value',aggfunc=np.sum)) # date key value # 0 2019-05-01 a 1 # 1 2019-05-02 b 2 # 2 2019-05-03 c 3 # 3 2019-05-01 b 4 # 4 2019-05-02 a 5 # 5 2019-05-03 c 6 # 6 2019-05-01 b 7 # 7 2019-05-02 c 8 # 8 2019-05-03 a 9 # key a b c # date # 2019-05-01 1.0 11.0 NaN # 2019-05-02 5.0 2.0 8.0 # 2019-05-03 9.0 NaN 9.0 # value # date key # 2019-05-01 a 1 # b 11 # 2019-05-02 a 5 # b 2 # c 8 # 2019-05-03 a 9 # c 9
pivot()也是用來生成透視表的,結果為一個二維的表格,結果中可能會存在空值,但是與pivot_table()用法和結果稍有不同。
pivot(data, index=None, columns=None, values=None),使用方法pd.pivot(df,...)或者df.pivot()
index 透視行
columns 透視列
values 對哪一列進行透視
date = ['2019/5/1','2019/5/2','2019/5/3']*3 df = pd.DataFrame({'date':pd.to_datetime(date),'key':list('abcbabccd'),'value':np.arange(1,10)}) res = pd.pivot(df,'date','key','value') print(res) # key a b c d # date # 2019-05-01 1.0 4.0 7.0 NaN # 2019-05-02 5.0 2.0 8.0 NaN # 2019-05-03 NaN 6.0 3.0 9.0
五、交叉表crossta()
交叉表默認用來統計元素出現的頻數,使用方法pd.crosstab(Seris1,Seris2)
crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None,
margins=False, margins_name='All', dropna=True, normalize=False)
- index:行的統計依據
- columns:列的統計依據
- values:在行為index、列為columns的基礎上,對values指定的列進行統計,默認為None,表示統計頻數
- aggfunc:統計方法,agggunc和values必須同時使用
- normalize:默認為False,值設置為True表示統計頻率
- margins:默認為False,值設置為True表示對結果添加一列,對每列的和進行統計,同時添加一行,對每行的和進行統計
- rownames和colnames:默認為None,即顯示index和columns的名稱
df = pd.DataFrame([[1,2,3,1,3],[2,'a','a',2,1],[3,np.nan,'a','b',2],[np.nan,'a','b',1,2],[4,1,'c','b',2]],columns=['A','B','C','D','E']) print(pd.crosstab(df['A'],df['B'])) #表示統計A為1且B為1出現的次數,A為2且B為1出現的次數,A為4且B為1出現的次數…… # B 1 2 a # A # 1.0 0 1 0 # 2.0 0 0 1 # 4.0 1 0 0
可以看到A為np.nan、B為np.nan的兩行在統計時都被忽略了。
normalize=True會將上述結果的非0值顯示為小數試的百分比
print(pd.crosstab(df['A'],df['B'],normalize=True)) # B 1 2 a # A # 1.0 0.000000 0.333333 0.000000 # 2.0 0.000000 0.000000 0.333333 # 4.0 0.333333 0.000000 0.000000
同時指定values=df['E']和aggfunc=np.sum,表示在A和B對應值得條件下,對E列求和
print(pd.crosstab(df['A'],df['B'],values=df['E'],aggfunc=np.sum)) # B 1 2 a # A # 1.0 0 1 0 # 2.0 0 0 1 # 4.0 1 0 0
margins增加行和列的總數統計
print(pd.crosstab(df['A'],df['B'])) print(pd.crosstab(df['A'],df['B'],margins=True)) # B 1 2 a # A # 1.0 0 1 0 # 2.0 0 0 1 # 4.0 1 0 0 # B 1 2 a All # A # 1.0 0 1 0 1 # 2.0 0 0 1 1 # 4.0 1 0 0 1 # All 1 1 1 3
rownames和colnames自定義結果顯示的行和列名稱
print(pd.crosstab(df['A'],df['B'],rownames=['AAA'],colnames=['BBB'])) # BBB 1 2 a # AAA # 1.0 0 1 0 # 2.0 0 0 1 # 4.0 1 0 0
