今回はアンケート結果の集計のように、ある値がその条件下で何回出現するかを表としてまとめるクロス集計を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でもエクセルと遜色ないクロス集計表が手軽に作れます。
クロス集計はデータ分析の第一歩とも言えるのでぜひ使いこなしていきましょう。