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

昨日までのはこちら

[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 |

こんな感じのデータです。
データフレームでは行と列でデータの操作を行えます。

**データの確認**

まずはデータフレームの確認をします。
どんなカラムが有るかをみていきます。

```python

print(train_df.columns)
print(len(train_df.columns))
```
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
      dtype='object')
12

次にデータ型の確認です。pandasライブラリでは列ごとにデータの型が決まっています。
型に合わせて操作をする必要があります。

```python
train_df.dtypes
```
PassengerId      int64
Survived         int64
Pclass           int64
Name            object
Sex             object
Age            float64
SibSp            int64
Parch            int64
Ticket          object
Fare           float64
Cabin           object
Embarked        object
dtype: object

`object`は文字列などのデータ型、他は数値のデータ型になっています。

**列の参照**
データフレームでは列ごとにデータを参照することもできます。
1列の場合は文字列で、複数列の場合はリスト型の列名で指定するとそのデータだけを参照できます。

`データフレーム[列名]`
`データフレーム[[列名,列名]]`

**データフレームの基礎集計**

データをもらってまずやることといえば、基礎集計です。
pandasではデータフレームの基本統計量の算出を行うことができます。

```python
# 数値データの基礎統計
train_df.describe()
```
|  | PassengerId | Survived | Pclass | Age | SibSp | Parch | Fare |
|:--|--:|--:|--:|--:|--:|--:|--:|
| count | 891 | 891 | 891 | 714 | 891 | 891 | 891 |
| mean | 446 | 0.383838 | 2.308642 | 29.699118 | 0.523008 | 0.381594 | 32.204208 |
| std | 257.353842 | 0.486592 | 0.836071 | 14.526497 | 1.102743 | 0.806057 | 49.693429 |
| min | 1 | 0 | 1 | 0.42 | 0 | 0 | 0 |
| 25% | 223.5 | 0 | 2 | 20.125 | 0 | 0 | 7.9104 |
| 50% | 446 | 0 | 3 | 28 | 0 | 0 | 14.4542 |
| 75% | 668.5 | 1 | 3 | 38 | 1 | 0 | 31 |
| max | 891 | 1 | 3 | 80 | 8 | 6 | 512.3292 |

数値型の列の基本統計量が出ます。
データがどれくらいあるか、どんな感じのデータなのかを見ることができます。

**データの種類の確認**
文字列型のデータは基礎集計はできないので、どういうカテゴリのデータがどれくらいの量あるのかは
`value_counts`というメソッドで算出できます。

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

```python
train_df['Pclass'].value_counts()
```
3    491
1    216
2    184
Name: Pclass, dtype: int64

**グループバイ集計**

複数の列を用いて集計を行う場合は`groupby`を用います。

`データフレーム[['列名','列名','列名']].groupby(['列名','列名']).集計関数()`

```python
# 男女別で生存数の確認(0:死亡 , 1:生存)
train_df[['Sex','Survived','PassengerId']].groupby(['Sex','Survived']).count()
```
|  |  | PassengerId |
|:--|:--|--:|
| Sex | Survived |  |
| female | 0 | 81 |
|  | 1 | 233 |
| male | 0 | 468 |
|  | 1 | 109 |

グループバイ集計では様々な集計を行えます。(平均、最小値、最大値など)

これで見ると、女性の生存割合と、男性の生存割合ではかなり差が出ているように見えます。
男性は`Survived=0`が多く圧倒艇に亡くなっているのが見えます。

**クロス集計**

複数列をまとめてクロス集計(個数のカウント)を行うことが出来ます。

`pd.crosstab(データフレーム[列名],データフレーム[列名])`

`pd.crosstab([データフレーム[列名],データフレーム[列名]],[データフレーム[列名],データフレーム[列名]])`
※margins=True をつけると合計も表示する

```python
pd.crosstab([train_df['Sex'], train_df['Survived']], train_df['Pclass'])
```
|  | Pclass | 1 | 2 | 3 |
|:--|:--|--:|--:|--:|
| Sex | Survived |  |  |  |
| female | 0 | 3 | 6 | 72 |
|  | 1 | 91 | 70 | 72 |
| male | 0 | 77 | 91 | 300 |
|  | 1 | 45 | 17 | 47 |


```python
pd.crosstab([train_df['Sex'], train_df['Survived']], [train_df['Pclass'], train_df['Embarked']],margins=True)
```
|  | Pclass | 1 |  |  | 2 |  |  | 3 |  |  |
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|  | Embarked | C | Q | S | C | Q | S | C | Q | S |
| Sex | Survived |  |  |  |  |  |  |  |  |  |
| female | 0 | 1 | 0 | 2 | 0 | 0 | 6 | 8 | 9 | 55 |
|  | 1 | 42 | 1 | 46 | 7 | 2 | 61 | 15 | 24 | 33 |
| male | 0 | 25 | 1 | 51 | 8 | 1 | 82 | 33 | 36 | 231 |
|  | 1 | 17 | 0 | 28 | 2 | 0 | 15 | 10 | 3 | 34 |
| All |  | 85 | 2 | 127 | 17 | 3 | 164 | 66 | 72 | 353 |


こういった列ごとの集計を行ったりして、どんなデータなのかをのぞいていきます。

**データの可視化**

`matplotlib`を用いるとデータの可視化を行うことが出来ます。

図表はヒストグラムや棒グラフ、散布図などをデータから作ることが出来ます。
ヒストグラムを描いてみましょう。

ヒストグラムは横軸を値、縦軸をその個数、とした際の図表になります。
ビンはデータを段階で区分けする際の段回の数です。

**ヒストグラムを表示する**

`データフレーム['列名'].plot(kind='hist',bin=ビンの数)`

`データフレーム['列名'].hist(bin=ビンの数)`

※figsize=(横幅, 縦幅)を加えると図表のスケールを変更できる

```python
train_df['Fare'].plot(figsize=(16, 5),kind='hist',bins=20)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/a9f659d9-0601-5d20-48a4-17c4d5c97ac4.png)

```python
train_df['Age'].plot(figsize=(16, 5),kind='hist',bins=20)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/a55647c8-bcb0-0262-d64c-efb7b0b71762.png)

**条件指定**

データフレームに抽出条件を加えることで、可視化するものを層別で分けることが出来ます。

`データフレーム[条件式]['列名']`
※データフレームの条件式は下記の記号で複数条件をつなげることが出来る

|条件|記号|
|:--:|:--:|
|and条件の場合| &|
|or条件の場合| &#124; |
|not条件の場合| ~|

```python
# 条件指定でデータフレームを抽出する
train_df[train_df['Survived']==0]['Age'].head()
```
0    22.0
4    35.0
5     NaN
6    54.0
7     2.0
Name: Age, dtype: float64

```python
# 年齢別で生存者と死亡者を分けてヒストグラムを描く
train_df[train_df['Survived']==0]['Age'].hist(figsize=(16,5),bins=16,color="#F8766D", alpha=0.3)
train_df[train_df['Survived']==1]['Age'].hist(figsize=(16,5),bins=16,color="#5F9BFF", alpha=0.3)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/c02b0611-5ece-f354-9aa7-ad71dde1784d.png)

```python
# 男性のみ年齢別で生存者と死亡者を分けてヒストグラムを描く
train_df[(train_df['Survived']==0)&(train_df['Sex']=='male')]['Age'].hist(figsize=(16,5),bins=10,color="#F8766D", alpha=0.3)
train_df[(train_df['Survived']==1)&(train_df['Sex']=='male')]['Age'].hist(figsize=(16,5),bins=10,color="#5F9BFF", alpha=0.3)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/e740c1ed-de0d-aeec-c170-f6baac32aafb.png)

```python
# 女性のみ年齢別で生存者と死亡者を分けてヒストグラムを描く
train_df[(train_df['Survived']==0)&(train_df['Sex']=='female')]['Age'].hist(figsize=(16,5),bins=10,color="#F8766D", alpha=0.3)
train_df[(train_df['Survived']==1)&(train_df['Sex']=='female')]['Age'].hist(figsize=(16,5),bins=10,color="#5F9BFF", alpha=0.3)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/3978c771-ab43-cb02-d0c2-bb645a94bcff.png)

いろいろな集計の結果を可視化してあげると違いが見やすくなり、データの中から予測に効きそうな物が発見しやすくなります。

男性の分布と女性の分布では生死にかなり差が出ているのが分かります。

次に散布図を表示してみます。

**散布図を表示する**

散布図は`scatter`です。二つの数値を用いての可視化なので何かしらの数値の列を二つ指定をします。

`データフレーム[['列名','列名']].plot(x='横方向の列名', y='縦方向の列名' , kind='scatter')`
※figsize=(横幅, 縦幅)を加えると図表のスケールを変更できる
日本語は設定しないと表示されないので注意

```python
train_df[['Fare','Age']].plot(x='Fare', y='Age', figsize=(16, 9),kind='scatter')
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/a5c167ef-64c7-1156-7a9b-6a59a030726a.png)

`性別`で色づけしたいのですが、データは文字列なので数値する必要があります。
1列追加して、種別を数値に直した値を代入します。

**データフレームの列の追加**

`データフレーム['列名'] = 値`

**データフレームの文字の置き換え**

`データフレーム.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 |

一番最後に性別を0,1にした列が出来上がります。

これで`性別`を数値にした値の列が追加できました。

色づけして散布図を書いてみます。

```python
# 散布図の色づけ(男性を0 , 女性を1)
train_df[['Fare','Age']].plot(x='Fare', y='Age', figsize=(16, 5),kind='scatter',c=train_df['Sex2'],cmap='winter')
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/69a69249-ed82-eed3-8ad5-1e3a021321b0.png)

生存者と死亡者で色分けしてみましょう。

```python
# 散布図の色づけ(0:死亡 , 1:生存)
train_df[['Fare','Age']].plot(x='Fare', y='Age', figsize=(16, 5),kind='scatter',c=train_df['Survived'],cmap='winter')
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/fd94a927-768a-095c-6741-c29ad03d50b2.png)

性別と生死で色分けした際のものがかなり似ているように感じられると思います。


**seabornでの可視化**

`matplotlib`をもう少しきれいに表示させるためのライブラリで`seaborn`と言うものが有ります。
`seabornラ`イブラリでは`matplotlib`では表現できない可視化の方法も有ります。

数値型のデータ同士の散布図をまとめて表示します。

`sns.pairplot(データフレーム , hue"文字列型の種別の列")`

```python
# 欠損値があると表示できないものも有るので、一旦欠損値を平均値で埋める
for name in ['Age']:
    train_df[name] = train_df[name].fillna(train_df[name].mean())

train_df['Survived2'] = train_df['Survived'].replace({0:'death',1:'survived'})
train_df['Sex2'] = train_df['Sex'].replace({'male':0,'female':1})

# pairplot
# データ系列同士の相関性などを確認
# 同データ同士の場合は棒グラフ、異なる場合は散布図を表示
sns.pairplot(train_df[['Pclass','Age','Fare','Sex2','Survived2']], hue="Survived2")
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/bedf3077-8e30-7b15-c809-240a291aecd6.png)


**factorplot**

`factorplot`は要因別で図表を振り分けることが出来ます。

`factorplot(x='横方向カラム名', y='縦方向カラム名', hue='種別用カラム名', col='カラム名', data=データフレーム)`

```python
# factorplot (種別比較図)
sns.factorplot(x='Pclass', y='Age', hue='Survived', col='Sex2', data=train_df)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/98422870-8909-d313-756a-7f4b0ba23205.png)

kind='box' を付け加えると箱ひげ図に変えることが出来ます。

```python
sns.factorplot(x='Pclass', y='Age', hue='Survived', col='Sex2',kind='box', data=train_df)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/c243a833-fbe4-102a-bc64-6830169e767c.png)

**lmplot**

2変数間の散布図と線形回帰線を可視化するグラフです。

`sns.lmplot(x=横軸のカラム, y=縦軸のカラム, hue='色づけするもの', data=データフレーム, x_bins=ビン値)`

```python
# 年齢層を指定
generations = [10,20,30,40,50,60,70,80]
# 客層、年齢ごとの生存率、および回帰直線を描画
sns.lmplot('Age', 'Survived', hue='Pclass', data=train_df,hue_order=[1,2,3], x_bins=generations)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379626/51326900-777c-34f0-97e2-20b842384b23.png)





## まとめ

データの理解では全体を集計してどんなデータがあるのかを把握し、層別で様々な可視化を試してみて
どんなデータの分布になっているのかをみていきます。

データをあらかた見終わった後は、どんなデータが予測に使えそうなのかを整理して
ここから、機械学習に使えるデータに直していきます。

まずはざっくりとした流れを覚えましょう。


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

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

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

Twitter：
https://twitter.com/otupython