pandasのピボットテーブル機能を試してみた

表やグラフをつくる際に意外と必要な機能。
知っているのと知らないのでは、天と地の差がある、そんなピボットテーブル機能をまとめてみました。
ちなみに私は学生時代、Excelのピボットテーブル機能も知らず、泣きながら実験データを整形していた記憶があります...
Qiitaへの投稿ということもあり、今回はpandasデータフレームで試してみました。

#ピボットテーブルとは
Excelにある機能のひとつで、あらかじめ登録したリストのなかから必要なデータを取り出して、あらゆる方向から集計や分析を行うツールのことを指しています。
特に「集計」機能は便利で、下図の左のようなデータ形式から右のような結果を返すことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/f95beeaa-c9b5-f3e0-edbd-f637a5bc645f.png)

クロス集計したい時や、ちょっとややこしいグラフをつくる際などに必要になります。
今回はExcelではなく、pandasデータフレーム上で似たことをやってみます。

#練習用に使ったデータ
[kaggle Titanicコンペ](https://www.kaggle.com/c/titanic)のtrainデータを拝借。
それぞれのカラムの意味については、[こちら](https://qiita.com/takuya66520126/items/983adb0c7a64755ff40a#%E3%82%B3%E3%83%BC%E3%83%89)を参照ください。

#本題
そのままだとこんな感じですので...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/086eeb08-4474-244e-7b2c-469134129cda.png)
解釈が難しい！

###準備

```py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Path
input_path = "../input_data/"

# Set Display Max Columns
pd.set_option("display.max_columns", 50)

train = pd.read_csv(input_path + "train.csv", sep=",", header=0, quotechar="\"")
# test = pd.read_csv(input_path + "bank/test.csv", sep=",", header=0, quotechar="\"")

# 扱いにくいので欠損値処理
train["Embarked"] = train.Embarked.fillna("S") 
train["Age"]=train["Age"].fillna(train.Age.mean())

# 年齢を年代別へ
labels = [ "{0} - {1}".format(i, i + 9) for i in range(0, 100, 10) ]
train["Age"] = pd.cut(train["Age"], np.arange(0, 101, 10),
                      include_lowest=True, right=False,
                      labels=labels)

#今回の目的に扱いやすそうなのに絞る
df = train[["PassengerId", "Survived", "Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]]

# データの確認
df.head()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/25e7499a-e715-7a4c-51cc-fa008481abf3.png)

扱いにくそうな乗客者の名前などは消去。


##使うのはpivot_table関数

最低限、必要な引数は下の3つ

・data（第一引数）: 元データのpandas.DataFrameオブジェクトを指定。
・index: 元データの列名を指定。結果の行見出しとなる。
・columns: 元データの列名を指定。結果の列見出しとなる。

引数index, columnsに指定していない列の平均値が結果として算出されるが、型が数値でない列は除外されます。

###基本的な使い方

```py
df.pivot_table(index="Age", columns= "Sex", values="Fare")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/df7f4762-60d3-5128-f3f1-f69c4a41b66e.png)
デフォルトでは集計の平均値を返している。
（引数にaggfuncを追加することで他の結果も計算できます。）

###結果の値の算出方法を指定: 引数aggfunc
デフォルトでは平均値が算出されるが、引数aggfuncに関数を指定することでほかの方法で値を算出することが可能。
デフォルト（引数aggfuncを省略した場合）はnumpy.mean()が指定される。

```py
df.pivot_table(index="Age", columns= "Sex", values="Fare", aggfunc='count')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/55b1b8b5-e80f-54c1-03b4-c6210df7f57c.png)

aggfunc='count'指定することで、当てはまるデータ個数を数え上げる。

###行・列も複数選択できる：引数 index, columns

```py
df.pivot_table(index="Age", columns= ["Pclass","Sex"], values="Survived", aggfunc='mean')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/e46004ef-5ab6-3c0d-5eca-32553fd83d77.png)


###aggfuncに複数の算出方法を指定できる

```py
df.pivot_table(index="Age", columns= ["Pclass","Sex"], values="Survived",  aggfunc=["mean","count"])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/ab26ab1b-bf1f-0d15-47ee-f9243d5f228c.png)


###総計結果を追加する：引数margins, margins_name

```py
df.pivot_table(index="Age", columns= ["Pclass","Sex"], values="Survived", aggfunc=["mean","count"],margins=True, margins_name="total")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/5485e1cf-0c3a-7951-c0c1-affda14a9f3c.png)



##結果で規格化したい時はcross_tab関数を使う: 引数normalize

cross_tab関数の引数normalizeを指定することで、全体・行ごと・列ごとに規格化できる。

```py
pd.crosstab(index=df.Age, columns= df.Sex, values=df.Fare, aggfunc='count',  normalize=True)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374781/deb6b858-d079-73bb-4b30-a22be9c0721e.png)


・normalize=Trueまたはnormalize='all'とすると、全体を合計すると1になるように規格化。
・normalize='index' or 'columns'とすると、行 or 列ごとに合計すると1になるように規格化。

以上。

ピボットデーブルの操作後、列が多層化するのですが、列指定で抜き出すことができません。
知ってる方いらっしゃいましたら、コメントください...

#参照・引用
・http://yaginogogo.hatenablog.jp/entry/2016/04/22/011327
・https://note.nkmk.me/python-pandas-pivot-table/
・https://deepage.net/features/pandas-pivot.html
・https://boxil.jp/mag/a2149/
・https://deepage.net/features/pandas-pivot.html
・https://qiita.com/kshigeru/items/bfa8c11d1e6487c791d3
・https://qiita.com/hoto17296/items/3442af64c7acb682de6a