PC超絶初心者が3か月プログラミング学んでみた

#［はじめに］
初めまして。PC超絶初心者のtencyouです。
どれくらい初心者かというと、今だにブラインドタッチできません:persevere:
それ位、初心者です！

今回はそんな私が、一大決心して3か月プログラミングを学んだ結果を書いていこうと思います。少しでも、PC初心者、プログラミング初心者の方の希望になればと思います。（PC初心者でも、これくらい出来るようになります）

##今回の環境
os windows
python 3
Jupyter Notebook

##今回やった事

* 過去2か月の実データのデータ成形
* 重回帰分析を使った売数の予測

##今記事の主な内容
今回、一番時間を割いた事は実際のデータ（私が働いている店の売上データを使いました）を分析に使えるDataFrameに直すところです。

機械学習を学ぶうえで、よくデータセットを使います。データセットは、データ量は多いですが目的変数と説明変数が分けやすいような形になっています。
しかし、実際のExelなどのデータはどうでしょうか？使用者が感覚的に使いやすいように表を作っているので目的値やその他のデータの行列の配置が様々です。（今回のデータがまさにそうでした。）また、説明変数となりそうと思っているデータが無かったりします。

今回の学習で痛感したのが、機械学習の主な作業はデータ収集とデータ成形だと言われる理由です。少ないデータですが一つ一つ、形を変えていくなかで、いろいろと調べたり悩んだりと良い勉強になりました。

基本的な動作ばかりですが、一部関数を定義してExelのデータを直しているので少しでも参考になれば幸いです。


##本文
今回のLibrary

```python
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Lasso
from sklearn.linear_model import Ridge
from sklearn.linear_model import ElasticNet
from sklearn.model_selection import train_test_split
from datetime import datetime
```

データ量が多くないので、今回は基本的なsklearnの重回帰分析で目標値とモデルの成果を比べていきたいと思います。

使用するExelのデータを読み込みます。

```python
df = pd.read_excel(r"C:\Users\user\OneDrive\デスクトップ\syokusu.2021.xlsx",sheet_name="Sheet6",header=1,dtype=str)
df2 = pd.read_excel(r"C:\Users\user\OneDrive\デスクトップ\syokusu.2021.xlsx",sheet_name="Sheet7",header=1,dtype=str) 
```

中身はこんな感じになっています。
![スクリーンショット 2021-08-21 014548.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1904465/4d1b4d0b-1fae-055c-3ecb-bafeb73101d1.png)
実際は、44 rows × 63 columnsとなります。
データの説明をすると、行にメニューが書いてあり列に一日おきの販売数と食材費（売上金額）が書かれています。

今回は、メニュー別の販売数を予測したいためメニューを列に置き換えたいと思います。

```
df = df.T
```

転置をして確認。
![スクリーンショット 2021-08-21 020515.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1904465/af3d5c35-0fd1-7aa2-497d-8c2c9a7ee539.png)

次に問題になったのが、列に販売数と売上が一緒になってしまったのでこれでは販売数のみ求めることができません。ここからが、今回一番悩んだところなのですが、売上の行のみを列に変えていきたいと思います。

まず、各メニューに対して販売数と売上のcolumnsを作ります。

```
columns = ["mainA_vol","mainA_pr","mainB_vol","mainB_pr",・・
```

関数で定義することで、今後のデータにも適応できるようにします。
###関数の内容
* 転置
* 欠損値の除去（threshで指定することで休日のみ削除）
* 新しく作ったDataFrameに売上を転置したデータを入れる

for文のなかで、一日分の販売数と売上を1マスとして転置を繰り返すといった感じです。

```python
def func(df):
    df = df.dropna(thresh=40)
    df = df.T
    global columns
    new_df = pd.DataFrame(index = list(range(31)),columns = columns )
    num = 0
    for i in range(1,len(df),2):
        tmp = df.iloc[i:i+2,:].values.T.ravel()
        new_df.iloc[num,:] = tmp 
        num +=1
        
    return new_df    
```
確認してみます。

```
func_df = func(df)
func_df
```
![スクリーンショット 2021-08-21 030645.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1904465/015a57af-a8df-b74d-83ed-a5d8ecb0e08f.png)

上手くいきました！

###データの追加
ここに、メニュー別の売上に関連のありそうな気温のデータと曜日を足していきます。気温のデータは、気象庁のHPからダウンロードすることが出来ます。
https://www.data.jma.go.jp/gmd/risk/obsdl/

今回は、平均気温と最高気温のみ指定してダウンロードしたデータを使っていきます。
それでも、余分な行列があったのでデータを結合できるように成形していきます。

```python
temperature_df = pd.read_csv(r"C:\Users\user\OneDrive\デスクトップ\data (6月).csv",encoding="shift jis",header=None,index_col=None)
temperature_df2 = pd.read_csv(r"C:\Users\user\OneDrive\デスクトップ\data (7月).csv",encoding="shift jis",header=None,index_col=None)

def func2(temperature_df):
    temperature_df = temperature_df.iloc[6:,[1,4]].reset_index(drop=True)
    return temperature_df

temperature_df = func2(temperature_df)
temperature_df2 = func2(temperature_df2)
```

axisを指定して横に結合します。

```python
df_concat1 = pd.concat([func_df, temperature_df], axis=1)
df_concat2 = pd.concat([func_df2, temperature_df2], axis=1)
```

曜日を取得するために、indexを日付に直し曜日を取得します。

```python
all_data = pd.concat([df_concat1,df_concat2])
all_data = all_data.rename(columns={1: 'average_tp',4:'highest_tp'})
index=pd.date_range("2021-06-01",periods=len(all_data),freq="D")
all_data.index=index

weekday = pd.date_range("2021-06-01",periods=len(all_data),freq="D").strftime('%A')
all_data["weekday"] = weekday
```
確認します。合わせることが出来ました。
![スクリーンショット 2021-08-21 033801.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1904465/12050449-ea47-4b6d-c2c4-392f18679ef4.png)

###モデルへ当てはめ
ここから、大詰めです！実力不足でここまで来るのに結構時間が掛かってしまいました。
このままでは、計算できないのでDataFrameの値を数値型に直していきます。
(今回は、メニューの中で一番気温と相関がありそうな麺を目的変数として設定し、利用者数、平均気温、最高気温、曜日を説明変数としました。）

曜日は、get_dummies()で0，1に変換しています。

```python
t = all_data["noodles_vol"].astype(int)
weekday = pd.get_dummies(all_data["weekday"])
cus_tp_data = all_data.astype({'cus':'int32','average_tp':'float32','highest_tp':'float32'})
cus_tp_data = cus_tp_data[['cus','average_tp','highest_tp']]
x = pd.concat([cus_tp_data,weekday],axis=1)
```

モデルに当てはめて、検証していきます。

```python
x_train,x_test,t_train,t_test = train_test_split(x, t, test_size=0.3,random_state=0)

for model in [LinearRegression(),Lasso(),Ridge(),ElasticNet()]:
    model.fit(x_train, t_train)
    print(f"train score:{model.score(x_train,t_train)}")
    print(f"test score:{model.score(x_test,t_test)}")
    y = model.predict(x_test)
    print(f"予測値:{y[5]}")
    print(f"目標値:{t_test[5]}")
```
目標の日付はランダムに当てはめてます。

###結果
![スクリーンショット 2021-08-21 040000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1904465/a1f4705e-cad9-4972-7ab0-56e98c349ca5.png)

データが少ない割には、どのモデルも割と良い結果だと思います。

今回はRidge回帰が良さそうですが、これからデータ量が増えていくとまた、適切なモデルが変わってくると思います。また、一年以上データを集めて時系列での分析もできればともっと実用的なものになるのではと期待しています。

###最後に
私は、この3か月間Aidemyで学ばせて頂きました。
正直、この3か月は毎日が寝不足でかなりきつかったです。しかし、プログラミングの基礎も何もかもわからない私でもここまで出来るようになりました。
しかし、ここからがスタートだとおもうので、これからも勉強を続けスキルを身に付けていきたいと思います。

####参考文献
[Ademiy データ分析コース](https://aidemy.net/grit/premium/)
[【signate】お弁当の需要予測チュートリアル　〜重回帰分析編〜](https://qiita.com/takapy0210/items/834558aa182e3cf6a8b4#1-%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E6%BA%96%E5%82%99%E3%83%87%E3%83%BC%E3%82%BF%E3%82%BB%E3%83%83%E3%83%88%E3%81%AE%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF)