100日後にエンジニアになるキミ - 79日目 - プログラミング - 機械学習について4

昨日までのはこちら

[100日後にエンジニアになるキミ - 76日目 - プログラミング - 機械学習について](https://qiita.com/otupy/items/6a4f960ba4df06e27050)

[100日後にエンジニアになるキミ - 70日目 - プログラミング - スクレイピングについて](https://qiita.com/otupy/items/f53e2e7d16adf80b5acf)

[100日後にエンジニアになるキミ - 66日目 - プログラミング - 自然言語処理について](https://qiita.com/otupy/items/5828ac7450c5ba8d6931)

[100日後にエンジニアになるキミ - 63日目 - プログラミング - 確率について1](https://qiita.com/otupy/items/0196897ae4d2134794fa)

[100日後にエンジニアになるキミ - 59日目 - プログラミング - アルゴリズムについて](https://qiita.com/otupy/items/f1a5e81633b868f3b5ed)

[100日後にエンジニアになるキミ - 53日目 - Git - Gitについて](https://qiita.com/otupy/items/0abaad82a7f3fea73f1f)

[100日後にエンジニアになるキミ - 42日目 - クラウド - クラウドサービスについて](https://qiita.com/otupy/items/d1765f1541a104ed6133)

[100日後にエンジニアになるキミ - 36日目 - データベース - データベースについて](https://qiita.com/otupy/items/7af17635d576677606dc)

[100日後にエンジニアになるキミ - 24日目 - Python - Python言語の基礎1](https://qiita.com/otupy/items/6d1e5453f530e07b7aec)

[100日後にエンジニアになるキミ - 18日目 - Javascript - JavaScriptの基礎1](https://qiita.com/otupy/items/2f71d5f9c754b29d9555)

[100日後にエンジニアになるキミ - 14日目 - CSS - CSSの基礎1](https://qiita.com/otupy/items/61a97863f6df14171994)

[100日後にエンジニアになるキミ - 6日目 - HTML - HTMLの基礎1](https://qiita.com/otupy/items/cd2315dcb38671cfded0)

今回は機械学習についてのお話の続きです。

## 機械学習のデータ加工流れについて

機械学習を取り入れる際の業務としては次のような流れになっていきます。

0.目的を決める
1.データ取得
2.データ理解・選択・加工
3.データマート(データセット)作成
4.モデル作成
5.精度検証
6.システム実装

このうち2-3の部分をデータの前処理などと言っています。

今回はこの前処理のうちのデータマート作成やっていきたいと思います。

### データマートの作成について

言語はPython
機械学習用のライブラリは`Pandas`や`Numpy`
可視化用のライブラリは`seaborn`,`matplotlib`を用います。


**ライブラリの読み込み**

```python
# ライブラリの読み込み
import pandas as pd 
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')

%matplotlib inline
```

**データの詳細**

今回用いるデータは`タイタニック号の乗船者名簿`です。

```
PassengerID:　乗客ID
Survived: 　　生存結果 (0=死亡 , 1=生存)
Pclass: 　　　乗客の階級(1が最も良いらしい)
Name: 　　 　 乗客の名前
Sex: 　　　　 性別
Age: 　　　　 年齢
SibSp 　　 　 兄弟、配偶者の数
Parch 　　　　両親、子供の数
Ticket 　　　 チケット番号
Fare 　　　　 乗船料金
Cabin 　　　　部屋番号
Embarked 　　 乗船した港
```

`titanic_train.csv`と言うファイルがあると想定してください。

**ファイルの読み込み**

`pandas`ライブラリでは`read_xxx`と言うファイル形式に合わせた読み込みメソッドがたくさんあるのでそれを用いてファイルを読み込みします。今回はCSVファイルなので`read_csv`です。

pandasライブラリは表形式のデータフレームと言うデータ形式を取り扱うライブラリです。
データフレームにファイルを読み込みします。

```python
# ファイルからデータの読み込み
file_path = 'data/titanic_train.csv'
train_df = pd.read_csv(file_path,encoding='utf-8')
train_df.head()
```

|  | PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked |
|--:|--:|--:|--:|:--|:--|--:|--:|--:|:--|--:|:--|:--|
| 0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22 | 1 | 0 | A/5 21171 | 7.25 | NaN | S |
| 1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
| 2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26 | 0 | 0 | STON/O2. 3101282 | 7.925 | NaN | S |
| 3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35 | 1 | 0 | 113803 | 53.1 | C123 | S |
| 4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35 | 0 | 0 | 373450 | 8.05 | NaN | S |

こんな感じのデータです。

前回はいろいろデータの中身をみて、どんなデータが使えそうなのかと言うところをやりました。
今回はその続きで、使えそうなデータを使って、機械学習用のデータに落とし込んでいきます。

**欠損値の確認**

データを読み込みした際に、データが無い場合はデータフレーム上では欠損値と言う扱いになります。

欠損値がどれくらいあるかは
`データフレーム.isnull().sum()`
で各列の欠損値の個数を確認できます。

```python
train_df.isnull().sum()
```
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

これを見るといくつかの列にのみ欠損値があるようですね。
「Age」「Cabin」「Embarked」に欠損があるようです

欠損値のある部分だけを表示してみましょう。

**条件に合致する行を抽出する**

`データフレーム[条件式]`

**欠損値のある行を抽出する**

`データフレーム[データフレーム['列名'].isnull()]`


```python
train_df[train_df['Embarked'].isnull()]
```
|  | PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked |
|--:|--:|--:|--:|:--|:--|--:|--:|--:|--:|--:|:--|:--|
| 61 | 62 | 1 | 1 | Icard, Miss. Amelie | female | 38 | 0 | 0 | 113572 | 80 | B28 | NaN |
| 829 | 830 | 1 | 1 | Stone, Mrs. George Nelson (Martha Evelyn) | female | 62 | 0 | 0 | 113572 | 80 | B28 | NaN |

`Embarked`列の値を見ると`NaN`になっていますね。
データフレーム上での欠損値は`NaN`と表示されます。

数値の欠損の場合は
・平均値
・中央値
・任意の値
などで欠損値を補完することが多いです。

`Embarked`などカテゴリ値は数値ではないので、なにかしらの数値で置き換えることは出来ません。

欠損値を埋めたい場合は`fillna`で補完することができます。

**欠損値を任意の値で補完する**

`データフレーム.fillna(補完値)`

その列の平均値などで補完したい場合は、まず平均値などを求めます。

**列の平均を求める**

`データフレーム['列名'].mean()`

**列の中央値を求める**

`データフレーム['列名'].median()`

```python
print(train_df['Fare'].mean())
print(train_df['Fare'].median())
```
32.2042079685746
14.4542

```python
# 年齢を平均値で補完する
train_df['Age'] = train_df['Age'].fillna(train_df['Age'].mean())
```

**カテゴリ値のベクトル化**

機械学習では計算に用いる値は基本的に全て数値で無ければなりません。
文字列で構成されるカテゴリ値の場合、一部のモデルを除いて多くの場合はそのままの形では機械学習のデータとして用いることが出来ません。

そのため`one-hotベクトル`としてカテゴリ値から数値に変換を行います。

**カテゴリ値をone-hotベクトルにする**

`one-hotベクトル`はカテゴリ値の列を作成し、値がその列名なら1、違えば0にしたデータです。

`pd.get_dummies(データフレーム[['列名']])`
※prefix='前置詞'を付けるとカラム名の前に名前を付けられる
これで新しいデータフレームを作ります。

```python
# 乗船場所のカテゴリ化
train_df["Embarked"] = train_df["Embarked"].fillna('N')
one_hot_df = pd.get_dummies(train_df["Embarked"],prefix='Em')
one_hot_df.head()
```
|  | Em_C | Em_N | Em_Q | Em_S |
|--:|--:|--:|--:|--:|
| 0 | 0 | 0 | 0 | 1 |
| 1 | 1 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 | 1 |
| 3 | 0 | 0 | 0 | 1 |
| 4 | 0 | 0 | 0 | 1 |

`Embarked`列は欠損があるので欠損を`N`に置き換えたのちカテゴリ値にしています。
カテゴリ値が有るところは 1 無いところは 0 で置き換えられたデータフレームを新たに生成します。

データの種類分の列を作ることになります。
データの種類が多すぎる場合はデータが素(ほとんど0)になってしまうので
ある程度種類が限られるものだけに絞ってカテゴリ変数を作るのが得策です。

**数値と文字列の変換**
データが文字列のものを数値に変更したり、数値のものを文字列に直したりして機械学習に使えるデータに直していきます。

`性別(Sex)`は文字列であるためこのままでは機械学習に用いることが出来ません。
文字列から数値に変換をしていきます。

`データフレーム['カラム名'].replace({値:値,値:値・・・})`

```python
# 性別の数値化(男性を0 , 女性を1)
train_df['Sex2'] = train_df['Sex'].replace({'male':0,'female':1})
train_df.head()
```
|  | PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | Sex2 |
|--:|--:|--:|--:|:--|:--|--:|--:|--:|:--|--:|:--|:--|--:|
| 0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22 | 1 | 0 | A/5 21171 | 7.25 | NaN | S | 0 |
| 1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38 | 1 | 0 | PC 17599 | 71.2833 | C85 | C | 1 |
| 2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26 | 0 | 0 | STON/O2. 3101282 | 7.925 | NaN | S | 1 |
| 3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35 | 1 | 0 | 113803 | 53.1 | C123 | S | 1 |
| 4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35 | 0 | 0 | 373450 | 8.05 | NaN | S | 0 |

新しく性別の列を作成しました。

数値から文字列に変換する際もreplaceが使えます。
列のデータ型を丸ごと変更する際は

`データフレーム['カラム名'].astype(np.データ型)`

**数値のカテゴリ化**

年齢を用いて年代を算出してみましょう。
年齢を10で切り捨て除算すると年代になります。
欠損のものは年齢無しとしてカラムを作成することも出来ます。

```python

# 年代のカテゴリ化
train_df['period'] = train_df['Age']//10
train_df['period'] = train_df['period'].fillna('NaN')
train_df['period'] = train_df['period'].astype(np.str)
period_df = pd.get_dummies(train_df["period"],prefix='Pe')
period_df.head()
```
|  | Pe_0.0 | Pe_1.0 | Pe_2.0 | Pe_3.0 | Pe_4.0 | Pe_5.0 | Pe_6.0 | Pe_7.0 | Pe_8.0 |
|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
| 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 3 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 4 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |


**データフレームの結合**

新しく作成したデータフレームを1つにまとめていきます。まとめるには`pd.concat`を用います。

`pd.concat([データフレーム,データフレーム],axis=1)`
※axis=1 は横に、axis=0は縦に結合する,結合する際のカラム数や行数は合わせておく必要がある。

```python

con_df = pd.concat([train_df,period_df],axis=1)
con_df = pd.concat([con_df,one_hot_df],axis=1)
con_df.head(1)
```
|  | PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | ... | Pe_3.0 | Pe_4.0 | Pe_5.0 | Pe_6.0 | Pe_7.0 | Pe_8.0 | Em_C | Em_N | Em_Q | Em_S |
|--:|--:|--:|--:|:--|:--|--:|--:|--:|:--|--:|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
| 0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22 | 1 | 0 | A/5 21171 | 7.25 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |

これで横方向にデータフレームを連結することができました。

**不要データの削除**

データフレームを結合する際に、変換前のデータは特に必要ありませんので元の列を削除します。

`データフレーム.drop(['列名'], axis=1)`

```python
data_df = con_df.drop(['PassengerId','Pclass','Name','Age','Ticket','Cabin','Embarked','period','Sex'], axis=1)
data_df.head()
```
|  | Survived | SibSp | Parch | Fare | Sex2 | Pe_0.0 | Pe_1.0 | Pe_2.0 | Pe_3.0 | Pe_4.0 | Pe_5.0 | Pe_6.0 | Pe_7.0 | Pe_8.0 | Em_C | Em_N | Em_Q | Em_S |
|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
| 0 | 0 | 1 | 0 | 7.25 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 1 | 1 | 1 | 0 | 71.2833 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| 2 | 1 | 0 | 0 | 7.925 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 3 | 1 | 1 | 0 | 53.1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 4 | 0 | 0 | 0 | 8.05 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |

全て数値のデータになりました。
このような形で最終的な機械学習用のデータとすることが出来ます。


## まとめ

本日はデータを加工して機械学習用のデータマートを作成しました。
本日紹介したのは加工手法のごく一部です。

まずはざっくりとした流れを覚えましょう。
そして、ある程度理解ができたら、より精度を高めるための工夫をしたり、新しい手法を試してみるのが良いかと思います。

君がエンジニアになるまであと21日

## 作者の情報
乙pyのHP：
http://www.otupy.net/

Youtube：
https://www.youtube.com/channel/UCaT7xpeq8n1G_HcJKKSOXMw

Twitter：
https://twitter.com/otupython