SIGNATEのコンペに初挑戦！

# はじめに

Qiita初投稿！！

今回、プログラミング初心者である私は[SIGNATE](https://signate.jp/)で行われているコンペに初挑戦することにしました。

挑戦したコンペは、[【練習問題】都市サイクルの燃料消費量予測](https://signate.jp/competitions/129)です。

# コンペの概要
> 車の情報を元に、燃料消費量を予測します。

> 課題種別：回帰
> データ種別：多変量
> 学習データサンプル数：199
> 説明変数の数：9
> 欠損値：あり

与えられたデータ

* 学習用データ(train.tsv)
* 評価用データ(test.tsv)
* 応募用サンプルファイル(sample_submit.csv)

|カラム名|説明|データ型|
|:-----------:|:------------:|:------------:|
|id|インデックス|int|
|mpg|燃料消費量|int|
|cylinders|シリンダー|int|
|displacement|変位|int|
|horsepower|馬力(一部欠損)|int|
|weight|重量|int|
|acceleration|加速|float|
|model year|モデル年|int|
|origin|起源|int|
|car name|車名|varchar|

# 実践

早速実践です！

* 環境：google colab

## データの読み込み

SIGNATEにはデータを直接ダウンロードすることなくできる便利な機能「SIGNATE CLI」というものがあったため、こちらを使いました。

参照

* https://pypi.org/project/signate/
* https://qiita.com/john-rocky/items/dd2b7f4cd8e655a5376b#5signate%E3%81%A7%E3%82%84%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%82%B3%E3%83%B3%E3%83%9A%E3%81%AE%E4%B8%80%E8%A6%A7%E3%82%92%E5%8F%96%E5%BE%97
* https://qiita.com/insilicomab/items/d757339d97b44feaff01

上記を参照したため、この章は省略します。
また、各種ライブラリの読み込みはここでは省略することにします。

## 欠損値の処理

データの説明にもある通り、horsepowerに欠損値があることを確認します。

### trainデータ

```python:
train.info()
```

と実行すると

```
RangeIndex: 199 entries, 0 to 198
Data columns (total 10 columns):
#   Column        Non-Null Count  Dtype  
---  ------        --------------  -----  
 0   Unnamed: 0    199 non-null    int64  
 1   mpg           199 non-null    float64
 2   cylinders     199 non-null    int64  
 3   displacement  199 non-null    float64
 4   horsepower    199 non-null    object 
 5   weight        199 non-null    int64  
 6   acceleration  199 non-null    float64
 7   model year    199 non-null    int64  
 8   origin        199 non-null    int64  
 9   car name      199 non-null    object
```
と表示されます。

一見、欠損値がないようにも見えますが、コンペテーブルには

> 欠損箇所は文字列"?"で表記されています

と書かれていました！

では?はあるのか確認してみましょう。

```python:
train.isin(['?']).sum()
```
を実行してみると、

```
Unnamed: 0      0
mpg             0
cylinders       0
displacement    0
horsepower      3
weight          0
acceleration    0
model year      0
origin          0
car name        0
dtype: int64
```

たしかにありました！

では、この欠損値をどの値で埋めるか考えるべく、horsepowerの分布を見てみることにします。
horsepowerはobject型のようなので、どの文字列が頻出しているのかSeries型でみてみましょう。

```python:
train['horsepower'].value_counts(ascending = False)
```

```
88     11
110    11
100    10
90      9
97      8
       ..
125     1
138     1
153     1
82      1
137     1
Name: horsepower, Length: 72, dtype: int64
```

頻出度を降順(大きい順)にし、上からみてみると88と110が11回、100が10回登場していることがわかります。

今回は88で欠損値を埋めることにします。

```python:
#まず?をNaNに変換
train.replace('?', np.nan, inplace = True)

#88で欠損値を埋める
train['horsepower'].fillna('88', inplace = True)
```

最後に欠損値がなくなったか確認します。
```python:
train.isnull().sum()
```

```
Unnamed: 0      0
mpg             0
cylinders       0
displacement    0
horsepower      0
weight          0
acceleration    0
model year      0
origin          0
car name        0
dtype: int64
```

無事欠損値の処理が終わりました。

### testデータ

trainデータと同様の作業を行なうため、省略します。

## データの可視化

今回のコンペでは、

* 説明変数：mpg
* 目的変数：その他

となります。

従って、mpgと相関関係が強い変数を探すため、ヒートマップを作成することにします。

```python:
sns.heatmap(train.corr(), annot = True, cmap = 'bwr', linewidths = 0.2);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299817/0096e28e-29a5-1da1-d43c-f3c9d7bdfccd.png)

このような結果となりました。

mpgと高い相関がある変数は

* cylinders
* displacement
* weight

であることがわかります。

この変数を使って学習をするとよいのでしょうか。

しかしよくみると、
これらの変数は相互に高い相関関係にあります。
つまり、このまま使うと多重共線性が発生する可能性があります。

※多重共線性については、以下を参照いただきたい
https://www.ydc.co.jp/column/mi/mieruka04.html

従って、ここではmpgと最も相関が大きいweightのみに変数を絞ることにします。

## 機械学習

いよいよ機械学習の実装です。
今回は回帰問題であるため、LinearRegressionを用いることにします。

```python:
#trainデータをmpgとweightのみにする
train1 = train[['mpg', 'weight']]

X = train1.drop('mpg', axis = 1)
y = train1['mpg']

#学習データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)

#モデルの作成
model = LinearRegression()

#学習
model.fit(X_train, y_train)

#評価
print('決定係数(train):{:.3f}'.format(model.score(X_train, y_train)))
print('決定係数(test):{:.3f}'.format(model.score(X_test, y_test)))
```

```
決定係数(train):0.671
決定係数(test):0.767
```
テストデータのほうがスコアが高いため、過学習は起きていないと思われます。

最後に回帰曲線がどのようになったか示してみます。

```python:
print('\n回帰係数\n{}'.format(pd.Series(model.coef_, index=X.columns)))
print('切片: {:.3f}'.format(model.intercept_))
```

```
回帰係数
weight   -0.007107
dtype: float64
切片: 44.725
```


```math
mgh = 44.725 - 0.007107 * weight
```

のような式になったことがわかりました。


## 提出

最後にサンプルファイルを参考に、提出用にデータを加工していきます。

実は、私自身ここでかなり手こずりました(°ー°〃)

まずテストデータのweightを用いて、mgtを予測します。

```python:
#テストデータからweightを取り出す
X_test = test[['weight']].values

#予測データをpredictに格納
predict = model.predict(X_test)

#テストデータにpredictを追加
test['mpg'] = predict

#提出するファイル用に、Unnamed: 0(id)とmpg(予測)のみを取り出す
sub = test[['Unnamed: 0', 'mpg']]
```

ここでサンプルファイルを見てみます。

```
1	40.9
0	2	28.8
1	4	11.0
2	5	19.4
3	6	14.0
4	7	32.9
...	...	...
193	391	20.5
194	392	16.0
195	393	18.2
196	394	37.2
197	397	38.0
198 rows × 2 columns
```

* ヘッダーなし
* mpgは小数第一位まで(コンペの説明文にはmpgはint型と書いてありましたが、intにするとなぜかエラーになったため)
* インデックスなし(Unnamed: 0をidとして用いるため)

以上の点に気を付けて加工を行います。

```python:
#小数第一位までに丸める
sub = sub.round(1)
#csvに変換(インデックスなし、ヘッダーなし)
sub.to_csv('mpg.csv', index = False, header = None)
```

これで提出用のファイルが完成しました。

ちなみに、提出した結果はかなり悪かったです。
下から数えたほうが早い順位でした≡(▔﹏▔)≡

参照
https://www.kaggle.com/katotaka/kaggle-prediction-house-prices
37行目あたりからかなり参照させていただきました。

# 反省(備忘録)

* 特徴量の意味をしっかり捉える必要がある
* ドメイン知識がかなり重要
* 新たな変数を作ってもよかった


***
SIGNATEは日本の分析コンペプラットフォームであるため、kaggleよりは初心者の私でも気軽に取り組むことができました。
これからも勉強しつつ、コンペに参加し、そのアウトプットとしてこのqiitaに記録していこうと思います。

最後までお読みいただきありがとうございました。