特徴量エンジニアリングの手法としてのクラスタリング(とX-meansによる最適なクラスタ数の導入)

# はじめに
[前回][link-2]、Votingと言うアルゴリズムを使い、タイタニックに果敢に挑戦しましたが、あまりいい精度とは言えませんでした。

僕「なぜ精度が出ないんだ？ 最強のアルゴリズムを使っているはずなのに。」
[？？？][link-1]「特徴量エンジニアリングをするとよいでしょう。」


### ※注意※　
[機械学習のための特徴量エンジニアリング][link-1]の7章「非線形特徴量の生成：k-meansを使ったスタッキング」を読んだ勢いで書いた記事です。

## クラスタリングとは
教師なし学習と呼ばれる機械学習の手法の一つで、正解ラベルが与えられていないデータ群を分類してくれる手法です。

今回はこのクラスタリングにより、~~適当~~ 適切に処理したデータを分類し、新しい特徴量として学習させます。
※使用するライブラリはsklearnのKMeansになります。

# [タイタニック][link-3]でやってみる
##ソース
```python

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import StratifiedKFold
from sklearn.cluster import KMeans

# X-meansを使用しない場合不要です。
# Kaggleで使用する際、pip等で[pyclustering]をインストールする必要があります。
from pyclustering.cluster.xmeans import xmeans, kmeans_plusplus_initializer
from pyclustering.utils import draw_clusters

# 〜〜〜データ処理部〜〜〜

# 後工程でデータを分けるために使用
train["tmp"] = "train"
test["tmp"] = "test"

#　変数を生成するために一時的にtrainとtestを結合
conb = pd.concat([train.drop(["Survived","PassengerId"],axis=1),test.drop("PassengerId",axis=1)])

# データ処理のためにpandasからnp.arrayに変換
conb_array = np.array(conb[conb.columns[1]].tolist())
for i in range(len(conb.columns)):
    if i <= 1:
        continue
    if conb.columns[i]=="tmp":
        continue
    conb_array = np.vstack((conb_array,conb[conb.columns[i]]))
conb_array = conb_array.T

# クラスタリングを行います。
# 変数 cluster_point は分類するクラスタの数を指定します。
# 2以上の任意の整数を指定してください。
cluster_point = 10
pred = KMeans(n_clusters=cluster_point).fit_predict(conb_array)
conb["cluster"] = pred

# trainとtestに再分割
train["cluster"] = conb[conb["tmp"]=="train"]["cluster"]
test["cluster"] = conb[conb["tmp"]=="test"]["cluster"]
train.drop("tmp",axis=1,inplace=True)
test.drop("tmp",axis=1,inplace=True)
```
[ソース][link-5]

「あ、あれ・・・？ スコアが0.746までさがった・・・？」
※[前回][link-2]のスコアは0.765

## 原因を考える
「データ処理はクラスタの情報以外追加していないはず・・・」
「なのにスコアが大きく低下すると言うことは分類するクラスタ数が悪いのか？」
「適切なクラスタ数か。。。自動でやってくれるようなツールはないかな」

# X-meansについて
k-meansの拡張で、k-meansによる計算を複数回繰り返すことによって最適なクラスタ数を探し出すと言うアルゴリズム、、、みたいです(自分の理解)

## ソース
```python
# 後工程でデータを分けるために使用
train["tmp"] = "train"
test["tmp"] = "test"

#　変数を生成するために一時的にtrainとtestを結合
conb = pd.concat([train.drop(["Survived","PassengerId"],axis=1),test.drop("PassengerId",axis=1)])

# データ処理のためにpandasからnp.arrayに変換
conb_array = np.array(conb[conb.columns[1]].tolist())
for i in range(len(conb.columns)):
    if i <= 1:
        continue
    if conb.columns[i]=="tmp":
        continue
    conb_array = np.vstack((conb_array,conb[conb.columns[i]]))
conb_array = conb_array.T

# X-meansによるクラスタ数の計算
initial_centers = kmeans_plusplus_initializer(conb_array, 2).initialize()
instances = xmeans(conb_array, initial_centers, ccore=True)
instances.process()
clusters = instances.get_clusters()

# 各クラスタの中心の数を求める
centers = []
conb_array = np.array(conb_array)
for cluster in clusters:
    c = conb_array[cluster, :]
    centers.append(np.mean(c, axis=0))

# cluster_point = 10   
cluster_point = len(centers)

pred = KMeans(n_clusters=cluster_point).fit_predict(conb_array)
conb["cluster"] = pred

# trainとtestに再分割
train["cluster"] = conb[conb["tmp"]=="train"]["cluster"]
test["cluster"] = conb[conb["tmp"]=="test"]["cluster"]
train.drop("tmp",axis=1,inplace=True)
test.drop("tmp",axis=1,inplace=True)
```
[ソース][link-6]

「よっしゃ！スコアが0.765まで上がったぜ！」
「あれ？」
※[前回][link-2]のスコアは0.765

## まとめ
クラスタリングを用いた特徴量生成を行ってみました。
Qiita記事用に適当な前処理しか行っていなかったためか、パラメータチューニングが甘かったためか、
あまり精度は出ませんでしたが、他のコンペでは若干スコアが上がりました。

すべてのデータに当てはまるわけではないですが、一つの手段として覚えておく価値はあると思います。

# 参考文献
* 機械学習のための特徴量エンジニアリング  ――その原理とPythonによる実践
Alice Zheng、Amanda Casari　著、株式会社ホクソエム　訳
[リンク][link-1]

* 【Python】 x-meansを使ってクラスタリングする例
[リンク][link-4]

[link-1]:https://www.oreilly.co.jp/books/9784873118680/
[link-2]:https://qiita.com/sakabe/items/9dc3ecc228aab3ffd5fe
[link-3]:https://www.kaggle.com/c/titanic
[link-4]:http://ni4muraano.hatenablog.com/entry/2018/06/17/101807
[link-5]:https://www.kaggle.com/gsakabe/feature-engineering-by-k-means
[link-6]:https://www.kaggle.com/gsakabe/feature-engineering-by-x-means