機械学習を学びなおしてみた3（重回帰モデルでsubmit）

# はじめに
前投稿の[「機械学習を学びなおしてみた2（重回帰モデル）」](https://qiita.com/Sora_Yamamoto/items/200a695b10dbd473e891)の続きです。
今回は重回帰分析を使って実際にsubumitまでしてみます。

*注意：
細かい部分は行っていません。
データをロード➡submitまでを行っています。


# データの用意
今回も前回まで同様にsignateの練習用コンペの「自動車の走行距離予測」のデータを使用します。
実際に挑戦してみたい方は是非挑戦してみてください。
データ数も多くない為、最初の回帰分析の練習に最適と思います。


# 実践

### データのロード
まずデータをロードします。今回はtsvファイルなのでpandasのread_table関数を使用します。
```
df_train = pd.read_table("data/train.tsv")
df_test = pd.read_table("data/test.tsv")
```


### データの確認
続いてデータの状態を確認します。
```
display(df_train.info())
print("-----------------------------------------------------------------------------------------")
display(df_test.info())
```
![スクリーンショット 2023-10-22 222243.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/40e9d0ee-6554-e9c2-0bca-e4d060fb545f.png)

欠損値は見た感じ存在していませんね。（本気で挑む場合は各データについて異常値がないのか等は確認して下さい。）

Dtypesを確認すると、horsepower（馬力）がobjectなので少しおかしいですね。
確認してみます。
```
print(df_train["horsepower"].unique())
print("------------------------------------------------------------------------------------------")
print(df_test["horsepower"].unique())
```

![スクリーンショット 2023-10-22 222620.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/ebb94b3a-e631-0b9e-c273-73defe74b775.png)

値の１つが?になっている為、object型になっているようです。
数値型に直したいですが、このままだと直せないので一旦別の値に置き換えることで対応します。
今回は "?"➡"-1.0"➡-1.0➡Nan の流れで全体をfloat型にしつつ"?"を欠損値に変えていきます。
```
# ?を一旦置き換える
df_train["horsepower"] = df_train["horsepower"].replace({"?":"-1.0"})
df_test["horsepower"] = df_test["horsepower"].replace({"?":"-1.0"})

# 数値型に直す
df_train["horsepower"] = df_train["horsepower"].astype(float)
df_test["horsepower"] = df_test["horsepower"].astype(float)

# 欠損値に置き換える
df_train["horsepower"] = df_train["horsepower"].replace({-1.0:np.nan})
df_test["horsepower"] = df_test["horsepower"].replace({-1.0:np.nan})
```

続いてoriginに関してですが、こちらはsignateのサイトの方を見ると1:USA、2:Europe、3:Japanが割り振られているようなので置き換えます。
```
# originの置き換え
origin_dict = {1:"USA", 2:"Europe", 3:"Japan"}
df_train["origin"] = df_train["origin"].replace(origin_dict)
df_test["origin"] = df_test["origin"].replace(origin_dict)
```

最後にcar nameについて見ていきます。
こちらはユニークな値が多いため、このまま特徴量として与えても意味のない物になってしまいます。
```
print(len(df_train["car name"]))
print(df_train["car name"].nunique())
# 199
# 167
```
car nameは"dodge aries se"の様な形式で当てられているので最初の修飾子だけを格納してみます。
最初の修飾子だけ取る方法は次のようにします。
```
df_train["car name"].apply(lambda x: x[:x.find(" ")]).unique()
```
![スクリーンショット 2023-10-22 223615.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/aa824061-91e3-670c-ac25-2e94b0cde873.png)

```
df_train["car name"] = df_train["car name"].apply(lambda x: x[:x.find(" ")])

df_train["car name"].nunique()
# 33
```
もう少し違った取り方をすればもっとまとめれそうですね。

最後に数値型の相関関係を見ておきます。
```
df_train.select_dtypes(include="number").corr()
```
![スクリーンショット 2023-10-22 223836.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/1b439e4d-4896-ceb8-2956-fb41f8bfd630.png)

cylinders、displacement、hosepower、weightはそれぞれ相関関係が強いため同時に学習させると多重共線性が心配されます。


### 数値型について
本来は多重共線性の強い特徴量については加工をするなどして対策をすべきですが、今回は従属変数に対して最も相関が強かったweightのみを採用して、他は削除する方針で行います。
weightのデータの分布について確認します。
下記の関数を定義しておけば簡単にデータの状態を確認できます。
```
# cylinders ~ weightは相関が高いのでmpgと最も相関のあるweightのみを使用
def draw_glaph(df,key):
    sns.distplot(df[key], fit=norm);
    fig = plt.figure()
    res = stats.probplot(df[key], plot=plt)
    
draw_glaph(df_train, "weight")
```
![スクリーンショット 2023-10-22 224339.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/af8df9e8-4b6e-0c6c-5e00-6a5affc861c6.png)

データの状態は酷くないのでこのまま続行します。


### カテゴリデータについて
カテゴリデータの分布についても見ていきます。
結論から言うと今回はoriginのみを採用してcar nameは削除します。
car nameは単純にOne-Hot-Encodingをすると次元数が多くなりすぎるためです。

カテゴリデータを従属変数のmpgとの関係を見る為に今回はcatplotを扱います。
```
sns.catplot(data=df_train, x="origin", y="mpg")
```
![スクリーンショット 2023-10-22 224728.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/5e970902-ac7d-2b20-1198-5a0aa96d8978.png)

日本の車は性能が良さそうですね。

```
sns.catplot(data=df_train, x="car name", y="mpg")
plt.xticks(rotation=90)
plt.show()
```
![スクリーンショット 2023-10-22 224835.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/1fbcad16-cb01-e008-0258-c348fd45d754.png)

同じモデルでも性能にはバラつきがある様です。
（計測したのが新車かどうかのデータがあればもっと綺麗にまとまりそうですね）

最後にmpg、origin、car nameの関係について見ていきます、
hueで指定しあげると綺麗に分けられます。
glaph = のように代入してあげないとX軸の回転が上手く動作しないようです。。
```
glaph = sns.catplot(data=df_train, x="car name", y="mpg", hue="origin", col="origin", col_wrap=3)
glaph.set_xticklabels(rotation=90)
plt.show()
```
![スクリーンショット 2023-10-22 225228.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491855/0e9d3e59-1e70-2d94-533e-7e392d7436ec.png)

originによってcar nameが決まる様なので、originのみ採用しようかと思います。


# 学習の実行
では実際にデータを加工してモデルに学習させていきたいと思います。

trainデータとtestデータを一緒に加工することで標準化やOne-Hot-Encodingなどの際のズレをなくしています。

testの"id"は最後のsubmitに使用するので残しています。

```
df_train = pd.read_table("data/train.tsv")
df_test = pd.read_table("data/test.tsv")

df_train["test"] = 0
df_test["test"] = 1

df = pd.concat([df_train, df_test])

# originの置き換え
origin_dict = {1:"USA", 2:"Europe", 3:"Japan"}
df["origin"] = df["origin"].replace(origin_dict)

# 標準化
sc = StandardScaler()
sc.fit(df[["weight", "acceleration","model year"]])
df[["weight", "acceleration","model year"]] = sc.transform(df[["weight", "acceleration","model year"]])

# 特徴量の選択
use = ["id","mpg","weight","acceleration","model year","origin"]
df = pd.concat([df[use],df["test"]],axis=1)

# one-hot-encoding
df = pd.get_dummies(df, columns=["origin"])*1
df.drop("origin_Europe",axis=1,inplace=True)


# trainとtestの分割
df_train = df[df["test"] == 0].drop(["id","test"], axis=1)
df_test = df[df["test"] != 0].drop(["mpg","test"], axis=1)
```

```
X = df_train.drop("mpg",axis=1)
y = df_train["mpg"]

reg = LinearRegression()
reg.fit(X,y)
```


# 結果の提出
結果を提出します。
signateの提出方法は個人的には分かりづらかったので大分時間かかりました。。。。
```
pred = reg.predict(df_test.drop("id",axis=1))

df_test["mpg"] = pred

df_test[["id","mpg"]].to_csv("重回帰でsubmit.csv",index=False,header=False)
```

# 結果
提出した結果は3.399(RMSE)でした。ほとんど予測できてません。
単純に重回帰で予測しただけでは難しそうですね。
ちなみに単回帰でやってみた結果は4.226だったので単回帰よりは出来てそうです。

次回は何するか考え中です。。