note.nkmk.me

pandasのcrosstabでクロス集計(カテゴリ毎の出現回数・頻度を算出)

Date: 2018-04-07 / tags: Python, pandas

pandas.crosstab()関数を使うとクロス集計分析ができる。

カテゴリデータ(カテゴリカルデータ、質的データ)のカテゴリごとのサンプル数(出現回数・頻度)の算出などが可能。

出現回数ではなく、カテゴリごとの平均値などを算出したい場合はピボットテーブルpandas.pivot_table()を使う。以下の記事を参照。

ここでは、

  • pandas.crosstab()関数の基本的な使い方
  • カテゴリごとの小計・総計を算出: 引数margins
  • 全体・行ごと・列ごとに規格化(正規化): 引数normalize

について説明する。

例としてタイタニックの生存情報のデータを使用する。Kaggleの問題からダウンロードできる。

import pandas as pd

df = pd.read_csv('data/src/titanic_train.csv', index_col=0).drop(['Name', 'Ticket', 'SibSp', 'Parch'], axis=1)

print(df.head())
#              Survived  Pclass     Sex   Age     Fare Cabin Embarked
# PassengerId                                                        
# 1                   0       3    male  22.0   7.2500   NaN        S
# 2                   1       1  female  38.0  71.2833   C85        C
# 3                   1       3  female  26.0   7.9250   NaN        S
# 4                   1       1  female  35.0  53.1000  C123        S
# 5                   0       3    male  35.0   8.0500   NaN        S

適当に列を除外している。

スポンサーリンク

pandas.crosstab()関数の基本的な使い方

第一引数indexに結果の行見出しとなるpandas.DataFrameの列(= pandas.Series)、第二引数columnsに結果の列見出しとなるpandas.DataFrameの列(= pandas.Series)を指定する。

pandas.pivot_table()と異なり、pandas.DataFrameオブジェクトとその列名で指定するのではない。注意。

print(pd.crosstab(df['Sex'], df['Pclass']))
# Pclass    1    2    3
# Sex                  
# female   94   76  144
# male    122  108  347

pandas.crosstab()関数が返すのはpandas.DataFrame

print(type(pd.crosstab(df['Sex'], df['Pclass'])))
# <class 'pandas.core.frame.DataFrame'>

引数index, columnsにはpandas.Seriesのリストを指定することもできる。結果はマルチインデックス(階層型インデックス)のpandas.DataFrameとして返される。

print(pd.crosstab([df['Sex'], df['Survived']], [df['Pclass'], df['Embarked']]))
# Pclass            1         2          3         
# Embarked          C  Q   S  C  Q   S   C   Q    S
# Sex    Survived                                  
# female 0          1  0   2  0  0   6   8   9   55
#        1         42  1  46  7  2  61  15  24   33
# male   0         25  1  51  8  1  82  33  36  231
#        1         17  0  28  2  0  15  10   3   34

マルチインデックスのpandas.DataFrameの行・列の選択方法については以下の記事を参照。

カテゴリごとの小計・総計を算出: 引数margins

引数marginsTrueとすると、各カテゴリごとの小計および全体の総計が算出できる。

print(pd.crosstab([df['Sex'], df['Survived']], [df['Pclass'], df['Embarked']],
                  margins=True))
# Pclass            1           2           3           All
# Embarked          C  Q    S   C  Q    S   C   Q    S     
# Sex    Survived                                          
# female 0          1  0    2   0  0    6   8   9   55   81
#        1         42  1   46   7  2   61  15  24   33  231
# male   0         25  1   51   8  1   82  33  36  231  468
#        1         17  0   28   2  0   15  10   3   34  109
# All              85  2  127  17  3  164  66  72  353  889

小計・総計の行ラベル・列ラベルは引数margins_nameで指定できる。デフォルトは'All'

print(pd.crosstab([df['Sex'], df['Survived']], [df['Pclass'], df['Embarked']],
                  margins=True, margins_name='Total'))
# Pclass            1           2           3          Total
# Embarked          C  Q    S   C  Q    S   C   Q    S      
# Sex    Survived                                           
# female 0          1  0    2   0  0    6   8   9   55    81
#        1         42  1   46   7  2   61  15  24   33   231
# male   0         25  1   51   8  1   82  33  36  231   468
#        1         17  0   28   2  0   15  10   3   34   109
# Total            85  2  127  17  3  164  66  72  353   889

全体・行ごと・列ごとに規格化(正規化): 引数normalize

引数normalizeを指定すると、結果を全体・行ごと・列ごとに1で規格化(正規化)できる。

normalize=Trueまたはnormalize='all'とすると、全体を合計すると1になるように規格化。

print(pd.crosstab(df['Sex'], df['Pclass'], margins=True, normalize=True))
# Pclass         1         2         3       All
# Sex                                           
# female  0.105499  0.085297  0.161616  0.352413
# male    0.136925  0.121212  0.389450  0.647587
# All     0.242424  0.206510  0.551066  1.000000

normalize='index'とすると、行ごとに合計すると1になるように規格化。

print(pd.crosstab(df['Sex'], df['Pclass'], margins=True, normalize='index'))
# Pclass         1         2         3
# Sex                                 
# female  0.299363  0.242038  0.458599
# male    0.211438  0.187175  0.601386
# All     0.242424  0.206510  0.551066

normalize='columns'とすると、列ごとに合計すると1になるように規格化。

print(pd.crosstab(df['Sex'], df['Pclass'], margins=True, normalize='columns'))
# Pclass         1         2         3       All
# Sex                                           
# female  0.435185  0.413043  0.293279  0.352413
# male    0.564815  0.586957  0.706721  0.647587

引数margins=Trueかつ引数index, columnsにリストで複数列を指定した場合、マルチインデックスを指定した方向に規格化するとエラーになるので注意。

# print(pd.crosstab(df['Sex'], [df['Pclass'], df['Embarked']],
#                   margins=True, normalize=True))
# TypeError: Expected tuple, got str

print(pd.crosstab(df['Sex'], [df['Pclass'], df['Embarked']],
                  margins=True, normalize='index'))
# Pclass           1                             2                      \
# Embarked         C         Q         S         C         Q         S   
# Sex                                                                    
# female    0.137821  0.003205  0.153846  0.022436  0.006410  0.214744   
# male      0.072790  0.001733  0.136915  0.017331  0.001733  0.168111   
# All       0.095613  0.002250  0.142857  0.019123  0.003375  0.184477   
# Pclass           3                      
# Embarked         C         Q         S  
# Sex                                     
# female    0.073718  0.105769  0.282051  
# male      0.074523  0.067591  0.459272  
# All       0.074241  0.080990  0.397075  

# print(pd.crosstab(df['Sex'], [df['Pclass'], df['Embarked']],
#                   margins=True, normalize='columns'))
# ValueError: Length of new names must be 1, got 2

バグとして報告されているがバージョン0.22.0の時点でまだFixされていない模様。

margins=False(デフォルト)であれば問題ない。

print(pd.crosstab(df['Sex'], [df['Pclass'], df['Embarked']], normalize=True))
# Pclass           1                             2                      \
# Embarked         C         Q         S         C         Q         S   
# Sex                                                                    
# female    0.048369  0.001125  0.053993  0.007874  0.002250  0.075366   
# male      0.047244  0.001125  0.088864  0.011249  0.001125  0.109111   
# Pclass           3                     
# Embarked         C        Q         S  
# Sex                                    
# female    0.025872  0.03712  0.098988  
# male      0.048369  0.04387  0.298088  

print(pd.crosstab(df['Sex'], [df['Pclass'], df['Embarked']], normalize='index'))
# Pclass           1                             2                      \
# Embarked         C         Q         S         C         Q         S   
# Sex                                                                    
# female    0.137821  0.003205  0.153846  0.022436  0.006410  0.214744   
# male      0.072790  0.001733  0.136915  0.017331  0.001733  0.168111   
# Pclass           3                      
# Embarked         C         Q         S  
# Sex                                     
# female    0.073718  0.105769  0.282051  
# male      0.074523  0.067591  0.459272  

print(pd.crosstab(df['Sex'], [df['Pclass'], df['Embarked']], normalize='columns'))
# Pclass           1                        2                             3  \
# Embarked         C    Q         S         C         Q         S         C   
# Sex                                                                         
# female    0.505882  0.5  0.377953  0.411765  0.666667  0.408537  0.348485   
# male      0.494118  0.5  0.622047  0.588235  0.333333  0.591463  0.651515   
# Pclass                        
# Embarked         Q         S  
# Sex                           
# female    0.458333  0.249292  
# male      0.541667  0.750708  
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事