KaggleのTitanicチュートリアルをTabPy（Tableau + Python）で解く方法

BIツールTableauとPythonを連携するフレームワークであるTabpyを使って、Kaggle[^1]のチュートリアルであるTitanicデータセット[^2]を題材に機械学習モデルの構築と結果の可視化までやってみました。
[^1]: お題のデータセットをもとに予測モデルを構築し、予測結果の精度を競うコンペです。データセットは様々で、過去にはmercariやリクルートがデータ提供したコンペもありました。
[^2]: Kaggleのチュートリアル的なコンペです。映画でおなじみのタイタニック号について、乗客のプロフィール情報をもとに生存・死亡を予測します。
EDA（探索的データ分析）[^3]や特徴量エンジニアリング[^4]とかをしっかりやって真剣にモデル構築したわけではなく、**あくまでTabPyをつかった機械学習と結果の可視化**をテーマに説明します。
[^3]: 仮説ありきでデータセットの特徴量やターゲット変数の分布・相関なんかを分析していく工程です。いかにデータを可視化できるかが重要です。
[^4]: データ前処理に該当する工程で、データ型の変換、標準化などなど、モデルが良い予測ができるようにいろいろと特徴量を加工します。
#環境
- Python 3.7.2
- Tableau 2018.1.6

#TabPyの準備
以下のサイトで詳しく説明されていますが、Dockerコンテナでサーバを起動する方が楽だと思います。
[Tabpy - Tableau + Python 連携  を使ってみよう！（その１：Tabpy導入編）](https://community.tableau.com/docs/DOC-10160)
なので、今回はDockerコンテナでTabpyサーバーを起動する方法を説明します。
※[Dockerのインストール方法](https://kitsune.blog/docker-install)

## Dockerfileを作成
以下のDockerfileを作成します。
Pythonベースイメージに`tabpy`をインストールし、サーバー起動コマンドを実行するだけの簡単なものです。

```:Dockerfile
FROM python:3.7-slim

RUN pip install tabpy==1.0.0

CMD ["tabpy"]
```

## Tabpyサーバーの起動
`Dockerfile`をもとにDockerイメージをビルドし、コンテナ（Tabpyサーバーのコンテナ）を起動させます。

```
# Dockerイメージのビルド
docker build -t tabpy .
# コンテナ起動(9004ポートをコンテナとローカルで繋ぐ)
docker run -p 9004:9004 tabpy
```


#Tableauでの操作
##データ準備
以下、KaggleからTitanicデータセットをダウンロードします。
[Titanic: Machine Learning from Disaster](https://www.kaggle.com/c/titanic/data)
学習用データの```train.csv```とテストデータの```test.csv```をTableauで読み込みます。
このとき、下図のように```train.csv```に```test.csv```をユニオンさせます。[^5]
[^5]: test.csvにはSurvivedカラムがなく、ユニオンできないと思われるかもしれませんが、自動的にnull埋めされるのでご心配なく。
![データ読み込み.png](https://qiita-image-store.s3.amazonaws.com/0/337794/e4c4089e-8ab1-ee4e-d895-afa930358c76.png)

Titanicデータセットは全レコード数が1309と、かなり軽量なので、csvファイルのまま分析を進めても支障ありません。
ただ、**データ量が多い場合はデータの抽出を実施し、hyperファイル（あるいはtdeファイル）を生成して分析する**方が良いです。
圧倒的に処理速度が向上します。

##計算フィールド作成
次に計算フィールドを作成していきます。
###LogisticRegressionフィールド
TaPpyで機械学習を行うための肝となる箇所です。
大まかに以下の流れで処理が行われます。

1. Tableauの集計関数or定数データをINPUT
2. Tabpy serverで機械学習を実施、結果を返却
3. TableauにOUTPUT

詳しくは以下を参照ください。
[Tabpy - Tableau + Python 連携  を使ってみよう！（その２：入出力の仕組みと簡単な計算）](https://community.tableau.com/docs/DOC-10365)

以下が今回作成したモデルになります。
sckit-learnのロジスティック回帰モデルを使用し、デフォルトのパラメータのままfit, predictしています。
![計算フィールド.png](https://qiita-image-store.s3.amazonaws.com/0/337794/e1f24d4c-7c9f-5a79-f7fe-ef5a1a27ef5a.png)


	SCRIPT_STR('
	import numpy as np
	import pandas as pd
	from sklearn.linear_model import LogisticRegression

	#引数をDataFrameに格納
	survived = pd.DataFrame(_arg1)
	sex = pd.DataFrame(_arg2)
	age = pd.DataFrame(_arg3)
	fare = pd.DataFrame(_arg4)
	table_nm = pd.DataFrame(_arg5)
	full = pd.concat([survived,sex,age,fare,table_nm],axis=1)
	full.columns = ["survived","sex","age","fare","table_nm"]

	#学習用データとテストデータに分割
	train = full[full["table_nm"]=="train.csv"]
	test = full[full["table_nm"]=="test.csv"]

	#欠損値補完(学習データのage, fareの中央値を使用)
	inpute_age = train["age"].median()
	inpute_fare = train["fare"].median()
	train["age"].fillna(inpute_age, inplace=True)
	train["fare"].fillna(inpute_fare, inplace=True)
	test["age"].fillna(inpute_age, inplace=True)
	test["fare"].fillna(inpute_fare, inplace=True)

	#ダミー変換(sex)
	train = pd.get_dummies(train, drop_first=True, columns=["sex"])
	test = pd.get_dummies(test, drop_first=True, columns=["sex"])

	#ターゲット変数
	train_X = train.drop(["survived","table_nm"], axis=1)
	train_y = train["survived"]
	test_X = test.drop(["survived","table_nm"], axis=1)
	X = pd.concat([train_X,test_X], axis=0)

	#ロジスティック回帰モデルを使って学習
	LR = LogisticRegression()
	LR.fit(train_X, train_y)
	rs = LR.predict(X)
	return rs.tolist()
	',MAX([Survived]),MAX([Sex]),SUM([Age]),SUM([Fare]),MAX([Table Name])))

scikit-learnの使い方は、通常のKaggleにチャレンジする場合と同じです。
**INPUTのデータ項目に対して集計関数を適用したり、OUTPUTはlist型にしたりする必要がある**ので、その点だけ注意してください。

###当てはめ成否
モデルの当てはめ結果と実際の生死を比較し、当てはめ失敗or成功かを判別するカラムを計算フィールドで作成します。

	if max([Survived]) != [LogisticRegression] then '失敗'
	else '成功'
	end

#Tableauでモデルの当てはめ結果を可視化
これでいろいろ可視化できます。
以下がロジスティック回帰モデルの当てはめ結果を可視化したシートになります。
![当てはめ結果.png](https://qiita-image-store.s3.amazonaws.com/0/337794/d4939036-5b3d-2d02-4e75-d4feb000ffe2.png)

下図は少々のEDAと学習データへの当てはめを可視化したダッシュボードになります。
左側は今回のモデルのINPUTに使った特徴量であるAge, Sex, Fareとターゲット変数であるSurvivedとの分布を示し、右側はモデルの当てはめ結果を可視化したものです。

![Kaggle Titanic Tutorial.png](https://qiita-image-store.s3.amazonaws.com/0/337794/3548a58e-6bea-c538-76bc-c2bdf2067834.png)
#テストデータの予測
LogisticRegressionフィールドを整数型に変換し、「PassengerId」単位で集計させます。
できたクロス集計表をcsvファイルでエクスポートし、以下のテストデータ用の「PassengerId」に絞るとKaggleに提出できます。

- 「PassengerId」1~891：学習データへの当てはめ結果
- 「PassengerId」892~1309：テストデータの予測
![testデータ予測.png](https://qiita-image-store.s3.amazonaws.com/0/337794/7bd27e4a-8b63-3d2e-8786-d5e2dce20450.png)


#まとめ
KaggleのKernelをみてみると、Pythonのpandasを使ってデータをごりごり加工し、matplotlibやseabornを使って可視化するのが定石になっています。
ただ、データの可視化の観点でいえばPythonのライブラリよりもTableauはかなり優れています。
なによりレスポンスが速く、すいすいアドホック分析ができます。
一方、機械学習を実施する場合は、Pythonでやった方が楽だなぁという印象でした。
Tabpyで機械学習できなくもないですが、いろいろと制約が多いです。