pandas之groupby分組與pivot_table透視


一、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')
get_group查看分組內容和groups查看分組所在行

 

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'>
size統計分組的長度

 

分組可對單列或者多列進行,如果對多列進行分組,需要寫在一個列表內。

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
groupby單列和多列分組

 

按照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分組

 

按照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
按照index長度分組

 

按照數據類型進行分組,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
# -------
按照數據類型dtypes分組

 

按照字典分組,需定義一個字典,鍵為列名稱,值為對應的分組名稱,按照列分組需要指明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
分組后多函數計算agg

 

多函數計算后的默認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
transform()演示

 

三、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
apply()自定義統計方法

 

四、透視表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_table()

 

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
數據透視pivot()

 

五、交叉表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

 

 

 


免責聲明!

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



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