このページは、AIや機械学習・ディープラーニング(深層学習)に興味はあるけど、プログラミングも数値解析もさっぱり!というAI初心者の方(私です)のための機械学習入門です。
必要なものはGoogle IDとSignate IDの2つだけ。どちらも無料で利用できます。
(Google IDは開発環境を、Signate IDは学習データを用意するために使用します)
難しい数式は使わずに、機械学習の一連の流れと基礎知識を学べるようにまとめていますので、機械学習やディープラーニング、AIなどに興味のある方はぜひコードを実行しながら読み進めてみて下さい。新しいアイデアを形にする足がかりになれば幸いです。
Google IDまたはSingate IDをまだ持っていない方は、以下の記事を参考にしてIDを取得して下さい。(既にIDを2つともお持ちの方は、スキップして下へ進んで下さい)


事前準備
SIGNATEからデータをダウンロード
まずは機械学習に必須である学習データを準備しましょう。
下のリンクからSIGNATEへログインし、「【練習問題】お弁当の需要予測」のデータページを開いて下記3つのデータをダウンロードして下さい。
- モデル学習用データ (train.csv)
- モデル検証用データ (test.csv)
- 応募用サンプルファイル (sample.csv)
リンク:SIGNATE「【練習問題】お弁当の需要予測」データ
データはファイル名の左にある「ダウンロードマーク」からそれぞれダウンロードできます。
データをGoogleドライブへアップロード
次に学習データをGoogleコラボから読み込めるようにGoogleドライブにアップロードします。

上のページを参考に、Googleドライブの「マイドライブ\Colab Notebooks」フォルダの中に「tml-1」(tecurio machine learning-1 の略です)というフォルダを作成し、その中に先ほどダウンロードした3つのファイルをアップロードして下さい。
これで事前準備は完了です。
Googleコラボで機械学習をはじめる
Googleコラボでノートブックを作成
Googleコラボではノートブックというファイルにプログラムを書くことで機械学習を行っていきます。まずは新しいノートブックを作成しましょう。
下のリンクからGoogleコラボを開いて下さい。
リンク:Googleコラボ
この画面が表示された場合は「キャンセル」を選択します。
メニューの「ファイル」から「Pyhton 3 の新しいノートブック」を選択します。
このように新しい真っ白なにノートブックが開きます。左上には「untitled0.ipynb」とノートブックのタイトルが表示されています。
タイトルは直接編集可能なので、今回は「tml-1」などご自分がわかりやすい名前に変更しておいて下さい。
これで新しいノートブックが用意できました。
ノートブックの簡単な使い方
少しだけノートブックの使い方の説明です。
ノートブックの入力エリアには「コードセル」と「テキストセル」の2種類のセルがあります。テキストセルは補助的な役割で、メインのプログラムはコードセルに記入します。
背景がグレーのエリアが「コードセル」、白いエリアが「テキストセル」です。
「+コード」ボタンでコードセルを、「+テキスト」ボタンでテキストセルを追加します。
「コードセル」
「コードセル」にはコード(プログラム)を記述します。基本的に「コードセル」だけ使えれば機械学習が可能です。
1つの「コードセル」には以下の3つが含まれます。
・コード入力エリア:ここにコードを記述します。
・実行ボタン:「コード入力エリア」に入力したプログラムを実行します。実行中はボタンの見た目が次のように変化します。
・各種操作:コードの移動や削除などが行えます。
テキストセル
「テキストセル」には、あとで自分が見返す時や、共同作業する人が見た時にわかりやすくなるように、コードの説明を書いたり、参照リンク、参考画像を貼り付けたりできます。
1つの「テキストセル」には以下の3つが含まれます。
・テキスト入力エリア:ここにメモなどを記述します。
・テキスト装飾:メモ内容を色づけしたり、リンクや画像を挿入したりできます。
・各種操作:テキストの移動や削除などが行えます。
Googleコラボのセットアップ
それではGoogleコラボで機械学習をする環境を整えましょう。セットアップといっても難しくなく、慣れれば1分もかからずにできるくらい簡単です。
Googleドライブにマウント
まず始めに、GoogleコラボからGoogleドライブに接続してファイルを読み込めるようにします。ここからはいよいよ実際にコードを書いて実行していきます。
下のコードをコードセル内に書いて下さい。(コピペでもOKです)
from google.colab import drive drive.mount('/content/drive') %cd "/content/drive/My Drive/Colab Notebooks/tml-1"
コード解説
- from google.colab import drive
drive.mount(‘/content/drive’) - GoogleコラボからGoogleドライブに接続するという意味です。
- %cd “/content/drive/My Drive/Colab Notebooks/tml-1”
- 先ほど作成してデータファイルをアップロードした「tml-1」フォルダに移動するという意味です。
それでは画面を見ながら進めていきましょう。
入力したコードを実行するため「実行」をクリックします。
Googleドライブに接続するための認証コードを発行するリンクが表示されます。リンクをクリックしましょう。
アカウント選択ページが表示されます。現在ログインしているアカウントを選択します。
アクセス確認ページが表示されます。「許可」を選択して下さい。
認証コードが表示されるので、コピーして下さい。
Googleコラボに戻って認証コードを貼り付けます。貼り付けたら「Enter」キーを押します。
上のように表示されたら、GoogleコラボからGoogleドライブへの接続完了です。
(また、コードの左横には「[1]」と表示されます。これは実行が完了した時に表示され、中の数字は実行した順番です。今は最初に実行したコードなので1と表示されています)
では、実際にGoogleドライブのファイルにアクセスできているかどうか、現在の作業フォルダにあるファイルの一覧を確認してみましょう。
「+コード」を押してコードセルを追加し、以下のコードを実行してみて下さい。
%ls
実行結果は次のようになります。
このようにアップロードした3つのファイルが表示されたらOKです。
Googleドライブに置いたデータにアクセスできるようになりました。
Googleコラボの日本語化&ライブラリの読み込み
日本語版グラフ描画ライブラリのインストール
Googleコラボは便利な開発環境ですが、初期状態ではグラフに日本語が使えません。次は日本語を使えるようにしましょう。
「+コード」を押してコードセルを追加し、以下のコードを実行して下さい。
※以降、新たにコードを書く時には新しいコードセルを追加してそこに書くようにして下さい。
pip install japanize-matplotlib
コード解説
- pip install japanize-matplotlib
- pipという管理ツールを使って、「japanize-matplotlib」というグラフを日本語化するモジュールをインストールする。という意味です。
ライブラリの読み込み&グラフ日本語化設定
ライブラリとは、誰でも使える便利なプログラムの集まりです。用途に合わせた様々なライブラリがあります。ここでは機械学習をするのに便利なライブラリを読み込みます。
下のコードを実行して下さい。
import pandas as pd # データ分析ライブラリ
import numpy as np # 数値計算ライブラリ
from matplotlib import pyplot as plt # グラフ描画ライブラリ
import seaborn as sns # グラフ描画ライブラリ
from sklearn.linear_model import LinearRegression as LR # 線形回帰モデル
from sklearn.metrics import mean_squared_error # 評価関数RMSE
import japanize_matplotlib #グラフ日本語化ライブラリ
sns.set(font="IPAexGothic") # 日本語フォント設定
コード解説
- import pandas as pd
- データ分析のためライブラリ「pandas(パンダス)」を読み込み、pdと略して使用するという意味です。
- import numpy as np
- 数値計算のためライブラリ「numpy(ナムパイ)」を読み込み、npと略して使用するという意味です。
- import matplotlib.pyplot as plt
- 可視化ライブラリ「matplotlib(マットプロットリブ)」に含まれるグラフ描画モジュール「pyplot」を読み込み、pltと略して使用するという意味です。
- import seaborn as sns
- 先に読み込んだ「matplotlib」を使いやすくするライブラリ「seaborn(シーボーン)」を読み込み、snsと略して使用するという意味です。
- from sklearn.linear_model import LinearRegression as LR
- 機械学習ライブラリ「scikit-learn」から線形回帰モデル作成モジュール「LinearRegression」を読み込み、LRと略して使用するという意味です。
- from sklearn.metrics import mean_squared_error
- 機械学習ライブラリ「scikit-learn」から予測値の誤差を算出して評価するモジュール「mean_squared_error」を読み込んでいます。
- import japanize_matplotlib
- 先に読み込んだ「matplotlib」で日本語を使えるようにするライブラリ「japanize_matplotlib」を読み込むという意味です。
- sns.set(font=“IPAexGothic”)
- グラフに使用する文字のフォントを「IPAゴシック」という日本語対応フォントに設定しています。
これでGoogleコラボのセットアップも完了し、いつでも機械学習を始められる準備ができました。
今回の機械学習の目的
さっそく機械学習をはじめたいところですが、その前に今回の機械学習の目的を覚えておいて下さい。
今回の機械学習の目的は、徐々に売上が少なくなり経営が苦しくなっているとあるお弁当屋さんの利益向上に貢献することです。日々の売上データを学習データとして機械学習を行い、毎日のお弁当の販売個数を予測して廃棄処分するお弁当を減らすことで余計な材料費を削減できるように手助けをしましょう。
もちろん今回の学習データは練習用で、本当にお弁当屋さんがあるわけではありませんが、実際の機械学習の流れを理解するためにも、このような目標があることを念頭に置いてこの入門を進めて下さい。
学習データの内容
先ほどダウンロードした「train.csv」には、下のような日毎のお弁当の販売データが約1年分入っています。
データの列名 | 内容 |
---|---|
datetid | インデックスとして使用する日付(yyyy-m-d) |
y | 販売数(目的変数) |
week | 曜日(月~金) |
soldout | 完売フラグ(0:完売せず、1:完売) |
name | メインメニュー |
kcal | おかずのカロリー(kcal)欠損有り |
remarks | 特記事項 |
event | 13時開始お弁当持ち込み可の社内イベント |
payday | 給料日フラグ(1:給料日) |
weather | 天気 |
precipitation | 降水量。ない場合は “–“ |
temperature | 気温 |
今回のケースでは、曜日や天気・気温などの情報を利用して、販売数(y)を予測できるようになることがゴールです。
(予測するデータのことを目的変数(今回は販売数y)、予測するために使用するデータを説明変数または特徴量(今回は日付・曜日・天気など)といいます)
データの種類について
データは大きく2つの種類に分けられます。量的データと質的データです。
尺度 | 意味 | |
---|---|---|
量的データ (数値に意味がある) | 間隔尺度 | 日付、西暦、温度など。 足し算・引き算ができる。 |
比例尺度 | 販売数、金額、個数など。 足し算・引き算ができ、比率にも意味がある。 | |
質的データ (区別に使用) | 名義尺度 | メニュー名、性別、名前、idなど。 (数値のidでも数字には意味がない) |
順序尺度 | 順位、満足度など。 順序に意味があり大小比較できるが、足し算引き算はできない。 |
ここでは簡単に数値に意味があって計算できるのが量的データ、物事を区別するために使用するのが質的データと覚えておいて下さい。
今回のデータの場合、以下のように分けることができます。
(データの解釈・利用方法によっては異なる分類になることもあります)
データの列名 | 内容 | データの種類 |
---|---|---|
datetid | インデックスとして使用する日付(yyyy-m-d) | 量的データ |
y | 販売数(目的変数) | 量的データ |
week | 曜日(月~金) | 質的データ |
soldout | 完売フラグ(0:完売せず、1:完売) | 質的データ |
name | メインメニュー | 質的データ |
kcal | おかずのカロリー(kcal)欠損有り | 量的データ |
remarks | 特記事項 | 質的データ |
event | 13時開始お弁当持ち込み可の社内イベント | 質的データ |
payday | 給料日フラグ(1:給料日) | 質的データ |
weather | 天気 | 質的データ |
precipitation | 降水量。ない場合は “–“ | 量的データ |
temperature | 気温 | 量的データ |
機械学習手順1 データの確認と整形
まずはデータの確認です。機械学習をするにあたって、データが最も重要と言っても過言ではないくらいに、データの善し悪しが学習の結果に大きく影響します。
もしデータに不足があったり、間違いがあった場合には、学習自体をイチからやり直さないといけなくなることも少なくありません。そうならないためにも、データに不備がないかしっかりチェックするようにしましょう。
データを読み込む
まず学習データが入っている「train.csv」を読み込みます。(train.csvのtrainは列車ではなく、トレーニング・学習の意味です)
新しいコードセルを追加し、下のコードを実行します。
※以降、新たにコードを書く時には新しいコードセルを追加してそこに書くようにして下さい
# train.csvを読み込む train = pd.read_csv("train.csv")
コード解説
- train = pd.read_csv(“train.csv”)
- pandasライブラリのread_csv関数を使い、変数trainの中にtrain.csvファイル内のデータを読み込むという意味です。(変数とはデータの入れ物のことです)
データを表示する
次は読み込んだ「train.csv」のデータを表示してみましょう。
次のコードを実行して下さい。
# データを表示 train.head(10)
コード解説
- train.head(10)
- head関数を使うと変数trainに読み込まれているデータの先頭5行を表示できます。
ここではカッコ()の中に10を指定することで先頭の10行を表示しています。
似た関数にtail関数があり、こちらは末尾からデータを表示します。
結果は次のようになります。
先頭10件のデータが表示されました。
お弁当屋さんの売上データらしく、日付(datetime)、販売数(y)、日替わり弁当名(name)、天気(weather)などの情報があるのがわかると思います。
データの件数を確認する
ではデータが全部で何件あるのか確認してみましょう。下のコードを実行して下さい
# データの行列数を表示 train.shape
コード解説
- train.shape
- shapeプロパティを使うと、データの要素数を確認できます。今回のような行と列からなる2次元配列の場合、(行数、列数)と表示されます。
実行結果は次のようになります。
結果は(行数, 列数)を表すので、12個(列)の項目を持ったデータが、207個(行)あるとわかります。(下の画像では10行目までしか表示していませんが実際は207行のデータがあります)
データを全件表示する
データを全件表示したい時は、下のコードを実行して下さい。
pd.set_option('display.max_rows', train.shape[0]) train.head(train.shape[0])
コード解説
- pd.set_option(‘display.max_rows’, train.shape[0])
- set_option関数を使うとpandasの設定を変更できます。
ここでは「(‘display.max_rows’, pd_data.shape[0])」として、表示最大行数にtrainのデータの行数を設定しています。 - train.head(train.shape[0])
- 「(train.shape[0])」としてデータの行数(shapeプロパティの0番目の値)をhead関数に渡すことで、すべてのデータを表示しています。
(「pd.set_option(‘display.max_rows’, 最大行数)」 とpandasで 最大表示行数を設定する方法もあるのですが、こちらの方法の方が無駄がないのでこちらを採用しました)
データ型(タイプ)を確認する
Pythonのデータ型には大きく分けて数値型と文字列型の2種類があります。
データ型名 | 数値/文字 | 備考 |
---|---|---|
int64 | 数値型 | 整数 |
float64 | 数値型 | 小数点付きの数 |
object | 文字型 |
表のようにint64とfloat64は数値型データ、objectは文字型データと覚えておいて下さい。
それでは各列のデータ型を確認してみましょう。下のコードを実行して下さい。
# 列ごとのデータ型と、空ではないデータ数を表示 train.info()
コード解説
- train.info()
- info関数を使うと、各列のデータ型(数値か文字か)や、空ではない値の入った行数を確認できます。
実行結果は次のようになります。
左から「列名」「空白ではないデータの個数」「データ型」の順に表示されます。例えば「kcal」の列には空白ではないデータが166件あり、数値型(float64型)であるとわかります。データは全部で207件なので「kcal」列には41件の空白データがあることも読み取れます。欠損値の処理
欠損値とは値のない空データや、値がないことを意味するデータのことです。欠損値があると正しく学習できないことがあるため対処が必要です。
先ほど見たtrain.csvのデータにも「NaN」や「–」などと表示されている箇所がありましたが、これらが欠損値にあたります。
空データ(NaN)の個数を確認
「NaN」は値がない空データであることを意味しています。目で確認できるところでは「kcal(カロリー)」「remarks(特記事項)」「event(イベント)」「payday(給料日)」に欠損値があるようです。
では実際にどのくらい空データがあるのか確認してみましょう。下のコードを実行して下さい。
train.isnull().sum() # 空データ件数を表示
コード解説
- train.isnull().sum()
- isnull関数を使うとデータが空白データであるかを確認できます。また、sum関数を使うとデータの合計を計算できます。2つを組み合わせることで、各列の空データ数を表示します。
実行結果は次のようになります。
「kcal」には41個、「remarks」には186個、「event」には193個、「payday」には197個の空データがあることが確認できました。その他の列には空データは無いようです。
空データの補完
空データがある行への対応には空データを含む行を削除する方法と、空データに代わりの値を入れる補完があります。今回はデータ数が少なく空データが多いため補完を行います。
数値型データの補完
まず「kcal(お弁当のカロリー)」です。こちらは数値型データです。今回はカロリーの平均値を算出し、それを空データに代入しましょう。下のコードを実行して下さい。
avg = train['kcal'].mean() # kcalの平均値を算出 train['kcal'] = train['kcal'].fillna(avg) # kcalの空データを平均値で埋める
コード解説
- avg = train[‘kcal’].mean()
- mean関数を使うとデータの平均値を取得できます。今回は「train[‘kcal’]」としてカロリーの平均値を取得し、それを変数avgの中に入れています。
- train[‘kcal’] = train[‘kcal’].fillna(avg)
- fillna関数を使うと指定した列に含まれる空データを別の値に置き換えられます。ここではkcal列の空データを変数avgに入れたkcalの平均値に置き換えています。
次に「payday(給料日)」です。給料日には「1」が設定されますが、給料日以外の日はNaNになっています。ここではNaNを「0」で埋めておきましょう。下のコードを実行して下さい。
train['payday'] = train['payday'].fillna(0) # paydayの空データを0で埋める
コード解説
- train[‘payday’] = train[‘payday’].fillna(0)
- fillna関数を使ってpayday列の空白データを0に置き換えています。
文字型データの補完
次は文字型データの「remarks(特記事項)」です。まずremarksにどのような値が入っているのか確認しましょう。下のコードを実行して下さい。
train['remarks'].value_counts()
コード解説
- train[‘remarks’].value_counts()
- value_counts関数を使うとデータに含まれる値の一覧を確認できます。今回は「train[‘remarks’]」として特記事項の値の一覧を表示しています。
結果は次のようになります。
メニューに関する追加メモのような内容ですね。ここでは空データを「なし」で埋めておきましょう。下のコードを実行して下さい。
train['remarks'] = train['remarks'].fillna('なし') # remarksの空データを「なし」で埋める
コード解説
- train[‘remarks’] = train[‘remarks’].fillna(‘なし’)
- fillna関数を使ってremarks列の空データを「なし」という文字に置き換えています。文字を扱う時は「’」または「”」で文字の前後を挟みます。
同じように「event(社内イベント)」の空データも「なし」で埋めておきましょう。下のコードを実行して下さい。
train['event'] = train['event'].fillna('なし') # eventの空データを「なし」で埋める
コード解説
- train[‘event’] = train[‘event’].fillna(‘なし’)
- fillna関数を使ってevent列の空データを「なし」という文字に置き換えています。
以上で空データ(NaN)の補完処理が終わりました。それでは空データ件数を確認してみましょう。下のコードを実行して下さい。
train.isnull().sum() # 空データ件数を表示
実行結果は次のようになります。
空白データがなくなりました。
空白データ以外の欠損値の補完
空データ(NaN)はなくなりましたが、「precipitation(降水量)」の列に「降水量なし」を意味する「–」というデータがありました。降水量は数値に意味があり計算ができる量的データですが、「–」は文字型データのため計算に使えません。
計算に使えるよう「–」を「0」に置き換えましょう。
train['precipitation'] = train['precipitation'].replace('--', 0) # 「--」を0に置換
コード解説
- train[‘precipitation’] = train[‘precipitation’].replace(‘–‘, 0)
- replace関数を使うとある値を別の値に置換できます。今回は「replace(‘–‘, 0)」として「–」を数値の「0」に置換しています。
実行後、Head関数で中身を確認すると下のようになります。
「precipitation(降水量)」の「–」が「0」に変わりました。先程補完した「kcal」「remarks」「event」「payday」も「NaN」がそれぞれ補完した値に変わっています。
これで欠損値の補完が完了しました。
データの変換
次はデータ型を使いやすい型に変換します。
データには量的データと質的データがありました。量的データは計算できる数値データなのでデータ型も数値型であると扱いやすく、質的データは区別するためのデータで計算には使用しないため文字型の方が扱いやすいことが多いです。
ここでは量的データなのに文字型となっているデータを数値型に、質的データなのに数値型となっているデータを文字型に変換していきます。
info関数でデータ型を確認しましょう。
量的データである「datetime(日付)」と「precipitation(降水量)」が文字型(object)に、質的データである「soldout(完売日フラグ)」「payday(給料日フラグ)」が数値型(int64、float64)になっています。
文字型を数値型に変換
まず単純に変換できる「precipitation(降水量)」から始めましょう。下のコードを実行して下さい。
train['precipitation'] = train['precipitation'].astype(np.float) # precipitation列を数値型に変換
コード解説
- train[‘precipitation’] = train[‘precipitation’].astype(np.float)
- astype関数を使うとデータの型を変更できます。ここでは「astype(np.float)」として「precipitation」列のデータ型を数値型に変換しています。
実行後、info関数でデータ型を確認すると次のようになります。
「precipitation(降水量)」のデータ型が文字型(object)から数値型(float64)に変わっています。
次に「datetime(日付)」を数値型に変換しましょう。日付データは年・月・日から構成されるため、それぞれ別々に数値型として抜き出します。下のコードを実行して下さい。
train['datetime'] = pd.to_datetime(train['datetime']) # 日付をdatatime型に変換 train['year'] = train['datetime'].dt.year # 日付型データからyear(年)を取得 train['month'] = train['datetime'].dt.month # 日付型データからmonth(月)を取得 train['day'] = train['datetime'].dt.day # 日付型データからday(日)を取得 train = train.drop('datetime', axis=1) # 元の日付データを+削除
コード解説
- train[‘datetime’] = pd.to_datetime(train[‘datetime’])
- to_datetime関数を使うとデータをdatetime型に変換できます。
ここでいうdatetime型とはPythonで日付を扱うためのデータ型で、日付データの列名である「datetime」とは関係ありません。 - train[‘year’] = train[‘datetime’].dt.year
- dt.yearプロパティを使うと日付から年を取得できます。
ここでは変数trainの中に新たに「year」という列を追加して、そこに年を入れています。 - train[‘month’] = train[‘datetime’].dt.month
- dt.monthプロパティを使うと日付から月を取得できます。
ここでは変数trainの中に新たに「month」という列を追加して、そこに月を入れています。 - train[‘day’] = train[‘datetime’].dt.day
- dt.dayプロパティを使うと日付から日を取得できます。
ここでは変数trainの中に新たに「day」という列を追加して、そこに日を入れています。 - train = train.drop(‘datetime’, axis=1)
- drop関数を使うと指定した行または列を削除できます。
ここでは「(‘datetime’, axis=1)」とすることで「datetime」の列を削除しています。行を削除する時は「axis=1」は不要です。
実行したあとhead関数でデータを確認すると次のようになります。
「datetime」の列がなくなり、「year」「month」「day」の列が追加されています。また、info関数でデータ型を確認すると次のようになります。
「year」「month」「day」が数値型(int64)として追加されています。
数値型を文字型に変換
次は数値型データを文字型に変換しましょう。下のコードを実行して下さい。
train['soldout'] = train['soldout'].astype(np.str) # soldout列を文字型に変換 train['payday'] = train['payday'].astype(np.str) # payday列を文字型に変換
コード解説
- train[‘soldout’] = train[‘soldout’].astype(np.str)
- astype関数を使ってデータの型を変更しています。
ここでは「astype(np.str)」として「soldout」列のデータ型を文字型に変換しています。 - train[‘payday’] = train[‘payday’].astype(np.str)
- 上と同じです。ここでは「payday」列のデータ型を文字型に変換しています。
実行後、info関数でデータ型を確認すると次のようになります。
「soldout(完売日フラグ)」「payday(給料日フラグ)」のデータ型が文字型(object)に変わっています。
これでデータ型の変換も完了です。
機械学習手順2 データの基礎分析
ここではデータの基本的な性質を見る方法をご紹介します。
基礎統計量を確認する
まずは列ごとの基礎統計量(平均・最小・最大値など)を見てみましょう。「統計?…難しそう…」と構えなくても大丈夫です。関数がすべて計算してくれるので簡単に確認できます。
基礎統計量を知ることは、このあとに出てくるグラフを理解するのにも役立ちます。
数値型データの基礎統計量を確認する
下のコードを実行して下さい。
# データの基礎統計量を表示 train.describe()
コード解説
- train.describe()
- describe関数を使うと、列ごとの基礎統計量を計算して表示してくれます。数値型のデータ列のみが対象です。
実行結果は次のようになります。
各行の意味は以下のようになっています。
項目 | 意味 | 備考 |
---|---|---|
count | データの個数 | 列にある空白ではないデータの個数 |
mean | 平均値 | 列のデータの平均値 |
std | 標準偏差 | データの散らばり度。下で解説 |
min | 最小値 | 列のデータで最も小さい値 |
25% | 第一四分位数。下で解説 | |
50% | 中央値 | 第二四分位数。下で解説 |
75% | 第三四分位数。下で解説 | |
max | 最大値 | 列のデータで最も大きい値 |
「y」列の場合、データの個数は207個、データの平均値は約86.6、最小値は29等と表示されています。
少しわかりにくい項目があると思いますので、説明を加えておきます。
標準偏差とは
標準偏差とは、データの散らばりを数値化したもので、数が小さいほどデータが密集していて、数が大きいほどデータが広い範囲に散らばっていることを表します。
例えばデータA・Bに以下のような数値が含まれている場合、平均値はどちらも4ですが、標準偏差はデータAが2、データBが4、とBの方が大きくなります。
データA「2,4,6」
データB「0,4,8」
下のコードを実行すると、実際にデータA・Bの基礎統計量が確認できます。気になる方はチェックしてみて下さい。
# データA・Bを作成して基礎統計量を確認する df = pd.DataFrame({'A': [2, 4, 6], 'B': [0, 4, 8]}) df.describe()
中央値・四分位数とは
中央値とは、数字を小さいものから順に並べたときに真ん中にある数値です。
「2,3,4,5,6」の場合、中央値は4になります。
「2,3,4,5」の場合、中央値は3と4の平均で3.5になります。
また、四分位数とは、数字を小さいものから順に並べて4分割したときにそれぞれ区切りにある数値で、25%の所にある数値を第一四分位数、50%の所にある数値を第二四分位数(=中央値)、75%の所にある数値を第三四分位数といいます。100%ところにあるのは最大値になります。
下のコードを実行して最小値、25%、50%(中央値)、75%、最大値の5つの数値をチェックしてもらうと、その関係性が何となくイメージできると思います。
# データAを作成して基礎統計量を確認 df = pd.DataFrame({'A': [1, 2, 3,4,5,6,7,8,9]}) df.describe()
文字型データの基礎統計量を確認する
文字型データの基礎統計量も見ることができます。
下のコードを実行して下さい。
# 文字型データの基礎統計量を表示 train.describe(include=['O'])
コード解説
- train.describe(include=[‘O’])
- describe関数の()内に引数 「include=[‘O’]」(Oはゼロではなく英語のオー)を渡すと、文字型データ列ごとの基礎統計量を計算して表示してくれます。
実行結果は次のようになります。
各行の意味は以下のようになっています。
項目 | 意味 | 備考 |
---|---|---|
count | データの個数 | 列にある空白ではないデータの個数 |
unique | ユニークデータの個数 | 重複データを省いたデータの個数 |
top | 最頻値 | 最も多く出現するデータ |
freq | 最頻値の個数 | 最も多く出現するデータの個数 |
上の結果からは、お弁当の日替わりメニューは207日間で156種類あり、最も多かったのは「メンチカツ」で207日間のうち6回登場したということが読み取れます。
ヒストグラム
ヒストグラムは量的データの分布を確認するのに利用します。ヒストグラムの形状からデータの性質をある程度把握できます。
ヒストグラムの形状と性質
形状 | データの性質 | |
---|---|---|
左右対称 | ![]() | 平均値・最頻値・中央値がほぼ同じ一般的なデータ。 (例:テストの点数、同学年同姓の身長・体重) |
左右非対称 | ![]() | 平均値・最頻値・中央値に乖離があるデータ。 (例:低所得が多く高所得少ない所得分布) |
ふた山 | ![]() | データに複数の性質が違う集団が含まれている可能性がある。 (例:身長のデータに男子・女子が混ざっている) |
離れ島 | ![]() | 外れ値がある (例:例外、不良品データが混ざっている) |
このように様々なことがヒストグラムから読み取れます。特に最後の外れ値には注意が必要です。外れ値の入ったデータで機械学習を行うと、予測結果が本来の結果からズレてしまうことがあるため、外れ値を除外するなどの対応が必要になります。
ヒストグラムを描く
それでは実際にヒストグラムを描いてみましょう。
下のコードを実行して下さい。
train['y'].plot.hist()
コード解説
- ax = train[‘y’].plot(figsize=(10,4))
- hist関数を使うことでヒストグラムを描けます。
ここでは変数train内の「y(販売数)」に関するヒストグラムを描いています。
実行結果は次のようになります。
左右非対称のヒストグラムが表示されました。1日の販売数が大体50個程度の日が最も多い(最頻値)ことがわかります。
ヒストグラムに平均値と中央値を重ねて描く
左右非対称のグラフの特徴は、平均値、最頻値、中央値に差があることでした。実際に平均値と中央値に線を引いて確認してみましょう。
下のコードを実行して下さい。
# ヒストグラムに平均値と中央値を重ねて描く plt.axvline(x=train['y'].mean(), color='red') # 平均値を描く plt.axvline(x=train['y'].median(), color='greenyellow') # 中央値を描く ax = train['y'].plot.hist(figsize=(10,4)) # サイズ指定してヒストグラムを描く ax.set_title('yのヒストグラム') ax.set_xlabel('y') ax.set_ylabel('分布量')
コード解説
- plt.axvline(x=train[‘y’].mean(), color=’red’)
- axvline関数を使うことで縦線を描けます。
ここでは「x=train[‘y’].mead()」としてxに「y(販売数)」の平均値を指定しています。
また「color=’red’」として線の色を赤色に設定しています。 - plt.axvline(x=train[‘y’].median(), color=’greenyellow’)
- ここでもaxvline関数を使って縦線を描いています。
ここでは「x=train[‘y’].median()」としてxに「y(販売数)」の中央値を指定しています。
また「color=’greenyellow’」として線の色を黄緑色に設定しています。 - ax = train[‘y’].plot.hist(figsize=(10,4))
- 横10インチ、縦4インチのサイズでヒストグラムを描いてます。
- ax.set_title(‘yのヒストグラム’)
- ヒストグラムに「yのヒストグラム」というタイトルを付けています。
- ax.set_xlabel(‘y’)
- X軸に「y」というラベルを付けています。
- ax.set_ylabel(‘分布量’)
- Y軸に「分布量」というラベルを付けています。
実行結果は次のようになります。
最頻値、中央値(黄緑線)、平均値(赤線)に差があることがわかります。この例のように平均値だけではデータの全容はわかりません。データを可視化して自分の目で確かめることが大切です。
機械学習手順3 有効な説明変数の選定
ここでは「y(販売数)」を予測するためにはどの説明変数(特徴量)を使って学習するのが有効だと考えられるかを探っていきます。
データの変化を可視化する
まず目的変数(予測対象)となる「y(販売数)」がどのように変化しているか確認してみましょう。
折れ線グラフ
折れ線グラフはデータの変化を確認するのに便利なグラフです。
折れ線グラフを描く
「y(販売数)」を折れ線グラフとして描画しましょう。下のコードを実行して下さい。
# 折れ線グラフの描画 train['y'].plot()
コード解説
- train[‘y’].plot()
- plot関数を使うと折れ線グラフを描けます。
ここでは変数train内にある「y(販売数)」のデータを折れ線グラフにしています。
実行結果は次のようになります。
このように簡単に折れ線グラフが描けます。
折れ線グラフを見やすく描く
折れ線グラフは描けましたが、タイトル付けや、サイズ調整をしてもう少し見やすくしてみましょう。下のコードを実行して下さい。
# 折れ線グラフを描く ax = train['y'].plot(figsize=(10,4)) ax.set_title('yの折れ線グラフ') ax.set_xlabel('時間') ax.set_ylabel('y')
コード解説
- ax = train[‘y’].plot(figsize=(10,4))
- 先程と同じく販売数に関する折れ線グラフを描いています。
ここでは「figsize=(10,4)」とオプションを指定することで、グラフのサイズを横10インチ、縦4インチに設定しています。
また、このあとタイトルなどの設定をするため、グラフを変数axの中に格納しています。 - ax.set_title(‘yの折れ線グラフ’)
- set_title関数を使うと折れ線グラフにタイトルを設定できます。
ここでは「yの折れ線グラフ」というタイトルを付けています。 - ax.set_xlabel(‘時間’)
- set_xlabel関数を使うとX軸にラベルを設定できます。
ここでは「時間」というラベルを付けています。 - ax.set_ylabel(‘y’)
- set_ylabel関数を使うとY軸にラベルを設定できます。
ここでは「y」というラベルを付けています。
実行結果は次のようになります。
先程より少し見やすくなりましたね。グラフ化したことで、お弁当の販売数が時間の経過とともに少なくなってきているのがわかるかと思います。このことから「datetime(日付)」と「y(販売数)」には関連性があると考えられます。
また、販売数が減ってきてもトゲのように突出した箇所があり以前と変わらず売れている日もあるようです。
量的データ同士の相関関係を確認する
次は「y(販売数)」とそれ以外のデータの相関関係を見てみましょう。相関とは、あるデータ同士、例えば「データA」と「データB」の関連性のことです。
データの関係 | 相関 | 備考 |
---|---|---|
Aが増えるとBも増える | 相関あり | 正の相関 |
Aが増えるとBは減る | 相関あり | 負の相関 |
Aが増えるとBはランダムに増減 | 相関なし |
表の1行目と2行目のように「データA」の変化が「データB」の変化と関連性があることを「相関あり」といいます。
相関係数
相関係数とは、データ同士の相関を数値化したもので、-1?1の間の数値で表されます。
相関係数 | -1 | 0 | 1 |
---|---|---|---|
相関 | 相関あり (負の相関) | 相関なし | 相関あり (正の相関) |
表のように「1」または「-1」に近いほど相関が強く、「0」に近いほど相関は少ないことを表します。
相関係数を確認する
Pythonでは数値型データ同士の相関係数を簡単に確認できます。下のコードを実行して下さい。
# 相関係数の表示 train.corr()
コード解説
- train.corr()
- corr関数を使うと数値型データ同士の相関係数を表示できます。
ここでは変数train内に含まれる数値型データの全ての組み合わせの相関係数を表示しています。
実行結果は次のようになります。
「y(販売数)」との相関を知りたいので、「y」列に注目しましょう。「temperature(天気)」が約-0.6、「year(年)」が約-0.5と「y」と相関が高いことを確認できます。
(※「1」となっている箇所は「y」と「y」など同じデータの相関係数で、同じデータなので当然相関は最大の1となります)
ヒートマップ
ヒートマップを使うと損関係数を視覚的に把握できます。下のコードを実行して下さい。
df_corr = train.corr() # 相関係数を取得 sns.heatmap(df_corr) # ヒートマップを描く
コード解説
- df_corr = train_new.corr()
- corr関数を使って変数train内のデータ同士の相関係数を取得し、変数df_corrに入れています。
- sns.heatmap(df_corr, cmap=’Blues’)
- heatmap関数を使うとヒートマップを描けます。
ここでは「(df_corr)」とすることで変数df_corrに入れた相関係数をヒートマップ化しています。
実行結果は次のようになります。
黒い箇所と白い箇所が相関が高いところです。先ほど見た「temperature」と「year」が黒くなっています。今回のケースでは必須というわけではありませんが、データ数が多くなるとヒートマップが便利です。
質的データと量的データの相関関係を確認する
次は質的データと量的データの相関を見てみましょう。
箱ひげ図
箱ひげ図を使うと、質的データと量的データの関係を確認できます。
あまり聞き慣れない箱ひげ図ですが、実は結構シンプルで最小値・四分位数・最大値を図にしたものです。(前述:中央値・四分位数とは)
上のように、箱の部分には上・下25%を除いた中心から50%のデータが含まれています。
また箱ひげ図は高さが低いほど、データが密集していることを表しています。
この図では、50~60くらいの値を持つデータが最も多いことがわかります。
また、箱ひげ図でもヒストグラムと同じように外れ値を確認できます。
外れ値があると上のように小さな点として表示されます。
箱ひげ図での相関関係の見方
質的データの区分ごとに量的データの分布を表示することで、質的データと量的データの間に相関があるかどうか視覚的にチェックできます。
相関がある箱ひげ図
このように区分ごとに量的データの分布が異なる場合、この質的データと量的データの間には何らかの関連性があると考えられます。
相関がない箱ひげ図
このように区分に分けても量的データの分布に差がない場合、この質的データと量的データの間には関連性がないと考えられます。
箱ひげ図を描く
それでは箱ひげ図を描いてみましょう。下のコードを実行して下さい。
train[['y', 'week']].boxplot(by='week')
コード解説
- train[[‘y’, ‘week’]].boxplot(by=’week’)
- boxplot関数を使うことで箱ひげ図を描けます。
ここでは「y(販売数)」と「week(曜日)」の関係性を箱ひげ図にしています。
「by=’week’」を指定することで、曜日別の販売数の分布を表示します。
実行結果は次のようになります。
月曜日は販売数が多い、木曜日は少ないなどある程度の関連性があることが見て取れるのではないでしょうか。
同じ要領で他の質的データとの相関も見てみましょう。下のコードを実行して下さい。
objcols = ['week','soldout','name','remarks','event','payday','weather'] for objcol in objcols: train[['y', objcol]].boxplot(by=objcol)
コード解説
- objcols = [‘week’,’soldout’,’name’,’remarks’,’event’,’payday’,’weather’]
for objcol in objcols:
train[[‘y’, objcol]].boxplot(by=objcol) - 上と同じくboxplot関数で箱ひげ図を描いています。
こちらではobjcolsという名前で質的データの列名が入ったリストを作り、それをfor文でループ処理することでまとめてグラフを出力しています。
実行結果は次のようになります。
「soldout(完売フラグ)」からは、完売した日(1)は販売数が多いことが読み取れます。ただし完売したかどうかわかるのはその日の営業終了後なので、販売数の予測に完売フラグを利用するのは無理がある気がします。
「name(メニュー名)」は種類が多すぎてこの箱ひげ図から情報を読み取るのは難しいです。
下の項目名が見にくいですが、「remarks(特記事項)」に「お楽しみメニュー」がある日は販売数が多いようです。
「event(イベント)」を見ると、キャリアアップ支援セミナーがある時は販売数が落ちるようです。
「payday(給料日)」からは、給料日の時(1.0)にはお弁当の販売数がやや多い傾向にあることが読み取れます。
「weather(天気)」からは「快晴」の日は売れ行きが良く、「薄曇」の日は売れ行きが良くないことなどが読み取れます。
機械学習手順4 学習モデルと評価関数の選定
使用する説明変数(特徴量)の目星がついたら、次は学習モデルと評価関数を決めましょう。
学習モデル
機械学習といっても学習モデルには様々なものがあります。それぞれ得意・不得意があり万能なモデルは今のところ存在しないため、自分の持つ課題にはどのモデルが適しているか試していくことが大切です。
モデル系列 | モデル | 予測対象 | 特徴 |
---|---|---|---|
回帰 | 線形回帰 | 数値 | 回帰によって数値を予測 |
ロジスティック回帰 | 分類 | 回帰によって分類する | |
サポートベクターマシーン(SVM) | 分類 | 回帰によって分類する | |
ツリー | 決定木 | 分類 | 木構造モデルによって分類 |
回帰木 | 数値 | 回帰と決定木の組み合わせモデル | |
ランダムフォレスト | 数値・分類 | 決定木を複数作って集計 | |
勾配ブースティング木 | 数値・分類 | 決定木を複数作って集計 | |
ニューラルネットワーク (NN) | パーセプトロン | 数値・分類 | 入力、中間、出力の3層からなるNN |
畳み込みニューラルネットワーク(CNN) | 数値・分類 | 画像解析が得意なNN | |
再起型ニューラルネットワーク(RNN) | 数値・分類 | 文章解析が得意なNN | |
残差ネットワーク | 数値・分類 | CNNの発展系 | |
ベイズ | 単純ベイズ | 分類 | 文章の単語分類が得意 |
時系列 | AR、MR、ARIMA | 数値 | 予測対象の過去値から予測 |
状態空間モデル | 数値 | 予測対象以外の過去値から予測 |
今回の予測対象は「y(販売数)」という数値なので、数値を予測できるモデルとして線形回帰モデルを選択します。
評価関数
評価関数は、作成した学習モデルによって出された予測結果がどれだけ正しいかを評価する関数です。
対象 | 評価関数 | 値 | 評価方法 | 特徴 |
---|---|---|---|---|
数値 | MAE(平均絶対誤差) | 0~∞ | 小さい程良い | 正解の回数が多いほど高く評価。 |
RMSE(平均平方二乗誤差) | 0~∞ | 小さい程良い | 誤差が大きいほどペナルティを与える。誤差を少なくしたい時に使用。 | |
分類 | Accuracy | 0~1 | 大きい程良い | 解釈しやすいが、データ数のバランスが悪いと評価がズレる。 |
AUC | 0~1 | 大きい程良い | 2値分類問題で使用。データ数のバランスが悪くても上手く評価。 | |
LogLoss | 0~∞ | 小さい程良い | 多値分類問題でも使用可能。 |
今回は数値の予測結果を評価し、なるべく誤差を少なくしたいことからRMAEという評価関数を使用します。
機械学習手順5 過学習の防止
過学習とは、モデルが学習データに過剰適用してしまうことです。
モデルが過学習になると、学習データに対する予測精度は非常に高い反面、実際に予測したい未知のデータに対する予測精度は低くなってしまいます。
ここでは過学習を防ぐために擬似的に未知のデータを作成します。
学習データでモデルを作成したあと、未知のデータを使って予測を行い評価することで、未知のデータに対する予測精度を測ります。
擬似的に未知のデータを作成する
それでは学習データ「train」を分割して評価用データ(未知のデータ)を作成しましょう。今回は学習データ80%、評価データ20%程度になるように分割します。下のコードを実行して下さい。
# 学習データを学習用と評価用に分割 train1 = train[:165] # 学習用データ train2 = train[165:] # 評価用データ
コード解説
- train1 = train[:165]
- train[a:b]でtrainのa行目~b行目までを取得できます。
ここではtrain1という新しい変数にtrainの最初から164行目までのデータを入れています。 - train2 = train[165:]
- ここではtrain2という新しい変数にtrainの165行目から最後までのデータを入れています。
train1の末尾5行、train2の先頭5行をそれぞれ確認してみましょう。
train1には164行目までの2004年7月25日までのデータが、train2には165行目以降の2004年7月28日以降のデータが入っているのが確認できます。
機械学習手順6 モデルの作成・予測・評価
モデルに合わせたデータの準備
今回作成する線形回帰モデルは「教師あり」学習といわれるもので、学習するためには「説明変数(y以外)」と「目的変数(y:販売数)」の2つが必要です。
学習用データtrain1を説明変数と目的変数に分割しましょう。下のコードを実行して下さい。
# train1_Xに説明変数、train1_Yに目的変数を入れる train1_X = pd.get_dummies(train1[['year','month','temperature','week']]) train1_Y = train1['y']
コード解説
- train1_X = pd.get_dummies(train1[[‘year’,’month’,’temperature’,’week’]])
- train1の中から「year(年)」「month(月)」「temperature(気温)」「week(曜日)」の列を取り出して、新たな変数train1_Xの中に入れています。
またget_dummies関数を使って文字型データをダミー変数化しています。 - train1_Y = train1[‘y’]
- train1の中から「y(販売数)」の列だけを取り出して、新たな変数train1_Yの中に入れています。
ダミー変数化とは
ダミー変数化とは、文字型データを数値型データに変換することです。
今回使う線形回帰モデルは、説明変数・目的変数ともに数値型データである必要がありますが、今回は文字型データである「week(曜日)」も説明変数として使いたいので、この「week」をダミー変数化する必要があります。
「week」には上のように曜日が文字型データで入っています。
ダミー変数化すると、上のように月~金それぞれの列が新しく作成され、該当するデータにのみ「1」が設定されます。
それでは先程作成したtrain1_X、train1_Yの中身をhead関数で確認してみましょう。
train1_Xには「year」「month」「temperature」とダミー変数化された「week」が、train1_Yには「y」が入っているのが確認できます。
評価用データtrain2も全く同じように準備しましょう。下のコードを実行して下さい。
# train2_Xに説明変数、train2_Yに目的変数を入れる train2_X = pd.get_dummies(train2[['year','month','temperature','week']]) train2_Y = train2['y']
これで線形回帰モデルに使うデータの準備ができました。
モデルの作成・学習
それではいよいよモデルを作成します。下のコードを実行して下さい。
model = LR() # 線形回帰モデルを作成 model.fit(train1_X, train1_Y) # モデルに学習させる
コード解説
- model = LR()
- 先のセットアップの項目で読み込んだ線形回帰モデル作成モジュール「LinearRegression(LR)」を使ってモデルを作成しています。
- model.fit(train1_X, train1_Y)
- fit関数を使ってモデルにデータを学習させています。
ここでは「(train1_X, train1_Y)」として説明変数に「train1_X」を、目的変数に「train1_Y」を指定しています。
これで学習済みモデルができました。
モデルから予測する
それでは作成したモデルを使って予測してみましょう。下のコードを実行して下さい。
# 学習済みモデルで予測する train1_P = model.predict(train1_X) # 学習用データから予測 train2_P = model.predict(train2_X) # 評価用データから予測
コード解説
- train1_P = model.predict(train1_X)
- predict関数を使うと予測データを取得できます。
ここでは「(train1_X)」として既知の学習用データを目的変数として渡し、新しい変数train1_Pの中に予測結果を入れています。 - train2_P = model.predict(train2_X)
- 上と同じですが、こちらは「(train2_X)」として未知の評価用データを目的変数として渡し、新しい変数train2_Pの中に予測結果を入れています。
予測結果(train1_Pまたはtrain2_P)を見てみましょう。下のコードを実行して下さい。
print(train2_P) # 予測結果を表示
コード解説
- print(train2_P)
- print関数を使うとデータの中身を表示できます。
ここでは「(train2_P)」として評価用データからの予測結果を表示しています。
実行結果は次のようになります。
このようにtrain2_Pにはモデルが予測した日々の「y(販売数)」が入っています。
予測結果を評価する
それでは予測したデータの精度を確認してみましょう。下のコードを実行して下さい。
# 評価する train1_R = np.sqrt(mean_squared_error(train1_Y, train1_P)) train2_R = np.sqrt(mean_squared_error(train2_Y, train2_P)) print('予測誤差/学習用データ:{}、評価用データ:{}'.format(train1_R, train2_R))
コード解説
- train1_R = np.sqrt(mean_squared_error(train1_Y, train1_P))
- ここでは既知のデータの実測値(train1_Y)と予測値(train1_P)の誤差の平均値を取得しています。
- (mean_squared_error関数を使ってMSE(平均二乗誤差)を取得し、sqrt関数で平方根を取ることでRMSE(平均平方二乗誤差)を取得しています)
- train2_R = np.sqrt(mean_squared_error(train2_Y, train2_P))
- ここでは未知のデータの実測値(train2_Y)と予測値(train2_P)の誤差の平均値を取得しています。
- print(‘予測誤差/学習用データ:{}、評価用データ:{}’.format(train1_R, train2_R))
- print関数を使うとデータを表示できます。また、format関数を使うと文字の中に数値を埋め込めます。ここでは「{}」の部分ににtrain1_R、train2_Rを表示させています。
実行結果は次のようになります。
学習用データ(既知データ)での予測誤差は約19個、評価用データ(未知データ)での予測誤差は約20個となりました。既知データと未知データで予測精度にほぼ差がないため、過学習の心配はなさそうです。
機械学習手順⑥ モデルの予測精度を高める
誤差約20個という予測結果が出ましたが、もう少し予測精度を上げてみましょう。
誤差の多いデータを見つける
train1・train2に予測結果を付け足して実測値(y)との差を求めましょう。誤差が大きいデータを見つけて何らかの対応が見つかれば、更に精度を上げられます。下のコードを実行して下さい。
train1 = train1.assign(prev=train1_P) # 予測の列を追加 train2 = train2.assign(prev=train2_P) # 予測の列を追加 train1['diff'] = abs(train1['y'] - train1['prev']) # 誤差の列を追加 train2['diff'] = abs(train2['y'] - train2['prev']) # 誤差の列を追加 trainW = pd.concat([train1, train2]) # train1とtrain2を結合 trainW.sort_values('diff', ascending=False)[:10] # 誤差の多い順にソート
コード解説
- train1 = train1.assign(prev=train1_P)
train2 = train2.assign(prev=train2_P) - assign関数を使うと新たな列を追加できます。
- ここでは「(prev=train1_P)」として、「prev」という名前の列を新しく追加し、そこにtrain1_Pの予測データを入れています。
(2行目の評価用データtrain2についても同じです) - train1[‘diff’] = abs(train1[‘y’] – train1[‘prev’])
train2[‘diff’] = abs(train2[‘y’] – train2[‘prev’]) - 「y(販売数)」から「prev(予測販売数)」を引いた値を「diff」という新しい列を作成してそこに入れています。また引いた値はasb関数を使ってマイナスを消した絶対値に変換しています。
(2行目の評価用データtrain2についても同じです) - trainW = pd.concat([train1, train2])
- concat関数を使うとデータを結合できます。
ここではtrain1とtrain2を結合してtrainWというデータを作成しています。 - trainW.sort_values(‘diff’, ascending=False)[:10]
- sort_values関数を使うとデータを並べ替えて表示できます。
ここでは「(‘diff’, ascending=False)」として「diff」の値を降順でソートしています。また「[:10]」として先頭10行だけを表示しています。つまり誤差の最も大きいもの順に10個を表示しています。
実行結果は次のようになります。
「diff(誤差)」の大きいデータには「remarks(特記事項)」に「お楽しみメニュー」と書かれているものが多いことがわかります。
説明変数(特徴量)を作る
それでは「remarks(特記事項)」と「y(販売数)」の相関関係を再度確認してみましょう。
先の項目で描いた「remarks(特記事項)」の箱ひげ図です。これを見ると一番左の「お楽しみメニュー」の50%が販売数100~120であること、左から二番目の「なし」では50%が販売数60~110であることから、「お楽しみメニュー」があるときは販売数が多くなる傾向が強いことが見て取れます。また、それより右の項目では箱ひげ図がキレイに描かれておらず、これはデータがほとんどないことを意味しています。
以上のことから、「お楽しみメニュー」がある日を「1」、それ以外の日を「0」とした新しい説明変数(特徴量)を作ってみましょう。下のコードを実行して下さい。
# ユーザー定義関数 def is_otanoshimi_funcion(x): if x == 'お楽しみメニュー': return 1 else: return 0 # お楽しみメニューをフラグ化 train['fun'] = train['remarks'].apply(lambda x: is_otanoshimi_funcion(x)) train['fun'] = train['fun'].astype(np.str) # fun列を文字型に変換
コード解説
- def is_otanoshimi_funcion(x):
if x == ‘お楽しみメニュー’:
return 1
else:
return 0 - ここではis_otanoshimi_funcionという関数を定義しています。
- 引数「x」が「お楽しみメニュー」なら「1」を返し、それ以外なら「0」を返すだけの単純な関数です。
- train[‘fun’] = train[‘remarks’].apply(lamda x: is_otanoshimi_funcion(x))
- apply関数を使うと、データ1つ1つに対して関数を実行できます。
ここでは上で定義したis_otanoshimi_funcionという関数に「remarks」の値を渡し、返ってきた値を新しく作成した列「fun」に入れています。 - train[‘fun’] = train[‘fun’].astype(np.str)
- astype関数を使って「fun」列を文字型に変換しています。
中身を確認してみましょう。下のコードを実行して下さい。
train[train['remarks']=='お楽しみメニュー'].head()
コード解説
- train[train[‘remarks’]==’お楽しみメニュー’].head()
- head関数を使って先頭5行を表示しています。
ここでは「train[train[‘remarks’]==’お楽しみメニュー’]」として、「remarks」に「お楽しみメニュー」と設定されている行だけを表示しています。
実行結果は次のようになります。
「remarks」に「お楽しみメニュー」がある行の「fun」に「1」が設定されています。これで新しい説明変数を作成できました。
モデルの再作成・予測・再評価
それでは先にモデルを作成した時に使った説明変数「year、month、week、temperature」に「fun」を加え、もう一度モデル作成→予測→評価をしてみましょう。下のコードを実行して下さい。
# 学習データを学習用と評価用に分割 train1 = train[:165] train2 = train[165:] # 説明変数と目的変数の抽出 train1_X = pd.get_dummies(train1[['year','month','temperature','week','fun']]) train1_Y = train1['y'] train2_X = pd.get_dummies(train2[['year','month','temperature','week','fun']]) train2_Y = train2['y'] # モデルの元を作成 model = LR() # 重回帰モデルを作成 model.fit(train1_X, train1_Y) #説明変数, 目的変数 # 学習データから予測する train1_P = model.predict(train1_X) # 学習用データで予測 train2_P = model.predict(train2_X) # 評価用データで予測 # 評価する train1_R = np.sqrt(mean_squared_error(train1_Y, train1_P)) train2_R = np.sqrt(mean_squared_error(train2_Y, train2_P)) print('予測誤差/学習用データ:{}、評価用データ:{}'.format(train1_R, train2_R))
実行結果は次のようになります。
学習用データでの誤差が約19→16、評価用データでの誤差は約20→14と、ともに予測精度が向上しました。
まとめ
最後まで読み進めて頂きありがとうございます。そしてお疲れ様でした!
機械学習の流れは理解できたでしょうか。機械学習=モデル作成というイメージですが、実際は多くの時間をデータの準備に費やしていることを感じられたと思います。
機械学習はデータ選択やモデル選択によって予測結果が大きく変わります。ここで挙げた流れも一例で、学習に使うデータを少し変えたり採用する学習モデルを変えるだけでも予測精度を上げることができます。
誤記・間違い、わからない箇所、上手くいかない箇所、または、上手くいった!もっといい方法があるよ!などなどありましたら下のコメント欄からお気軽にコメント頂けると幸いです。
コメント