【備忘録】機械学習プロセス(教師あり学習・教師なし学習)

#1.機械学習の処理の手順

大まかな処理の流れは以下の8つのステップに分かれる

|#|ステップ|処理|使用ツール・ライブラリ|
|:--|:--|:--|:--|
|1|データ入手|スクレイピング、データの読み込み|Requests、BeautifulSoup、NumPy、Pandas|
|2|データ加工|前処理(欠損値の対応、データの整形、データの連結処理)|NumPy、Pandas|
|3|データ可視化|データの可視化|Matplotlib、Seaborn、Pandas|
|4|アルゴリズム選択|教師あり学習(分類・回帰)、教師なし学習(クラスタリング)|scikit-learn(train_test_split, SVM, DecisionTreeClassifier, RandomForestClassifier, LinearRegression, LogisticRegression, PCA, KMeans, AgglomerativeClustering)|
|5|学習プロセス|ハイパーパラメータの最適化|scikit-learn(train_test_split, GridSearchCV)|
|6|精度評価|ROC曲線、AUCの導出|scikit-learn(roc_curve, roc_auc_score, classification_report)|
|7|試験運用|(割愛)|-|
|8|結果利用|(割愛)|-|

各ステップで使用するライブラリとその使い方を解説

##1-1.データ入手

###1-1-1.スクレイピング

####1-1-1-1.HTMLのtable要素の内容を取得(Pandas)

```python:[In]
import pandas as pd

url = "https://example.com"
tables = pd.read_html(url)
df = tables[1] #サイト内の2つ目の表を抽出
df.head(20) #上位20項目を表示
```

####1-1-1-2.HTMLの任意の要素の内容を取得(Requests)

```python:[In]
import requests

r = requests.get("https://example.com")

text =r.text #要素のInnerTextを取得
for line in text.split('\n'): #要素を改行して分割表示
    if '</th>' in line or '</td>' in line:
        print(line.strip()) #ブランク、空白文字を削除して出力
```

####1-1-1-3.HTMLを構文解析し、要素を取得する(BeautifulSoup)

```python:[In]
import requests
from bs4 import BeautifulSoup

r = requests.get('')
soup = BeautifulSoup(r.text, 'html.parser')
```

###1-1-2.データの読み込み

```python:[In]
import pandas as pd

#data変数に格納されているDataFrame化
df = pd.DataFrame(data)
#CSVファイルの読み込み
df = pd.read_csv("pokemon_galar.csv") #書き込みの場合はpd.to_csvを使用
#Excelファイルの読み込み
df = pd.read_excel("pokemon_galar.xlsx") #書き込みの場合はpd.to_excelを使用
#DataFrameファイルの読み込み 
df = pd.read_pickle("pokemon_galar.pickle") #書き込みの場合はpd.to_pickleを使用
```

##1-2.データ加工

###1-2-1.前処理

####1-2-1-1.欠損値の対応

#####各要素が欠損値かどうかをBool値で返す(isnull)

```python:isnull
df.isnull()
```

#####欠損値の行を削除(dropna)

```python:dropna
df.dropna()
```

#####欠損値を別の値で補完する(fillna)

```python:[In]
#1つ手前の値で補完する
df.fillna(method='ffill')
#平均値で補完する
df.fillna(df.mean()) #中央値の場合はdf.median()、最頻値の場合はdf.mode().iloc[0, :]
```

#####欠損値を別の値で補完する(SimpleImputer)

```python:[In]
from sklearn.impute import SimpleImputer #バージョン0.20以降

#平均値で補完する
imp = SimpleIimputer(strategy='mean') #中央値の場合は'median'、最頻値の場合は'most_frequent'
imp.fit(df)
```

####1-2-1-2.データの整形

#####条件で抽出

```python:pandas
df["hp"] >= 100
df.loc[:, "hp"] >= 100
df.query('hp >= 100 and sp <= 90')
```

#####データ型の変換

```python:pandas
#データ型を確認する
df.dtypes
#int型をfloat型に変換する
import numpy as np
df.loc[:, "hp"] = df.loc[:, "hp"].astype(np.float32)
```

#####並べ替え

```python:pandas
df.sort_values(by="hp", ascending=False) #デフォルトはTrue(昇順)
```

#####不要なカラムの削除

```python:pandas
df = df.drop("hp", axis=1) #axis=1で列方向、axis=0で行方向
```

#####カラム名の変更

```python:pandas
df = df.rename(column={'HP': 'hp'}) #{'変更前': '変更後'}
```

#####組み合わせデータの挿入

```python:pandas
df.loc[:, "hp/total"] = df.loc[:, "hp"] / df.loc[:, "total"]
```

#####ダミー関数

```python:pandas
import pandas as pd
df_mod = pd.get_dummies(df.loc[:, "hp_index"], prefix="hp")
```

#####データ連結

```python:pandas
import pandas as pd
#行方向のデータ連結
df_merged = pd.concat([df_1, df_2], axis=0)
#列方向のデータ連結
df_merged = pd.concat([df, df_new_column], axis=1)
```

#####時系列データの扱い

```python:pandas
import pandas as pd
#1か月分のデータ
dates = pd.date_range(start="2020-01-01", end="2020-01-31")
#1年分365日のデータ
dates = pd.date_range(start="2020-01-01". periods=365)
#任意の曜日のデータ
sunday = pd.date_range(start="2020-01-01", end="2020-01-31", freq='W-SUN') #日曜日のみのデータ

#月平均のデータ
df.groupby(pd.Grouper(freq='M')).mean()
```

##1-3.データ可視化

###1-3-1.基本統計量

```python:statistics
#最大値
df.loc[:, "hp"].max()
#最小値
df.loc[:, "hp"].min()
#最頻値
df.loc[:, "hp"].mode()
#平均値
df.loc[:, "hp"].mean()
#中央値
df.loc[:, "hp"].median()
#標準偏差
df.loc[:, "hp"].std(ddof=0) #デフォルトはddof=1

#件数のカウント
df[df.loc[:, "hp"]>=100].count()
#要約
df.describe()
#相関係数
df.corr()
#DataFrame(Pandas)→ndarray(Numpy)変換 
df.loc[:, ["hp", "sp"]].values
```

###1-3-2.グラフ描画

####1-3-2-1.散布図行列

```python:pandas
from pandas.plotting import scatter_matrix
_ = scatter_matrix(df)
```

####1-3-2-2.Matplotlib

#####描画オブジェクト(fig)とサブプロット(ax)

```python:matplotlib
import matplotlib.pyplot as plt
#２つのサブプロットを配置
fig, axes = plt.subplots(2)
plt.show() 

#２行２列(４つ)のサブプロットを配置
fig, axes = plt.subplots(2, 2)
fig, axes = plt.subplots(nrows=2, ncols=2)
plt.show()
```

#####タイトル

```python:matplotlib
import matplotlib.pyplot as plt

fig, axes = plt.subplots(ncols=2)

#描画オブジェクトにタイトル設定
fig.subtitle('figure title')
#サブプロットにタイトル設定
axes[0].set_title('subplot title 0')
axes[1].set_title('subplot title 1')

plt.show()
```

#####軸ラベル

```python:matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

ax.set_xlabel('x label') #X軸にラベルを設定
ax.set_ylabel('y label') #Y軸にラベルを設定

plt.show()
```

#####凡例

```python:matplotlib
import matplotlib.pyplot as plt

ax.plot(x, y, label='legend label')
ax.legend(loc='best') #データとの重なりが最小の場所に凡例を表示
plt.show()
````

#####ファイル出力

```python:matplotlib
import matplotlib.pyplot as plt

fig.savefig('figure.png') #ファイル形式はpng, pdf, eps, svgが選択可能
```

####1-3-3-3.各種グラフの描画

#####折れ線グラフ

```python:matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

x = df["hp"]
y = df["total"]

ax.plot(x, y)

plt.show()
```

#####棒グラフ

(i)複数の棒グラフを並べて描画する場合

```python:matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

x1 = df["No."]
y1 = df["hp"]
y2 = df["speed"]

width = 0.4
ax.bar(x1, y1, width=width) #棒グラフの幅を０.４に設定

x2 = [num + width for num in x] #幅分ずらして棒グラフ2を描画
ax.bar(x2, y2, width=width)

plt.show()
```

(ii)積み上げ棒グラフを描画する場合

```python:matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

x = df["No."]
y1 = df["at"]
y2 = df["sp_attack"]

y_total = [num1 + num2 for num1, num2 in zip(y1, y2)]

ax.bar(x, y_total)
ax.bar(x, y2)

plt.show()
```

#####散布図

```python:matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

ax.scatter(df["hp"], df["total"], marker='v', label="hp-total") #下向き三角形
ax.scatter(df["attack"], df["total"], marker='^', label="attack-total") #上向き三角形
ax.scatter(df["defense"], df["total"], marker='s', label="defense-total") #正方形
ax.scatter(df["sp_attack"], df["total"], marker='*', label="sp_attack-total") #星形
ax.scatter(df["sp_defense"], df["total"], marker='x', label="sp_defense-total") #X

ax.legend()
plt.show()
```

#####ヒストグラム
```python:matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

x = df["total"]

n, bins, patches = ax.hist(x, orientation='vertical')#デフォルトはvertical、横向きに描画したい場合はhorizontal
#n:各ビンの度数
#bins:ビンの境界値
#patches:ビンを描画するための情報

plt.show()
```

#####箱ひげ図

```python:matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

x0 = df["hp"]
x1 = df["attack"]
x2 = df["defense"]

ax.boxplot(x0, x1, x2)

plt.show()
```

#####円グラフ

```python:matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

x0 = df.loc[0, "hp"] / df[0, "total"]
x1 = df.loc[0, "attack"] / df[0, "total"]
x2 = df.loc[0, "defense"] / df[0, "total"]

status = [x0, x1, x2]

ax.pie(status, startangle=90, counterclock=False) #上から時計回り
ax.axis('equal') #アスペクト比維持

plt.show()
```


##1-4.アルゴリズム選択

###1-4-1.分類(教師あり学習)

####1-4-1-1.テストデータと学習データの分割

```python:scikit-learn
from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3) #テストデータ:学習データ = 3:7に分割
```

####1-4-1-2.サポートベクタマシン

#####学習

```python:scikit-learn
from sklearn.svm import SVC

svc = SVC(kernel='linear', C=1e6) #線形分離、マージンの狭さ10^6

svc.fit(df["attack"], df["sp_attack"])
```

#####可視化

####1-4-1-3.決定木

#####学習

```python:scikit-learn
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)

tree = DecisionTreeClassifier(max_depth=3) #木の最大の深さを"3"に指定
tree.fit(x_train, y_train)
```

#####可視化

```python:scikit-learn
from pydotplus import graph_from_dot_data
from sklearn.tree import export_graphviz

dot_data = export_graphviz(tree, 
                           filled=True, #多く分類されたノードに着色する
                           rounded=True, #ノードの角を丸くする
                           class_names=['ordinary', 'semi-legendary', 'legendary'])
                           feature_names=['hp', 'attack', 'defense', 'sp_attack', 'sp_defense', 'speed']
                           out_file=None) #ファイルを介さずにデータを渡す

graph = graph_from_dot_data(dot_data)
graph.write_png('tree.png')
```

####1-4-1-4.ランダムフォレスト

```python:scikit-learn
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)

forest = RandomForestClassifier(n_estimators=100) #決定木の個数

forest.fit(x_train, y_train)
y_pred = forest.predict(x_test)
y_pred
```

###1-4-2.回帰(教師あり学習)
データの読み込み・学習

```python:scikit-learn
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split

df = pd.DataFrame(data)

x = df.loc[:, 0]
y = df.loc[:, 1]

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)

lr = LinearRegression() 
lr.fit(x_train, y_train) 

y_pred = lr.predict(x_test)
```

可視化

```python:scikit-learn
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

ax.scatter(y_pred, y_test) #x軸を予測値、y軸を実績値として予測の精度を散布図で可視化する
ax.plot((0, 50), (0, 50), linestyle='dashed', color='red') #45度線の描画

ax.set_xlabel('predicted value')
ax.set_ylabel('actual value')

plt.show()
```


###1-4-2-1.次元削減

###1-4-2-2.主成分分析

```python:scikit-learn
from sklearn.decomposition import PCA

pca = PCA(n_components=2) #新たな2変数に変換
x_pca = pca.fit_transform(df[:, "hp"], df[:, "total"])

fig, ax = plt.subplots()
ax.scatter(x_pca[:, 0], x_pca[:, 1])
plt.show()
```

###1-4-5.クラスタリング(教師なし学習)


####1-4-5-1.k-means
データの読み込み・学習

```python:scikit-learn
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

df = pd.read_csv("data.csv")

fig, ax = plt.subplots()
ax.scatter(df.loc[:, 0], df.loc[:, 1])

km = KMeans(n_cluster=2, init=ramdom, n_init=10)
#クラスタ数:2
#初期値:ランダムに乱数で生成
#k-means実行回数:10

y_km = km.fit_predict(df)
```

可視化

```python:scikit-learn
import numpy as np
fig, ax = plt.subplots()

#クラスタ1散布図
ax.scatter(df[y_km == 0, 0], df[y_km == 0, 1], s=50, edgecolor='black', marker='s', label='cluster1')

#クラスタ1中心
ax.scatter(np.mean(df[y_km == 0, 0]), np.mean(df[y_km == 0, 1]))

#クラスタ2散布図
ax.scatter(df[y_km == 1, 0], df[y_km == 1, 1], s=50, edgecolor='black', marker='o', label='cluster2')

#クラスタ2中心
ax.scatter(np.mean(df[y_km == 1, 0]), np.mean(df[y_km == 1, 1]))

ax.legend()
plt.show()
```




####1-4-5-2.階層的クラスタリング(凝集型)
データの読み込み

```python:scikit-learn
import pandas as pd
from sklearn.cluster import AgglomerativeClustering

df = pd.read_csv("data.csv")

ac = AgglomerativeClustering(n_cluster=3, affinity='euclidean', linkage='complete')
#最大クラスタ数:3
#リンケージ手法:complete

labels = ac.fit_predict(df)
labels
```

可視化

```python:scikit-learn
import numpy as np
from scipy.cluster.hierarchy import dendrogram #樹形図を描画するライブラリ

#子クラスタとの関係を抽出
children = ac.children_

#クラスタ間の距離を抽出
distance = np.arrange(children.shape[0])

#各データの観測番号
number_of_observations = np.arange(2, children.shape[0]+2)

#子クラスタ、クラスタ間の距離、観測番号を列方向に結合
linkage_matrix = np.hstack((children,
                            distance[:, np.newaxis],
                            number_of_observations[:, np.newaxis])).astype(float)

fig, ax = plt.subplots(figsize=(15, 3), dpi=300)
dendrogram(linkage_matrix, labels=np.arange(100), leaf_front_size=8, color_threshold=np.inf)
plt.show()
```


##1-5.学習プロセス

###1-5-1.ハイパーパラメータの最適化

####1-5-1-1.グリッドサーチ

##1-6.精度評価

###1-6-1.カテゴリの分類精度

|指標|説明|計算式||
|:--|:--|:--|:--|
|<b>適合率(precision)</b>|正例と予測したデータのうち、実際に正例の割合|真陽性/(真陽性+偽陽性)|偽陽性をなくしたい際に求める|
|<b>再現率(recall)</b>|実際の正例のうち、正例と予測したものの割合|真陽性/(真陽性+偽陰性)|偽陰性をなくしたい際に求める|
|<b>F値(F-Value)</b>|適合率と再現率の調和平均|2*適合率*再現率/(適合率+再現率)||
|<b>正解率(accuracy)</b>|正例、負例にかかわらず、予測が的中した割合|真陽性+真陰性/全データ||

###1-6-2.分類精度の出力

```python:scikit-learn
from sklearn.metrics import classification_report

print(classification_report(y_test, y_pred))
```

###1-6-3.交差検証

```python:scikit-learn
from sklearn.svm import SVC
from sklearn.model_selection import cross_val_score

svc = SVC()

x = df.loc[;, "hp"]
y = df.loc[:, "total"]

cross_val_score(svc, x, y, cv=10, scoring='precision')
#分割数：10
#評価指標：適合率(precision)
```

###1-6-4.ROC曲線の可視化

```python:matplotlib
import numpy as np
import matplotlib.pyplot as plt

fpr = df.loc[:, "false_positive_rate"].values()/ n1 #n1:偽陽性のデータの個数
tpr = df.loc[:, "true_positive_rate"].values()/ n2 #n2:真陽性のデータの個数

fig, ax = plt.subplots()
ax.step(fpr, tpr) #ROC曲線を描画
ax.set_xlabel('false positive rate')
ax.set_ylabel('true positive rate')
plt.show()
```

###1-6-5.AUC

AUCとは...ROC曲線の下側の面積の合計。1に近づくほど正例と負例の区別がつきやすくなり、0に近づくほど区別がつかなくなる。