3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

featuretoolsとtpotを用いた需要予測(回帰分析)。①

Last updated at Posted at 2022-06-30

はじめに

初めまして、現在プログラミングスクールに在籍しているものです。
今回は成果物の1つとして、Signateのこちらのコンペに参加致しました。

このコンペの内容を簡単にお伝えしますと、説明変数(温度や曜日,メニュー名等)
から 目的変数(お弁当の売上数)を予測するものです。いわゆる教師有学習(回帰)です。
イメージとしてはscikit-learnのボストンの住宅価格予想をイメージして頂けると分かりやすいかも知れません。

初投稿故、駄文散文ご了承くださいませ。

コンセプト

今回Qiitaに挑戦した理由は下記のとおりです。

  • 私のアウトプットや言語化の為。
  • これからQiitaの記事を書く為のステップアップの練習。ライティングスキル向上の為。

以上、この記事を閲覧するだけで初心者の方も簡単に理解し実装して頂ける事を目標と致します。

丁寧に解説するつもりですので多少長くなります。
少しでも読みやすくと思い3つにパートを分ける予定です。
今回はデータ確認と特徴変換を中心として記入します。

開発環境

Jupyter lab ⇒少ないデータですが、今回の機械学習モデルは大幅な時間を要します。それ故google colabのように時間制限やサーバーに依存するものは推奨いたしません。

私の経験として、google colab無料版にて機械学習にかけた際に、2時間経過した後フリーズして泣きを見ました。その後Jupyter labにて一晩実行し提出しました。

  • プロセッサ:Intel(R) Core(TM) i7-6600U CPU
  • 実装RAM :16.0 GB
  • システム :64 ビット オペレーティング システム、x64 ベース プロセッサ
  • OS :Windows 11

今回使う、featuretoolsとtpotとは?

featuretools
特徴量生成自動ツールです。使い方は②で説明致します。公式サイト

TPOT
遺伝的プログラミングを用いて、機械学習モデルの選定やパラメータチューニングなどを自動で行ってくれる機械学習モデルです。公式サイト

実際にやってみる。

1.データのインポートと確認。

  • こちらからデータをダウンロードしてください。
    2022-06-22_14h30_42.png

ダウンロードした後は下記の手順に沿ってjupyter上で取り込み確認できるようにします。

①データインポート
このようにpd.read_csv()の中にパスを指定してあげてください。
.tsx
import pandas as pd
train  = pd.read_csv("C:/Users/user/Desktop/****/****/****/train(1).csv")
test   = pd.read_csv("C:/Users/user/Desktop/****/****/****/test(2).csv")

・パスは最初から記入してください。C:~から。
・2022年現在はパスの区切りは「/」です。コピーした際、[]の場合は置き換えてください。

  • データの確認。

trainとtestの相違点や情報等を確認します。説明変数(各特徴)に関しては
こちらをご参照ください。precipitationの"--"と"0"の違いがピンと来ませんが、そこまで意識するものでもございません。私見ですが。

①カラム確認。
今回は便宜上1つのセルにまとめて記入しておりますが、実際にはそれぞれ個別のセルに記入し、 逐一実行して確認していただくのが良いかと思われます。
.tsx
train.head(2)           #trainのカラム確認
test.head(2)            #testのカラム確認
train.shape,test.shape  #train/testの何行何列かの確認
train.dtypes            #dfの型を確認列情報整数(int)なのか,objectなのか

こちらが上記コードの情報です。
2022-06-25_22h23_46.png

更にdatetimeに着目してみましょう。train.tail(2)を見てみたところ、testの冒頭と繋がっている事がわかります。
2022-06-25_22h31_20.png

  • データの統合
    上記を踏まえ、trainとtestのデータを統合して一括処理できるようにしましょう。
    データの特徴把握や、整形をする際にtrainとtestを結合して1つのデータとします。
    その方が一度のコードで済みますので2度手間になりません。
    勿論1行1行にtrainとtestが判別できる処理は施します。これが下記の[flg]です。
.tsx
#各々の行がtrainかtestか区別するためにflgを付与します
train['flg'] = 1   #trainデータはflg 1
test['flg'] = 0    #testデータはflg 0

# trainデータとtestデータを結合
datas = pd.concat([train,test], axis=0, sort=False)

これでtrainとtestが合体したデータフレーム、dataが完成しました。確認してみましょう。
前半のflg=1 がtrain部分で、後半のflg =0 がtest部分です。
2022-06-25_22h44_09.png

②トレンドの確認と対処

以上を鑑みてこの図のようにyの値を予測しましょう。
まずはデータを確認します。
2022-06-22_21h09_21.png
ここで着目して頂きたいポイントとして、前半と後半では傾向が異なるという点です。
私の場合、思い切って前半をまるっとカットしまいました。
カットする際の塩梅ですが、私の場合は2014年の4月一杯までの部分をカットしました。根拠は目視で5月から傾向が変わっていると感じたので。

.tsx
datas = datas.loc[106:,]

2022-06-23_11h09_30.png
これがその結果です。

  • ①のまとめ
.tsx
import pandas as pd

train  = pd.read_csv("C:/Users/user/Desktop/****/****/****/train (1).csv")
test   = pd.read_csv("C:/Users/user/Desktop/Jupyter/lab/成果物/test (2).csv")

# フラグを立てるtrainとtestの区別
train['flg'] = 1
test['flg'] = 0
# trainデータとtestデータを結合
datas = pd.concat([train,test], axis=0, sort=False)

datas = datas.loc[106:,]

2.データ前処理

まずは前処理するデータの確認。最初のステップとして欠損値を確認します。

.tsx

datas.isnull().sum()   #データの欠損値を確認

いくつか欠損地が見当たりますね。まずはこの欠損値を埋めましょう。
2022-06-23_11h42_28.png

①欠損値穴埋め
欠損値を埋めていきます。
  • まずはpaydayの要素を確認してみましょう。
.tsx
datas["payday"].value_counts()

# 結果
# 1.0    7
# Name: payday, dtype: int64

なるほど、1.0が7つあり他はNaNみたいですね。
そもそも1.0は何を表すのでしょうか?確認してみましょう。
再度カラム説明で確認しますと、
payday | boolean | 給料日フラグ(1:給料日)
どうやら1.0は給料日のようです。それならば、対になるように給料日ではない日、つまりNanを[0]
で埋めましょう。

  • eventはいかがでしょうか。
.tsx
datas["event"].value_counts()

#ママの会             5
#キャリアアップ支援セミナー    5
#Name: event, dtype: int64

どうやらイベントがある日にそのイベント名が入力されているみたいです。
それならば、NaNはイベント無しと考えて良さそうです。イベント無しなのでそのまま「なし」で埋めましょう。

  • remarksも同様に確認します。
.tsx
datas["remarks"].value_counts()

#結果
#お楽しみメニュー           13
#料理長のこだわりメニュー        7
#手作りの味               1
#スペシャルメニュー800     1
#近隣に飲食店複合ビルオープン      1
Name: remarks, dtype: int64

eventと同じ記載方法のようです。それならば同様に「なし」で補完します。

.tsx
datas.info()           #データの型やカラム数欠損値を確認

2022-06-25_23h10_27.png

上記を踏まえたうえで欠損地をこのように補います。

.tsx
#給料
datas['payday'] = datas['payday'].fillna(0)         #欠損値を0で埋めます

#イベント
datas['event'] = datas['event'].fillna('なし')      #欠損値をなしで埋めます

#remarks
datas["remarks"] = datas["remarks"].fillna('なし')  #欠損値をなしで埋めます

kcalの欠損値補完については平均値や中央値を用いましょう。
今回はグラフで可視化した際に、少し頂点が右にズレているので中央値で補完します。

.tsx
# kcalの欠損値補完について
import seaborn as sns
plt.show()
sns.displot(train.kcal, bins =8,kde=False)
sns.set()
train['kcal'] = train[['kcal']].fillna(train['kcal'].median())

イメージはこちら。
2022-06-23_19h12_22.png

  • それではこれで、すべての欠損値が補完できたはずです。確認してみましょう。

2022-06-29_20h31_41.png

eventには「なし」というカテゴリが追加され…、他欠損値もtestのy(40)以外埋まってますね。
因みにこれはこの値を予測するのが目標ですので、そのままNaNで問題ありません。むしろ前処理で補完したらいけません。

②特徴量生成
  • remarksの要素内容を確認してみましょう。
.tsx
datas["remarks"].value_counts()
<a id="anchor1"></a>
'''
結果
お楽しみメニュー           13
料理長のこだわりメニュー        7
手作りの味               1
スペシャルメニュー(800円)     1
近隣に飲食店複合ビルオープン      1
Name: remarks, dtype: int64
'''

remarksの中にも様々な項目がある事がわかりますね。それぞれの項目の違いについてみてみましょう。
まずは必要なmodule import。これにより日本語で表示することができます。

.tsx
!pip install japanize-matplotlib   #日本語モジュールインポート
import japanize_matplotlib         #そのモジュールをセット
import matplotlib.pyplot as plt    
import seaborn as sns

それぞれの項目を箱ひげ図にて確認しましょう。

  • 欠損値処理されたもの かつ,yの値のあるものからデータを見てみましょう。
    つまり、欠損値処理が済んだtrainデータのことです。
.tsx
df_train = datas[datas['flg']==1]

このように抽出することができます。datasのflgが1の部分を抜き出して df_trainという変数に格納しています。

.tsx
fig, ax = plt.subplots(2,2,figsize=(12,7))
sns.boxplot(x="week",y="y",    data =df_train,ax=ax[0][0])
sns.boxplot(x="weather",y="y", data =df_train,ax=ax[0][1])
sns.boxplot(x="remarks",y="y", data =df_train,ax=ax[1][0])
sns.boxplot(x="event",y="y",   data =df_train,ax=ax[1][1])
plt.tight_layout()

意味としては、data =df_trainで適用するdataを指定します。今回はdf_trainです。
df_trainの中から、xとyを指定します。一番下の例ですと、
sns.boxplot(x="event",y="y", data =df_train,ax=ax[1][1])
x⇒df_trainの"event"。y⇒df_trainの"event"という意味です。

2022-06-26_19h19_03.png

分かったこと。

  • お楽しみメニューの際に売上数が多い。
    なのでお楽しみメニューの時は1、そうでないときは0のカラムを作成しましょう。
.tsx
datas['fun'] = datas['remarks'].apply(lambda x : 1 if x.find("fun") >=0 else 0)

lambda式の復習です。
・1行づつremarksの要素がxに格納され、その度に「お楽しみメニュー」かそうでないかを場合分けしています。「お楽しみメニュー」であれば1,そうでなければ0と。
その内容を新しいカラム名["fun"]に格納しています。

  • eventは特に意味はない。

  • "name"の中からも何か特徴量を作成できないか検討してみましょう。
    同じように"name"の情報を見てみます。それでは上記通り、箱ひげ図で確認しますと、

.tsx
sns.boxplot(x="name",y="y",   data =df_train)

2022-06-26_19h49_28.png
このように文字が潰れて確認できません。この場合では下記のような箱ひげ図で確認してください。
詳しい説明は省きますが、jupyterとgoogle colabではコピペして頂けると表示されます。

.tsx
import plotly
import plotly.graph_objs as go
# Google Colab. やJupyter Lab.でプロットするためには以下を実行する
import plotly.io as pio
pio.renderers.default = "colab"
plotly.__version__
import plotly.offline as offline

layout = go.Layout(
   autosize=False,
   width=950,
   height=500,
   margin=go.layout.Margin(
       l=80,
       r=50,
       b=50,
       t=10,
       pad=4),
   xaxis=dict(
       title='menu',
       titlefont=dict(
           size=14,
       ),
       showticklabels=True,
       tickangle=0,
       tickfont=dict(
           size=12,
       ),
   ),
   yaxis=dict(
       title='販売数(y)',
       titlefont=dict(
           size=14,
       ),
       showticklabels=True,
       tickfont=dict(
           size=10,
       ),

   ))

data = [go.Box( x=df_train['name'], y=df_train['y'] )]

fig = go.Figure(data=data, layout=layout)
offline.iplot(fig, filename='example', show_link=False, 
             config={"displaylogo":False, "modeBarButtonsToRemove":["sendDataToCloud"]})

2022-06-26_19h52_18.png

このようにメニュー名とyの相関関係を見てみると、カレーが人気メニューのように見えます。
カレーの日は売上が伸びるのですね。なのでメニューに「カレー」が含まれる場合に1、そうでないときは0のカラムを作成しましょう。

.tsx
datas['curry'] = datas['name'].apply(lambda x : 1 if x.find("カレー") >=0 else 0)

カラム["fun"]と基本的構造は同じで、["name"]からの要素を1行づつ、取り出し、「カレー」という文字列が含まれているかどうかを確認しています。

③One-Hot Encoding
['week','weather',"precipitation"]はOne-Hot Encodingにしましょう。 項目により特徴量(列)が大量になってしまう可能性がありますが、今回の学習方法TPOTに関してはさほど心配する必要はございません。
.tsx
one_hot_columns = ['week','weather',"precipitation"]#encordingしたい要素達

datas = pd.get_dummies(datas, dummy_na=False, columns=one_hot_columns)

- One-Hot Encoding
各変数に対して該当するカテゴリかどうかを0,1で表現したベクトルに変換する処理のことです。
例えば天気が「晴れ、曇り、雨」という3つのカテゴリをもつ変数があるとすれば、
その変数に対してOne-hot encodingを行うと晴れ?曇り?雨?という3つの特徴量が作成され、 該当部分に1がつき、その他は0になります。図で可視化するとより理解して頂けると思います。

2022-06-27_13h32_41.png

  • ②のまとめ
.tsx
#給料
datas['payday'] = datas['payday'].fillna(0)         

#イベント
datas['event'] = datas['event'].fillna('なし')      

#remarks
datas["remarks"] = datas["remarks"].fillna('なし')  

datas['fun'] = datas['remarks'].apply(lambda x : 1 if x.find("fun") >=0 else 0)

datas['curry'] = datas['name'].apply(lambda x : 1 if x.find("カレー") >=0 else 0)

one_hot_columns = ['week','weather',"precipitation"]#encordingしたい要素達

datas = pd.get_dummies(datas, dummy_na=False, columns=one_hot_columns)

2022-06-27_15h54_44.png

いかがでしょうか?次回はfeaturetoolsを用いてさらに特徴量を増やしていきたいと思います。
ここまで確認できましたら、次のステップに行きましょう。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?