Amazon SageMakerでpythonデータサイエンス入門その2

#はじめに
本稿は Amazon SageMakerでPythonデータサイエンス入門 の連載記事になります。

その1. [Amazon SageMaker の環境構築、事前準備、基礎分析と可視化について](https://qiita.com/NendaiHiroki/items/bc4e6ccb6be88eca7a6b)
その2. 線形回帰を用いたお弁当の売り上げ予測　　←【本稿】
その3. [決定木を用いた銀行の顧客ターゲティング](https://qiita.com/NendaiHiroki/items/96606c17de2a864fd8ef)

以下の Udemy のコースをベースに記事を作成しています。
[【ゼロから始めるデータ分析】 ビジネスケースで学ぶPythonデータサイエンス入門 ](https://www.udemy.com/optworks_1/)

# 本稿の内容
- 線形回帰(単回帰、重回帰分析)を用いたお弁当の売り上げ予測
- 特徴量(説明変数)の作成

## 予測とは
データを基にある値がどのような値になるかを推定することです。
予測を行うには予測対象と予測を行う上で有用なデータが必要となります。
分析業務では予測対象を目的変数、有用なデータを説明変数と言う場合が多いです。

また本稿では線形回帰を利用したお弁当の売上予測を行います。


## 線形回帰
回帰とはデータから適する関数を抽出する方法のことです。
線形回帰は回帰分析の手法の中で1次関数を利用して予測モデルを導出する手法です。

# 線形回帰を用いたお弁当の売り上げ予測 
今回は skit-learn を用いて、線形回帰(単回帰分析、重回帰分析)を行います。

## 単回帰分析編
まずは、単回帰分析(説明変数が1つ)で、お弁当の売り上げを予測します。

- その1で作成した case1 フォルダーに新しい notebook を conda_python3 で作成してください。

#### モジュールのインポートと設定

```Python
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
from sklearn.linear_model import LinearRegression as LR
```

#### データ(csvファイル)の読み込み

```Python:read_csv.py
train=pd.read_csv("train.csv")
test=pd.read_csv("test.csv")
sample=pd.read_csv("sample.csv",header=None)
```

- trainの先頭行を確認しておきましょう。

```Python
train.head()
```
***実行結果***
<img width="467" alt="01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/52e2b1e7-aeba-3c1c-e4cb-182709d808b1.png">


#### 説明変数と目的変数の用意  
- 説明変数は train から temperature を選択し、変数 trainX に代入します。
- 目的変数は train から y を選択し、変数 y に代入します。


```Python
trainX = train["temperature"]
y = train["y"]
```
- test からも同じ説明変数の temperature を選択し、変数名を testX に代入します。

```Python
testX=test["temperature"]
```

- 説明変数のデータの形を整えます。  
※単回帰の場合のみ必要な操作となります。

```Python
trainX = trainX.values.reshape(-1,1)
testX = testX.values.reshape(-1,1)
```

#### 回帰モデルの作成
- 回帰モデルの箱を作る。  
モデルを作成するために用いる手法を変数(モデルの箱)に覚えさせます。
今回は model1 という変数にLinearRegression(線形回帰)という手法を覚えさせます。

```Python
model1 = LR()
```
- 単回帰モデルの作成  
()の中に、説明変数、目的変数の順番に書くことで、 model1 に格納されている手法でモデルを作成します。

```Python
model1.fit(trainX,y)
#実行結果
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
```

- 作ったモデルの傾き、切片を確認します。  



```Python
#傾き
model1.coef_
```

```Python
#切片
model1.intercept_
```

実行結果

```
array([-2.5023821])
```

```
134.79948383749922
```

#### お弁当の売り上げ予測を行う
- 予測を実行。  
予測結果は変数 pred に格納します。  
()内にはテスト用の説明変数 testX を入力すると、作成したモデル(model1)で y を予測します。

```Python
pred = model1.predict(testX)
```

- sample の中身を確認し、予測結果を代入します。
 sample[0] には日付( test の日付と同じ)が入っているので、 sample[1] に予測結果を代入します。  


```Python
sample.head()
```
実行結果
<img width="85" alt="02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/0ef958e9-ac71-ed02-6ee6-3723e8c88dc2.png">


```Python
sample[1] = pred
```

- sample を csv ファイルで書き出します。  
オプションには、"submit1.csv"(ファイル名)、index=None、header=Noneを書きます。

```Python
sample.to_csv("submit1.csv",index=None,header=None)
```

#### SIGNATE に結果を投稿し、確認
保存したcsvファイルをSIGNATEに投稿して、精度を確認します。

- [こちら](https://signate.jp/)からSIGNATEのサイトを開いて、Competitionsを選択。
<img width="821" alt="02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/97d3dbeb-9905-2e91-d99a-952769aad8b6.png">

- 練習問題を選択。
<img width="840" alt="03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/2f1c9d92-f06b-e5cc-19ca-9e698d329804.png">

- 下にスクロールしてお弁当の需要予測を選択。
<img width="840" alt="04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/4b7b4d8b-6312-e160-3108-836f04bc8066.png">

- 投稿を選択。
<img width="837" alt="05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/dd72fe42-b897-3290-4354-d8ab3c126323.png">

- ファイルは先ほど作成した submit1.csv を選択。
メモには使用した説明変数は temperature と書いておきます。
<img width="685" alt="06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/21b86e8e-984e-b65b-e56c-800aada7fdcc.png">

- これで投稿完了です。結果が出るまで2、3分あるので少し待ちましょう。
<img width="834" alt="07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/6741a419-bcda-f15c-69a8-63ee9f28fab3.png">


- 結果が出ました。
ここでは評価の数が小さいほど精度がいいということになります。
このモデルの精度は42ほどなので、まだまだというところです。
<img width="838" alt="08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/04afc40d-0c52-18df-9991-b23bd362f8eb.png">


### 別の説明変数で予測をする
先ほどは説明変数をtemperatureでモデル作成、予測を行いましたが他のカラムを説明変数にしたら結果が変わるだろうと考えます。
ここでは試しに説明変数をkcalにしてモデル作成、予測を行います。

#### 新たな説明変数を取り出す。  
- 説明変数として kcal を選択し、それぞれ trainX 、 testX に代入します。

```Python
trainX = train["kcal"]
testX = test["kcal"]
```
#### 欠損値の補間
- 欠損値の有無を確認します。

```Python
train.isnull().any()
```

実行結果

```
True
```

- 補間に用いる値を計算。  
今回は、補間に kcal の平均値を用います。

```Python
avg = trainX.mean()
avg
```

実行結果

```
404.4096385542169
```
- trainX と testX の欠損値を補間します。  

```Python
trainX = trainX.fillna(avg)
testX = testX.fillna(avg)
```

- trainXの中身をする。

```Python
trainX
```

実行結果

```
0      404.409639
1      404.409639
2      404.409639
3      404.409639
4      404.409639
5      404.409639
6      404.409639
7      404.409639
8      404.409639
9      404.409639
10     404.409639
11     404.409639
12     404.409639
13     404.409639
14     404.409639
15     404.409639
16     404.409639
17     404.409639
18     404.409639
19     404.409639
20     404.409639
21     404.409639
22     404.409639
23     404.409639
24     404.409639
25     404.409639
26     404.409639
27     404.409639
28     404.000000
29     462.000000
          ...    
177    396.000000
178    385.000000
179    423.000000
180    405.000000
181    404.409639
182    412.000000
183    400.000000
184    410.000000
185    396.000000
186    398.000000
187    380.000000
188    440.000000
189    408.000000
190    405.000000
191    380.000000
192    385.000000
193    460.000000
194    450.000000
195    385.000000
196    404.409639
197    438.000000
198    430.000000
199    395.000000
200    400.000000
201    395.000000
202    408.000000
203    394.000000
204    404.409639
205    404.000000
206    398.000000
Name: kcal, Length: 207, dtype: float64
```

#### モデルの作成と予測  
- ここから先の手順は先ほどの手順とほとんど同じなので、コードのみ記述します。

```Python
model2 = LR()  #モデル名をmodel2とする
model2.fit(trainX,y)
pred2 = model2.predict(testX)　#結果をpred2に代入
sample[1] = pred2
sample.to_csv("submit2.csv",index=None,header=None)  #ファイル名はsubmit2.csvで出力
```
#### SIGNATEに結果を投稿し、確認

- 先ほどと同じ手順で sample2.csv を投稿する。
メモには説明変数は kcal と書いておきます。
<img width="717" alt="09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/837d80d1-6524-55d1-1388-38f09d67d20d.png">

- 結果が出たら結果を確認。
 sample1.csv の結果と比べると若干精度が上がっていることがわかります。
このように説明変数を変えるだけで、同じ手法でも精度は変わってきます。
しかし、精度は39程度とまだまだです。
<img width="753" alt="10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/4fc41391-69a9-5dbe-c2ea-a48d30fa3335.png">


## 重回帰分析編
もっと良い精度のモデルを作成するために説明変数を増やして、重回帰分析をします。

データは単回帰分析で使ったデータと同じデータを使用します。
重回帰分析は別の notebook で行います。
同じ notebook で作業を行う方は、モジュールのインポートと設定、データの読み込みを飛ばしてください。

#### モジュールのインポートと設定

```Python:import.py
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
from sklearn.linear_model import LinearRegression as LR
```

#### データ(csvファイル)の読み込み。

```python:read_csv.py
train=pd.read_csv("train.csv")
test=pd.read_csv("test.csv")
sample=pd.read_csv("sample.csv",header=None)
```

- trainのweek列の各値がそれぞれいくつあるか確認します。

```Python
train["week"].value_counts()
#実行結果
木    43
水    43
火    41
金    41
月    39
Name: week, dtype: int64
```

#### 文字列データの加工(ダミ－変数化)  
- まず、pandasでダミー変数を作成する、get_dummies関数について説明します。  
 - get_dummies関数  
()内にカテゴリー変数(文字列)が含まれるデータを入力することで、カテゴリー変数をダミー変数(数値)に置き換えます。

```Python
pd.get_dummies(変数)
```

- 今回、説明変数は train から week と temperature を選択します。  
しかし、 week には文字列(カテゴリー変数)が含まれるため、その文字列をダミー変数(数値)に置き換えます。  
行末に.head()を付け加えて結果の確認もします。

```Python
  pd.get_dummies(train["week"]).head()
```

実行結果
<img width="84" alt="03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/7fa425ac-2e20-6c44-434f-aa176b0c32f8.png">


#### 説明変数と目的変数の取り出し
- 説明変数として、 train から week と temperature を抜きだし、ダミー変数化したものを変数 trainX に代入します。  
代入後、 trainX の中身を確認しておきます。

```Python
trainX = pd.get_dummies(train[["week","temperature"]])
trainX.head()
```

　　目的変数として、 train から y を取り出し、変数yに代入。

```Python
y = train["y"]
```

　　test からも同じ説明変数の week と temperature を選択し、同様の手順で変数名 testX に代入し、確認します。

```Python
testX = pd.get_dummies(test[["week","temperature"]])
testX.head()
```

実行結果
<img width="229" alt="04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/c2837657-3740-dab7-afc5-08cdd632000a.png">


#### モデルの作成と予測  
- ここから先の手順は単回帰分析の手順とほとんど同じなので、コードのみ記述します。

```Python
model1 = LR()  #モデル名はmodel1とする
model1.fit(trainX,y)
pred = model1.predict(testX)  #結果をpredに代入
sample[1] = pred
sample.to_csv("submit3.csv",index=None,header=None)  #ファイル名はsubmit3.csvで出力
```

#### SIGNATE に結果を投稿し、確認
- 先ほどと同じ手順で sample3.csv を投稿します。
メモには説明変数は week と temperature と書いておきます。
<img width="715" alt="14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/0d897ac7-181e-534a-c04b-71bf91e2d723.png">

- 結果が出たら結果を確認。
精度は kcal を説明変数にした時より、下がってしましました。
精度を上げるには説明変数をただ増やせばいいというわけではありません。
精度を上げるために、次は追加する説明変数をデータの特徴から考えて作ってみましょう。
<img width="755" alt="15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/193ece3d-b568-2378-a412-20a28804346e.png">


### 特徴量(説明変数)を作って解析してみる。

- お弁当の売り上げの特徴を確認します。  
 y の折れ線グラフを描く。  
見やすいように figsize=(12,5) としておきます。  
 x 軸が時間、 y 軸が売り上げ。

```Python
train["y"].plot(figsize=(12,5))
```
実行結果
<img width="406" alt="05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/e005dacb-142d-596a-6cf0-f4e33e0f8341.png">


　　グラフから時間が進むにつれて、売り上げが少なくなっていることがわかります。
　　先ほどの予測では時間に関する特徴量が入っていなかったので、時間に関する特徴量を作成します。

#### 特徴量の作成
- train の datetime から年と月のデータを取り出し、 train の新たなカラムとして追加します。 
その後 train の中身を確認する。  
 train の datetime は"年-月-日"という文字列で構成されています。
年、月のカラム名はそれぞれ year 、 month とする。
年と月を取り出すために apply 関数と split 関数、 lambda 式を使用します。
 -  apply 関数  
  指定された列の各値に関数を適用させる関数。
 -  split 関数  
 文字列を指定された区切り文字で区切ってリスト化する関数。
 -  lambda 式  
 無名関数  
  x を引数、 y を戻り値とした無名の関数を作れます。
 関数の引数として関数を使うときに用いられます。

```Python
データ.apply(関数)
```

```Python
文字列.split(区切り文字)
```

```Python
lambda x = y
```

-  train の datetime 列に対して apply 関数で各値に関数を適用します。  
適用する関数を無名関数として定義します。  
その無名関数の中では、入力として入ってきた文字列を"-"で区切ってリストにするために split 関数を使用。  
 split 関数で文字列を区切って作成されたリストの[0]にある年のデータを train の新しいカラム year として追加します。  
 month についても同様です。

```Python
train["year"] = train["datetime"].apply(lambda x :x.split("-")[0])
train["month"] = train["datetime"].apply(lambda x :x.split("-")[1])
train.head()
```  
実行結果
<img width="497" alt="06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/f493b7af-dd87-9176-a8ad-0c4a0fbfee83.png">

- test の説明変数にも同様の手順を行います。

```Python
test["year"] = test["datetime"].apply(lambda x : x.split("-")[0])
test["month"] = test["datetime"].apply(lambda x : x.split("-")[1])
```

- train のデータ型を確認します。

```Python
train.info()
```


実行結果

```
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 207 entries, 0 to 206
Data columns (total 14 columns):
datetime         207 non-null object
y                207 non-null int64
week             207 non-null object
soldout          207 non-null int64
name             207 non-null object
kcal             166 non-null float64
remarks          21 non-null object
event            14 non-null object
payday           10 non-null float64
weather          207 non-null object
precipitation    207 non-null object
temperature      207 non-null float64
year             207 non-null object
month            207 non-null object
dtypes: float64(3), int64(2), object(9)
memory usage: 22.7+ KB
```

-  train , test のデータの型を整数（ int ）に変換します。  
train.info() で見た通り、先ほど追加した year と month は数値ではなく、文字列として保存されています。  
データの型を変換したい場合は、astype 関数を使います。  
オプション(かっこの中)には変換先のデータの型を入れます。 

```Python
train["year"] = train["year"].astype(np.int)
train["month"] = train["month"].astype(np.int)
```
- データの型が変更されたか確認します。

```Python
train.info()
```
実行結果

```
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 207 entries, 0 to 206
Data columns (total 14 columns):
datetime         207 non-null object
y                207 non-null int64
week             207 non-null object
soldout          207 non-null int64
name             207 non-null object
kcal             166 non-null float64
remarks          21 non-null object
event            14 non-null object
payday           10 non-null float64
weather          207 non-null object
precipitation    207 non-null object
temperature      207 non-null float64
year             207 non-null int64
month            207 non-null int64
dtypes: float64(3), int64(4), object(7)
memory usage: 22.7+ KB
```

- testについても同様にデータ型を変更します。

```Python
test["year"] = test["year"].astype(np.int)
test["month"] = test["month"].astype(np.int)
```

#### 説明変数と目的変数の用意  
- train と test から説明変数の year 、 month を取り出し、新たなカラムとして追加します。

```Python
trainX = train[["year","month"]]
testX = test[["year","month"]]
```

- trainからyを取り出します。

```Python
y = train["y"]
```

#### モデルの作成と予測

- ここから先の手順は先ほどの手順とほとんど同じなので、コードのみ記述します。

```Python
model2 = LR()  #モデル名はmodel2とする
model2.fit(trainX,y)
pred = model2.predict(testX)  #結果をpredに代入
sample[1] = pred
sample.to_csv("submit4.csv",index=None,header=None)  #ファイル名はsubmit4.csvで出力
```

#### SIGNATE に結果を投稿し、確認

- 先ほどと同じ手順で sample4.csv を投稿する。
メモには説明変数は year と month と書いておきます。
<img width="716" alt="18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/b597f144-9abc-7f5c-fc7d-c546df22bbf7.png">

- 結果が出たら結果を確認。
今までより精度がかなり上がりました。
さらに精度を上げるために、新たな説明変数を追加してみましょう。
<img width="771" alt="19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/8041df7c-cee6-97b2-fc6c-af78fffa55ff.png">


### 追加する特徴量の検討
先ほどは折れ線グラフから特徴を確認し、特徴量の作成を行いました。
次は、別の方法で特徴を確認し、特徴量を追加します。

#### 実際のyの値と予測したyの差から特徴を確認する。
- trainXに対して予測を行い、trainXの予測値と、trainXの実際の値を比べることができます。  
まず、trainXから先ほど作ったモデルを利用して予測値を求めます。

```Python
pred = model2.predict(trainX)
```

- 予測値predを新たなカラムとして追加します。

```Python
train["pred"] = pred
```

- 予測値と実際の値を引き算した値を新たなカラムとして追加します。

```Python
train["res"] = train["y"]-train["pred"]
```

- 先ほど追加したカラムでソートして中身を確認します。  
大きな差があるデータからから共通する要素が見つけられれば、その要素を加えることで更に精度が良いモデルを作れる可能性があります。

```Pyton
train.sort_values(by="res")
```

実行結果
<img width="558" alt="08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/02c067cd-6259-d920-8f39-69fce24b660c.png">





- 実行結果を確認する。  
結果の下のほうを見ると、誤差が大きい行にはお楽しみメニューであるのがわかります。
<img width="557" alt="09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/3a2543e9-8fd3-a386-1bc6-6cb8f1ce11ed.png">

#### 新たな特徴量を追加する準備
- 特徴量としてお楽しみメニューを使いするにはお楽しみメニューを数値化する必要があります。   
具体的にはお楽しみメニューなら1、そうでないなら0という特徴量を作成したいと考えます。  
特徴量を作成するために、関数を作成します。  
関数の作成には def文 と if 文を使用します。
 - def文  
 自分で関数を作成できます。
 - if文  
 条件の結果によって処理を分けられます。

```Python
def 関数名(引数):
   関数の処理
```


```Python
if 条件式1:
  条件式1が真の時に実行する処理
elif 条件式2:
  条件式1が偽で条件式2が真の時に実行する処理
elif 条件式3:
  条件式1及び条件式2が偽で条件式3が真の時に実行する処理
else:
  全ての条件式が偽の時に実行する処理
```

- お楽しみメニューなら1、そうでないなら0という関数を作成します。

```Python
def jisaku1(x):
    if x=="お楽しみメニュー":
        return 1
    else : 
        return 0
```

- 作成した jisaku1 関数と apply 関数を使用して、新たなカラム fun を作成します。

```Python
train["fun"] = train["remarks"].apply(lambda x : jisaku1(x))
test["fun"] = train["remarks"].apply(lambda x : jisaku1(x))
```

- trainの中身を確認します。

```Python
train.sort_values(by="fun")
```

実行結果
<img width="559" alt="10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/8eaf1ed9-e2a5-6618-0547-b8040f8c8ecf.png">

- 説明変数の取り出し。  
今回は、year 、 month 、 fun 、 temperatureを説明変数として取り出します。

```Python
trainX = train[["year","month","fun","temperature"]]
testX = test[["year","month","fun","temperature"]]
```

#### モデルの作成と予測

- ここから先の手順は先ほどの手順とほとんど同じなので、コードのみ記述します。

```Python
model3 = LR()  #モデル名はmodel3とする
model3.fit(trainX,y)
pred = model3.predict(testX)  #結果をpredに代入
sample[1] = pred
sample.to_csv("submit5.csv",index=None,header=None)  #ファイル名はsubmit5.csvで出力
```


#### SIGNATE に結果を投稿し、確認
- 先ほどと同じ手順で sample5.csv を投稿する。
メモには、説明変数はyear、month、fun、temperatureと書いておきます。
<img width="714" alt="24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/a0b27ca3-1afd-505b-bd4a-054127b43ee1.png">

- 結果が出たら結果を確認。
今までで一番良い精度の予測ができました。
このように、関係する特徴量をよく考えて作ったり、追加したりすることで精度を上げることができます。
<img width="754" alt="25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/2c92ecc7-7e8e-7da5-dcc7-8e5a3f99dac3.png">

#おわりに
今回は実際にpythonでプログラミングをしながら、お弁当の売り上げ予測を線形回帰で行いました。
次の[その3](https://qiita.com/NendaiHiroki/items/96606c17de2a864fd8ef)では決定木を用いた銀行の顧客ターゲティングについてまとめていきますので、よろしくお願いします。