【signate】お弁当の需要予測チュートリアル　〜単回帰分析編〜

signateと呼ばれる日本版kaggleのようなサイトで、勉強用に提供されているデータを使用して、お弁当の需要予測をしていきたいと思います。

コンペの概要
>
千代田区四番町のとある会社のカフェフロアで販売されているお弁当の販売数を予測するモデルを作成していただきます。
データの期間は、お弁当の販売を開始した 2013年11月18日 から 2014年11月30日 まで（土日祝を除く平日）です。 
学習用データ期間：2013年11月18日 ～ 2014年 9月30日 
検証用データ期間：2014年10月 1日 ～ 2014年11月30日 
 
使用しているデータは[こちら](https://signate.jp/competitions/24)にあります。

# この記事の概要
お弁当の需要予測を`単回帰分析`を用いて実践してみます。
→`重回帰分析`を用いるパターンは[別記事](https://qiita.com/takapy_AI/items/834558aa182e3cf6a8b4)にしたいと思います。
最後には、signateへデータを提出し、単回帰分析と重回帰分析（[こちら](https://qiita.com/takapy_AI/items/834558aa182e3cf6a8b4)）でどの程度、精度に差が出るのかを検証してみます。

# モデリングの手順
まず初めに、一般的（？）なモデリングの手順を書いておきます。
1. データの準備
　- 欠損値などの前処理とか
　- 特徴量抽出（精度に影響する変数を選定）
2. モデルの準備（今回は単回帰モデルと重回帰モデルを使用する）
3. モデルの作成
4. 予測
5. 評価

# 単回帰分析（説明変数を温度で）
まずは、温度から需要予測を立ててみます。
（その日の温度が、お弁当の売り上げに影響を与えているのではないか？という予測を立てた上で温度を使用します）

## 1. データの準備（データセットの読み込み）
冒頭で説明したデータセットをダウンロードし、プログラムと同じフォルダに配置後、以下コードを実行しデータフレームとして読み込みます。

```py
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from sklearn.linear_model import LinearRegression as LR # 線形回帰のモデル
%matplotlib inline # notebook上でグラフを表示するおまじない

train = pd.read_csv('train.csv') # 学習データの読み込み
test = pd.read_csv('test.csv') # 検証データの読み込み
sample = pd.read_csv('sample.csv', header=None) # 提出用サンプルデータの読み込み
```

データの中身を確認してみます。

```py
train.head() #先頭データ
train.describe() #基礎統計量
```
> 先頭データ
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/0c92cf0f-3398-c4d6-b960-b672fd09d39e.png)

> 基礎統計量
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/15ce03ea-ab52-29b0-5358-aeb3dfc4fdb8.png)

基礎統計量を見てみると、kcalには欠損値があることが分かりますね。


このセクションでは、`temperature （温度）`を説明変数として、単回帰分析を行います。
目的変数は`y （その日の売り上げ）`です。
（次セクションで、`kcal（カロリー）`を説明変数として単回帰分析してみた結果も載せています）

```py
# 学習用データ
# 説明変数に "temperature (温度)" を利用
X_train = train['temperature']
# 目的変数に "y (売り上げ個数)" を利用
y_train = train['y']

# 検証用（予測用）データ
X_test  = test['temperature']
```

そして、データの形状を変換します。**（単回帰分析ではこの変換が必要です）**
今のままだと下記のような形状なので、これを1行1列の配列に変換します。

```py
X_train.head()
# > 0    19.8
# > 1    17.0
# > 2    15.5
# > 3    15.2
# > 4    16.1
# > Name: temperature, dtype: float64

X_train.shape
# > (207,)
```

変換

```py
X_train = X_train.values.reshape(-1,1)
X_test = X_test.values.reshape(-1,1)
```

`reshape(行,列)`と指定することで、指定した配列へと変換されます。
ここでは行に`-1`を指定して、行数は元の形状から推測するようにしています。
少しわかりにくいですが、公式ドキュメントには下記のように記載されています。
>新しい形状は、元の形状と互換性がなければなりません。整数の場合、結果はその長さの1次元配列になります。 1つの形状寸法は-1とすることができる。この場合、値は配列の長さと残りの次元から推測されます。

また、[こちら](https://qiita.com/guitar_char/items/deb49d5a433a2c8a8ed4)の記事で`-1`について詳しく解説されていますので、よければ参考になさってください。

なお、変換後の形状はnumpyのndarray型になります。
dataframeではないので、`head()`関数は使えません。

```py
X_train[:5]
# > array([
# >    [19.8],
# >    [17. ],
# >    [15.5],
# >    [15.2],
# >    [16.1]
# > ])

X_train.shape
# > (207, 1)
```
行数は元の`207`のまま、列数が`1`になりました。


## 2. モデルの準備
今回は回帰モデルを使用します。
冒頭でimportした`LinearRegression`を使ってモデルの定義をします。

```py
# モデルの定義
model = LR()
```

## 3. モデルの作成
単回帰モデルを生成します。
モデルの作成とは、データの説明変数と目的変数を用いて、モデルに学習させることです。

```py
model.fit(X_train, y_train)
```

線形モデルは、モデル作成時に引数として下記のようなオプションを指定することもできます。

|指定オプション        |概要              |
|:-----------------|:-----------------|
|fit_intercept|False に設定すると切片を求める計算を含めない。目的変数が原点を必ず通る性質のデータを扱うときに利用。 (デフォルト値: True)|
|normalize|True に設定すると、説明変数を事前に正規化します。 (デフォルト値: False)|
|copy_X|メモリ内でデータを複製してから実行するかどうか。 (デフォルト値: True)|
|n_jobs|計算に使うジョブの数。-1 に設定すると、すべての CPU を使って計算します。 (デフォルト値: 1)|

ここで、学習後モデルの傾きや切片を確認してみます。

```py
model.coef_ #傾き
# > array([-2.5023821])

model.intercept_ #切片
# > 134.79948383749922
```

上記の結果から、今回の線形モデルは
$$ y = -2.5023821x + 134.79948383749922 $$
という数式で表すことができる、ということが分かります。

おさらいですが、この数式での`y`は予測する売上数で、
`x`は温度を示しています。

試しに、このモデルの制度がどれくらいか検証してみましょう。

```py
# 検証（決定係数の計算）
model.score(X_train, y_train)

# > 0.4294602815194137
```
ちなみに決定係数とは何かと言うと・・・
>
独立変数（説明変数）が従属変数（被説明変数=目的変数）のどれくらいを説明できるかを表す値である。寄与率と呼ばれることもある。標本値から求めた回帰方程式のあてはまりの良さの尺度として利用される。

と定義されています。
**1に近ければ近いほど、説明変数を使って目的変数を表現できている**ことを表しているようです。

## 4. 予測

では、実際に売上数を予測してみます。
予測には`predict`関数を使用します。

```py
pred = model.predict(X_test)
print(pred)

# > [ 84.25136537  74.99255159  62.9811175   80.99826864  79.49683938
# >   76.49398085  78.49588653  69.48731097  67.73564349  95.76232304
# >   88.25517673  77.24469548  74.99255159  84.25136537  96.01256125
# >   98.01446693  88.00493852  77.99541011  87.00398568  85.75279463
# >   85.75279463  87.75470031  87.5044621   94.7613702   86.75374747
# >   84.50160358  81.74898327  98.01446693  98.76518156  86.00303284
# >   95.26184662  99.01541977  94.51113199  97.51399051 108.52447176
# >   97.51399051 106.27232787 111.7775685   97.01351409  96.51303767]
```

ここで、予測に使用した`X_test`には何が入っていたかというと、
検証用データ`test.csv`の温度データでしたね。

`X_test`の中身を確認してみると、最初のデータは`20.2`となっています。
これを先ほどの数式に当てはめて計算してみると・・・

```math
\begin{align}
y &= -2.5023821 * 20.2 + 134.79948383749922 \\
&= -50.5481184 + 134.79948383749922 \\
&≒ 84.25136
\end{align}
```
と、確かに計算されていることが分かります。

## 5. 評価
最後に、signateに提出してモデルを評価するためにCSV出力を行います。

まずは提出用サンプルファイル`sample.csv`の形状を確認してみます。

```py
sample.head()
```
>　1列目に日付、2列目に売り上げ数を設定すれば良いことが分かる
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/2a075c71-1e27-bf0c-095b-c2a47486d39f.png)

予測値をsample[1]を予測した売上数で置き換えCSVファイル出力します。

```py
sample[1] = pred
sample.head()
```
> 予測した数値が設定されていることが分かります
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/81734a41-f654-4c2b-e4f0-6d0b412b9f35.png)

CSV出力するには`to_csv'関数を使用します。

```py
sample.to_csv('submit1.csv', index=None, header=None)
```

また、今回はデータの都合上行なっていませんが、このフェーズで検証用データセットに関する
決定係数を確認したりもします。（汎化性能の検証）

```py
# 今回は模擬的に変数を設定しています
model.score(X_test, y_test)
```
ここで、温度を用いた単回帰分析は終了です。
作成されたCSVファイルですが、この後のカロリーで単回帰分析で作成するCSVと一緒に
signateに提出しようと思います。

# 単回帰分析（説明変数をカロリーで）
基本的には前半部分と一緒ですが、`kcal`には欠損値があったため、そこをデータの前処理で補う必要があります。
前半部分と異なる部分だけ補足を入れながら解説します。

## 1. データの準備（データセットの読み込み）
まずはデータの確認をします。

```py
train.head() #先頭データ
train.describe() #基礎統計量
```
> 先頭データ
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/0c92cf0f-3398-c4d6-b960-b672fd09d39e.png)

> 基礎統計量
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/15ce03ea-ab52-29b0-5358-aeb3dfc4fdb8.png)

記事冒頭でも説明しましたが、`kcal`には欠損値があります。（画像でいうNaN）
欠損しているデータに関して、今回は`kcal`の平均値で補い、それを説明変数として使用します。

```py
# kcalの平均値を算出
avg = train['kcal'].mean()

# 学習用データ
# 説明変数に "kcal (カロリー)" を利用
X_train = train['kcal'].fillna(avg)
# 目的変数に "y (売り上げ個数)" を利用
y_train = train['y']

# 検証用（予測用）データ
X_test = test['kcal'].fillna(avg)
```

データの形状を変換

```py
X_train = X_train.values.reshape(-1,1)
X_test = X_test.values.reshape(-1,1)
```

## 2. モデルの準備

```py
# モデルの定義
model2 = LR()
```
## 3. モデルの作成

```py
model2.fit(X_train, y_train)
```

学習後モデルの傾きや切片を確認してみます。

```py
model2.coef_ #傾き
# > array([0.13195178])

model2.intercept_ #切片
# > 33.26061577029441
```
温度を説明変数としてモデル構築した時とは値が違うことが分かります。

決定係数も確認してみます。

```py
# 検証（決定係数の計算）
model2.score(X_train, y_train)

# > 0.01151900743440537
```
温度の時と比べ、極端に値が小さいです。

## 4. 予測

```py
pred2 = model2.predict(X_test)
print(pred2)

# > [88.68036439 88.02060548 86.70108765 86.04132874 89.99988221 88.68036439
# >  90.65964112 86.62318841 87.096943   92.63891786 86.04132874 88.02060548
# >  89.47207508 87.36084656 88.02060548 89.73597865 86.62318841 86.96499122
# >  89.3401233  89.99988221 89.47207508 90.79159291 91.31940004 89.3401233
# >  87.36084656 86.62318841 87.62475013 89.47207508 88.41646082 85.51352161
# >  89.60402686 90.39573756 87.22889478 89.99988221 88.81231617 86.62318841
# >  93.95843568 88.02060548 88.68036439 88.15255726]
```

## 5. 評価

CSV出力します

```py
sample[1] = pred2
sample.to_csv('submit2.csv', index=None, header=None)
```

# signateへ提出

signateの[コンペティションページ](https://signate.jp/competitions/24)に行き、提出します。

投稿ボタンをクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/df6713d2-468d-25d7-49f5-d617f673a916.png)

ファイルを選択し、然るべきものにチェックを入れて投稿ボタンを押下します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/51661f44-c667-3e37-5b88-e38a97d969fe.png)

同じように、`submit2`ファイルも提出します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193032/7db51678-2bca-011b-c33f-129494b1e99e.png)

２〜3分経つと、このように評価されると思います。

ここまでで一連の作業が終了です。

お疲れ様でした。

次は`重回帰分析`を用いて、お弁当の需要予測をしてみたいと思います。