今回はアンケート結果の集計のように、ある値がその条件下で何回出現するかを表としてまとめるクロス集計をPandasで行っていきます。

クロス集計を行うにあたって、データの出現頻度をまとめたい場合はcrosstab関数が有効です。

平均や合計などの統計量を知りたい場合や属性ごとにまとめたい場合はpivot_table関数を使いましょう。 pivot_tableの詳しい使い方は以下の記事で解説しています。

Pandasでピボットテーブルを手軽に作成するpivot_table関数の使い方 /features/pandas-pivot.html

crosstab関数

早速使い方を見ていきます。

APIドキュメント

crosstab関数のAPIドキュメントは以下の通りです。

pandas.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, margins_name=’All’, dropna=True, normalize=False)

params:

パラメータ名 概要
index 配列,Series
もしくは
配列やSeriesのリスト
行ごとにグループとしてまとめる値を指定します。
columns 配列,Series
もしくは
配列やSeriesのリスト
列ごとにグループとしてまとめる値を指定します。
values 配列 (省略可能)初期値None
何かしらの処理を行いたい値を指定します。同時にaggfunc引数も指定する必要があります。
aggfunc 関数 (省略可能)初期値None
値に対して何かしらの処理を行いたい時に指定します。values引数も指定する必要があります。
rownames シーケンス (省略可能)初期値None
行ラベルを新たに指定する際に使います。
colnames シーケンス (省略可能)初期値None
列ラベルを新たに指定する際に使います。
margins bool値 (省略可能)初期値False
行/列 にマージンを追加し、小計を表示します。
margins_name str (省略可能)初期値’All’
追加されたマージンにつける行/列ラベルを指定します。
dropna bool値 (省略可能)初期値True
欠損値を飛ばすかどうかを指定します。
normalize bool値,
{‘all’,’index’,’columns’}
{0, 1}
(省略可能)初期値False
指定された範囲の合計値で値を割ることで値を正規化します。Trueの場合は全ての値が対象になります。

returns:

クロス集計表となったDataFrameが返されます。

今回は以下のデータを使っていきます。

In [1]: import numpy as np

In [3]: import pandas as pd

In [4]: sex = np.random.choice(['male','female'],size=20)

In [6]: item = np.random.choice(['dish', 'knife', 'fork'],size=20)

In [7]: price = np.random.choice([100,1000,5000],size=20)

In [8]: df = pd.DataFrame({'sex':sex,'item':item,'price':price})

In [9]: df
Out[9]:
     item  price     sex
0   knife   5000    male
1    dish   5000  female
2   knife   5000    male
3   knife   1000  female
4    fork    100  female
5    dish   1000  female
6    dish    100  female
7    dish   1000  female
8   knife   5000    male
9    dish   1000  female
10  knife   1000    male
11   dish    100    male
12   fork   5000  female
13  knife    100    male
14  knife   1000  female
15  knife   1000  female
16   dish   5000  female
17   fork   5000    male
18   fork   5000    male
19  knife   1000  female

基本的なクロス集計表の作り方

まずは2つの属性を持つ値がどれだけあるのかを集計する最も基本的なクロス集計表を作ります。

'item''sex'で集計してみます。

In [10]: pd.crosstab(index=df['item'],columns=df['sex'])
Out[10]:
sex    female  male
item               
dish        6     1
fork        2     2
knife       4     5

これを見ると、女性だとお皿を買う傾向が強く、男性だとナイフを買う傾向が強いと判断することができます。

それぞれの行、列は複数の値を指定することができます。

In [14]: pd.crosstab(index=[df['item'],df['price']],columns=df['sex']) # 今回はタプルでまとめた
Out[14]:
sex          female  male
item  price              
dish  100         1     1
      1000        3     0
      5000        2     0
fork  100         1     0
      5000        1     2
knife 100         0     1
      1000        4     1
      5000        0     3

In [15]: pd.crosstab(index=df['item'],columns=[df['sex'],df['price']]) # 列も同様
Out[15]:
sex   female           male          
price   100  1000 5000 100  1000 5000
item                                 
dish       1    3    2    1    0    0
fork       1    0    1    0    0    2
knife      0    4    0    1    1    3

値の合計や平均を求める

では先ほど分けたグループごとの購入金額の合計を求めてみます。 values引数にdf['price']を指定し、aggfunc引数に"sum"と指定すればできます。 平均を求めたいのであれば、aggfunc="mean"と指定しましょう。

In [11]: pd.crosstab(index=df['item'],columns=df['sex'],values=df['price'],aggfunc='sum') # 合計金額を求める
Out[11]:
sex    female   male
item                
dish    13100    100
fork     5100  10000
knife    4000  16100

In [12]: pd.crosstab(index=df['item'],columns=df['sex'],values=df['price'],aggfunc='mean') # 平均金額を求める
Out[12]:
sex         female    male
item                      
dish   2183.333333   100.0
fork   2550.000000  5000.0
knife  1000.000000  3220.0

小計を表示する

margins=Trueにするとそれぞれの行/列の合計値を表示させることができます。

In [16]: pd.crosstab(index=df['item'],columns=df['sex'],margins=True) # 小計を表示
Out[16]:
sex    female  male  All
item                    
dish        6     1    7
fork        2     2    4
knife       4     5    9
All        12     8   20

margins_nameでラベル名変更可能です。

In [17]: pd.crosstab(index=df['item'],columns=df['sex'],margins=True, margins_name='小計') # ラベル変更
Out[17]:
sex    female  male  小計
item                   
dish        6     1   7
fork        2     2   4
knife       4     5   9
小計         12     8  20

出力結果を正規化する

では、クロス集計表で出した頻度を相対的なものに変換しましょう。

相対的にするには、normalize引数で指定できます。どの値に対して相対的なものにするかを指定することができます。

normalize='All'normalize='True'にすると全てのデータの合計値で値を割ります。

normalize='index'normalize=0にすると行ごとの合計値で値を割ります。

normalize='columns'normalize=1にすると列ごとの合計値で値を割ります。

また、margins=Trueにしていると、表示されている小計の値も正規化されます。

In [22]: pd.crosstab(index=df['item'],columns=df['sex'], margins=True) # 正規化しない状態
Out[22]:
sex    female  male  All
item                    
dish        6     1    7
fork        2     2    4
knife       4     5    9
All        12     8   20

In [23]: pd.crosstab(index=df['item'],columns=df['sex'], margins=True, normalize=True) # 全体の値を対象にする
Out[23]:
sex    female  male   All
item                     
dish      0.3  0.05  0.35
fork      0.1  0.10  0.20
knife     0.2  0.25  0.45
All       0.6  0.40  1.00

In [24]: pd.crosstab(index=df['item'],columns=df['sex'], margins=True, normalize='index') # 行ごと normalize=0でも可
Out[24]:
sex      female      male
item                     
dish   0.857143  0.142857
fork   0.500000  0.500000
knife  0.444444  0.555556
All    0.600000  0.400000

In [26]: pd.crosstab(index=df['item'],columns=df['sex'], margins=True, normalize='columns') # 列ごと normalize=1でも可
Out[26]:
sex      female   male   All
item                        
dish   0.500000  0.125  0.35
fork   0.166667  0.250  0.20
knife  0.333333  0.625  0.45

まとめ

今回はPandasを使ったクロス集計のやり方についてまとめました。エクセルでもクロス集計の機能は有名ですがPandasでもエクセルと遜色ないクロス集計表が手軽に作れます。

クロス集計はデータ分析の第一歩とも言えるのでぜひ使いこなしていきましょう。

参考