「雨の日には、全体的に売上が落ち込む」
「猛暑日にはビールが人気」
「肌寒くなって来ると、おでんが売れ筋になる」
気温や降雨量等の気象条件が、購買行動に影響を与えることは古くから知られており、発注計画や販促施策に役立てられてきました。
特に小売業界では活用が進んでおり、在庫コストや品不足による機会損失をできる限り抑えることを目的に、殆どの企業が気象情報に基づいた販促計画を実施しています。
さらに近年では、気象情報をもとにより高度な販促支援をするツールが登場し始めました。
例えば、天気予報専門メディア「tenki.jp」では、
ユーザーの位置情報やアプリの利用状況を気象情報とかけ合わせて分析し、天候条件を踏まえて最適なタイミングで商品を広告配信する「天気プッシュ」と呼ばれるサービスを展開しています。
このように、気象データのビジネス活用は、古くから商品販売において重視されている一方で、近年はより一層注目が集まる分野です。
そこで、今回のコンペティションでは、気温や湿度等の気象情報をもとに、あるコンビニで扱う商品について売上数を予測するアルゴリズムを構築していただきます。
コンペティションを通じて、気象情報と商品売上との間にある、隠れた法則を探してみてください。
1位 100,000円
※ 対象者には、コンペティション終了後、メールにて連絡いたします。
提出締切 2022/11/13 22:00 JST
LB公開 2022/11/13 24:00 JST
データをダウンロードするにはログインまたはユーザー登録して下さい
コンペでは主に2種類のデータを使用します。
訓練データおよび、テストデータには、それぞれ350、21レコードのサンプルデータが含まれています。
訓練データには、各日の天候情報と各商品売上数が記載されています。機械学習モデルの構築に使用ください。
構築した機械学習モデルを使用して、テストデータセットの対象期間3週間における商品ごとの売上数を予測してください。
データセットのカラムは以下の通りになっています。
カラム名 | 説明 | 例 |
---|---|---|
id | ユニークキー | |
date | 日付 | |
highest | 最高気温 | |
lowest | 最低気温 | |
rain | 降水量(mm) | |
ice1 | 商品ice1の売上数 | |
ice2 | 商品ice2の売上数 | |
ice3 | 商品ice3の売上数 | |
oden1 | 商品oden1の売上数 | |
oden2 | 商品oden2の売上数 | |
oden3 | 商品oden3の売上数 | |
oden4 | 商品oden4の売上数 | |
hot1 | 商品hot1の売上数 | |
hot2 | 商品hot2の売上数 | |
hot3 | 商品hot3の売上数 | |
dessert1 | 商品dessert1の売上数 | |
dessert2 | 商品dessert2の売上数 | |
dessert3 | 商品dessert3の売上数 | |
dessert4 | 商品dessert4の売上数 | |
dessert5 | 商品dessert5の売上数 | |
drink1 | 商品drink1の売上数 | |
drink2 | 商品drink2の売上数 | |
drink3 | 商品drink3の売上数 | |
drink4 | 商品drink4の売上数 | |
drink5 | 商品drink5の売上数 | |
drink6 | 商品drink6の売上数 | |
alcol1 | 商品alcol1の売上数 | |
alcol2 | 商品alcol2の売上数 | |
alcol3 | 商品alcol3の売上数 | |
snack1 | 商品snack1の売上数 | |
snack2 | 商品snack2の売上数 | |
snack3 | 商品snack3の売上数 | |
bento1 | 商品bento1の売上数 | |
bento2 | 商品bento2の売上数 | |
bento3 | 商品bento3の売上数 | |
bento4 | 商品bento4の売上数 | |
tild1 | 商品tild1の売上数 | |
tild2 | 商品tild2の売上数 | |
men1 | 商品men1の売上数 | |
men2 | 商品men2の売上数 | |
men3 | 商品men3の売上数 | |
men4 | 商品men4の売上数 | |
men5 | 商品men5の売上数 | |
men6 | 商品men6の売上数 |
テストデータセットの天候情報から、各商品の売上数。y(売上数)を非負整数で予測してくだい。
モデルの予測性能は評価関数”Pinball loss Error”で評価されます。
$$
\frac{1}{|q|} \sum_{\tau \in q} L_{\tau}(y, \hat{y})
$$
$$
\begin{array}{ll} L_{\tau}(y,\hat{y}) & = & (y - \hat{y}) \tau & \textrm{ if } y \geq \hat{y} \\ & = & (\hat{y} - y) (1 - \tau) & \textrm{ if } \hat{y} > y \end{array}
$$
回答用のsubmission.csvを用意する(エントリーとヘッダー行を含む) 。
提出されたファイルに余分な行や列が含まれていた場合はエラーとなります。
提出ファイルは以下の列のみを必ず含んでください:
id,ice1_0.01,ice1_0.1,ice1_0.5,ice1_0.9,ice1_0.99,ice2_0.01,ice2_0.1,ice2_0.5
1,18.517,21.043,27.061,62.297,128.690,63.462,64.212,65.504
2,14.840,17.721,23.035,61.899,141.993,54.360,54.967,56.546
3,4.989,9.192,11.768,37.785,105.411,29.853,30.132,31.939
4,8.009,11.868,14.877,41.923,105.885,37.590,37.927,39.578
5,14.050,17.141,21.883,53.773,119.423,52.465,53.039,54.487
6,13.868,17.204,21.084,40.240,77.116,52.221,52.763,53.896
7,10.020,13.858,17.120,30.913,63.966,42.271,42.763,44.002
8,11.314,14.973,18.608,34.460,69.959,45.492,46.026,47.245
9,14.686,17.894,22.461,42.532,81.912,53.878,54.524,55.661
10,22.174,24.287,31.141,66.414,127.121,72.532,73.416,74.509
本コンペでは、開催期間終了後 賞金対象者のコードを公開し、ユーザーの皆様にチーティング有無をレビューしていただき順位確定させる、オープンレビュー方式のコンペティションを行います。
コード公開後1週間:
レビュアー(ユーザー)より、チーティングの疑いに関するコメントがある場合は、ご回答をお願いいたします。
※チーティングとは無関係のコメント(ノウハウに関する質疑 等)についてもご回答いただけると幸いですが、順位確定の判断材料とは致しません。
レビュアーからの質疑と、回答状況をふまえて、最終的に運営側で順位確定を判断します。
開始日 2022/8/16 14:00 JST
提出締切 2022/11/13 22:00 JST
LB公開 2022/11/13 24:00 JST
エントリー締め切り なし
ユーザー間での情報共有
コンペティションに関連するコード・データを、チーム外のユーザーと共有することはできません。全参加者が利用できる場合に限り、共有可能です。
外部データ/学習済みモデルの使用
本コンペティションの基本情報/データから取得できるデータのみを用いてチャレンジして下さい。コンペ外データを用いて学習されたモデルの使用も禁止とします。
※コンペ期間中であっても、不正が疑われる場合は、運営より確認のためメール連絡させていただくことがございます。一週間以内にご回答いただけない場合も、不正と判断させていただきます。
公平性の担保、チーティング等の不正防止のため、予告なくルールの追加・変更を行う場合がございます。
ご不便をおかけすることもあるかと思いますが、サービス向上のためご了承ください。
はい。
最も精度の高い学習モデルを作成した優勝者には、賞金10万円を贈呈します。
順位確定までのプロセスについては、ルール「Open Review Competition」を参照ください。
可能です。チームページから作成いただけます。
こちらから作成いただけます。
コンペティション参加にはアカウント登録が必要となりますのでご注意ください。
Seed を固定することが推奨です。
ただし、Seed を固定しなくても提出用コードとしては認めています。
ファイル提出後、23時間ごとにリセットとなります。
グローバル展開していくにあたり、居住国(時差)による有利・不利を最小化するため、一定時間でリセットする仕様としております。
このチュートリアルでは、店舗の売上データに対して
を行います。
import pandas as pd
import numpy as np
import statsmodels
!python3 --version
print("Pandas", pd.__version__)
print("Numpy", np.__version__)
print("statsmodels", statsmodels.__version__)
import matplotlib
print("Matplotlib", matplotlib.__version__)
Python 3.9.13
Pandas 1.4.3
Numpy 1.22.4
statsmodels 0.13.2
Matplotlib 3.5.2
まずはデータを読み込んで見ましょう。csvデータの読み込みは複数のやり方がありますが、pandas
のread_csv
関数はその中でも機能が豊富で、一般的に使われている関数です。
こちらの関数はcsvデータを読み込み、pandas.DataFrame
として文字列を返してくれます。
#データの読み込み
import pandas as pd
train_data = pd.read_csv("train_data.csv", index_col='id')
print(train_data.shape)
(350, 43)
また、DataFrame
には.head()
というメソッドが定義されており、これを呼び出すとDataFrame
の先頭の数行を確認できます。
columns = train_data.columns
train_data.head()
date | highest | lowest | rain | ice1 | ice2 | ice3 | oden1 | oden2 | oden3 | ... | bento3 | bento4 | tild1 | tild2 | men1 | men2 | men3 | men4 | men5 | men6 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
id | |||||||||||||||||||||
1 | 4/11 | 21.9 | 12.4 | 0.0 | 25 | 72 | 26 | 10 | 23 | 52 | ... | 70 | 27 | 12 | 12 | 57 | 30 | 41 | 38 | 37 | 35 |
2 | 4/12 | 25.9 | 13.9 | 0.0 | 30 | 85 | 33 | 9 | 18 | 42 | ... | 23 | 9 | 5 | 8 | 19 | 9 | 13 | 26 | 4 | 16 |
3 | 4/13 | 20.9 | 11.9 | 0.0 | 21 | 68 | 28 | 12 | 22 | 57 | ... | 19 | 6 | 4 | 9 | 23 | 9 | 11 | 33 | 4 | 13 |
4 | 4/14 | 18.8 | 11.4 | 0.0 | 19 | 62 | 35 | 13 | 29 | 62 | ... | 74 | 28 | 15 | 17 | 55 | 35 | 46 | 46 | 51 | 46 |
5 | 4/15 | 22.1 | 13.5 | 19.0 | 21 | 72 | 32 | 10 | 24 | 44 | ... | 69 | 26 | 4 | 9 | 54 | 33 | 39 | 40 | 41 | 40 |
5 rows × 43 columns
訓練データの商品について、価格分を確認してみましょう。
import matplotlib.pyplot as plt
%matplotlib inline
plt.hist(train_data['ice1'], log=True)
(array([295., 28., 16., 9., 0., 1., 0., 0., 0., 1.]),
array([ 13. , 74.5, 136. , 197.5, 259. , 320.5, 382. , 443.5, 505. ,
566.5, 628. ]),
<a list of 10 Patch objects>)
plt.hist(train_data['ice2'], log=True)
(array([13., 59., 56., 43., 55., 55., 30., 28., 10., 1.]),
array([ 16. , 29.7, 43.4, 57.1, 70.8, 84.5, 98.2, 111.9, 125.6,
139.3, 153. ]),
<a list of 10 Patch objects>)
外れ値を含むカラムと含まないカラムがあるようです
続いて、下記コードから、使用するデータ全体の概観を確認します。
各項目について欠損値('null')がどの程度含まれているか確認できます。データの形式も判別可能です。
train_data.isnull().sum(axis=0)
date 0
highest 0
lowest 0
rain 0
ice1 0
ice2 0
ice3 0
oden1 0
oden2 0
oden3 0
oden4 0
hot1 0
hot2 0
hot3 0
dessert1 0
dessert2 0
dessert3 0
dessert4 0
dessert5 0
drink1 0
drink2 0
drink3 0
drink4 0
drink5 0
drink6 0
alcol1 0
alcol2 0
alcol3 0
snack1 0
snack2 0
snack3 0
bento1 0
bento2 0
bento3 0
bento4 0
tild1 0
tild2 0
men1 0
men2 0
men3 0
men4 0
men5 0
men6 0
dtype: int64
欠損値が含まれているカラムは特にないようです。
モデルに投入する目的変数(y)、説明変数(X)を作成します。
import statsmodels.api as sm
from statsmodels.regression.quantile_regression import QuantReg
train_columns = ['highest', 'lowest', 'rain']
target_columns = columns[4:]
x = train_data[train_columns]
X = sm.add_constant(x)
Y = train_data[target_columns]
いよいよ予測モデルの作成に入ります。model.fit('説明変数','目的変数')
と記述することでモデルの学習をします。目的変数を説明変数の組み合わせにより説明可能とする回帰モデルを作成できます。
Y
ice1 | ice2 | ice3 | oden1 | oden2 | oden3 | oden4 | hot1 | hot2 | hot3 | ... | bento3 | bento4 | tild1 | tild2 | men1 | men2 | men3 | men4 | men5 | men6 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
id | |||||||||||||||||||||
1 | 25 | 72 | 26 | 10 | 23 | 52 | 35 | 180 | 254 | 270 | ... | 70 | 27 | 12 | 12 | 57 | 30 | 41 | 38 | 37 | 35 |
2 | 30 | 85 | 33 | 9 | 18 | 42 | 26 | 202 | 219 | 235 | ... | 23 | 9 | 5 | 8 | 19 | 9 | 13 | 26 | 4 | 16 |
3 | 21 | 68 | 28 | 12 | 22 | 57 | 31 | 164 | 210 | 223 | ... | 19 | 6 | 4 | 9 | 23 | 9 | 11 | 33 | 4 | 13 |
4 | 19 | 62 | 35 | 13 | 29 | 62 | 33 | 193 | 242 | 251 | ... | 74 | 28 | 15 | 17 | 55 | 35 | 46 | 46 | 51 | 46 |
5 | 21 | 72 | 32 | 10 | 24 | 44 | 33 | 218 | 271 | 274 | ... | 69 | 26 | 4 | 9 | 54 | 33 | 39 | 40 | 41 | 40 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
346 | 27 | 77 | 30 | 29 | 36 | 77 | 53 | 147 | 197 | 206 | ... | 17 | 9 | 5 | 8 | 23 | 12 | 13 | 35 | 4 | 17 |
347 | 14 | 33 | 32 | 76 | 73 | 159 | 96 | 116 | 221 | 207 | ... | 72 | 26 | 5 | 8 | 51 | 31 | 39 | 40 | 37 | 39 |
348 | 20 | 51 | 34 | 104 | 108 | 185 | 134 | 105 | 193 | 189 | ... | 70 | 28 | 5 | 10 | 51 | 31 | 39 | 37 | 31 | 38 |
349 | 19 | 50 | 33 | 79 | 91 | 160 | 94 | 111 | 211 | 207 | ... | 20 | 8 | 6 | 8 | 21 | 9 | 11 | 27 | 4 | 18 |
350 | 19 | 53 | 31 | 18 | 30 | 69 | 42 | 161 | 232 | 251 | ... | 63 | 27 | 6 | 7 | 52 | 30 | 36 | 41 | 47 | 36 |
350 rows × 39 columns
#回帰モデルのインスタンス化
model = [QuantReg(Y[c], X) for c in target_columns]
qs = np.array([0.01, 0.1, 0.5, 0.9, 0.99])
#予測モデルの作成
[[m.fit(q=q) for q in qs] for m in model]
最後に、作成したモデルを使用してテストデータでの売上数を予測します。
path_test = "test_data.csv"
test_data = pd.read_csv(path_test, index_col='id')
test_data.head()
date | highest | lowest | rain | |
---|---|---|---|---|
id | ||||
1 | 3/27 | 19.7 | 7.3 | 0.0 |
2 | 3/28 | 16.9 | 9.0 | 0.0 |
3 | 3/29 | 9.3 | 6.8 | 0.0 |
4 | 3/30 | 11.7 | 7.2 | 3.5 |
5 | 3/31 | 16.3 | 7.3 | 1.5 |
x = test_data[train_columns]
test_X = sm.add_constant(x)
results = dict({})
for target in target_columns:
model = QuantReg(Y[target], X)
for q in qs:
res = model.fit(q=q)
pred_y = test_X @ res.params
#pred_y = model.predict(test_X)
results[(target, q)] = pred_y
/usr/local/lib/python3.7/site-packages/statsmodels/regression/quantile_regression.py:192: IterationLimitWarning: Maximum number of iterations (1000) reached.
") reached.", IterationLimitWarning)
/usr/local/lib/python3.7/site-packages/statsmodels/regression/quantile_regression.py:192: IterationLimitWarning: Maximum number of iterations (1000) reached.
") reached.", IterationLimitWarning)
/usr/local/lib/python3.7/site-packages/statsmodels/regression/quantile_regression.py:192: IterationLimitWarning: Maximum number of iterations (1000) reached.
") reached.", IterationLimitWarning)
/usr/local/lib/python3.7/site-packages/statsmodels/regression/quantile_regression.py:192: IterationLimitWarning: Maximum number of iterations (1000) reached.
") reached.", IterationLimitWarning)
/usr/local/lib/python3.7/site-packages/statsmodels/regression/quantile_regression.py:192: IterationLimitWarning: Maximum number of iterations (1000) reached.
") reached.", IterationLimitWarning)
/usr/local/lib/python3.7/site-packages/statsmodels/regression/quantile_regression.py:192: IterationLimitWarning: Maximum number of iterations (1000) reached.
") reached.", IterationLimitWarning)
submit_rows = [[f'{k[0]}_{k[1]}']+ list(v.values) for k, v in results.items()]
先ほど使用したmodel.predict('説明変数')
の'説明変数'にテストデータの値を代入することで、テストデータの予測値を算出できます。
下記のようにして、提出用のsubmission.csv
を出力します。
#テスト結果の出力
submit_df = pd.DataFrame(np.array(submit_rows)[:, 1:], index=np.array(submit_rows)[:, 0])
submit_df.columns = list(range(1, 22))
submit_df = submit_df.transpose()
submit_df.index.name = 'id'
submit_df.to_csv('submission.csv')
コンペティションへの参加に際しては、ProbSpace利用規約(以下、「利用規約」といいます。)に加え、本ProbSpace参加規約(以下「本規約」といいます。)に同意いただく必要があります。利用規約にて定義された用語は、本規約においても同様の意味で用いられるものとします。
第1条(適用)
第2条(定義)
本規約において次の各用語の定義は、それぞれ以下に定めるとおりとします。
第3条(権利の帰属)
第4条(入賞者の義務)
第5条(禁止事項)
第6条(本コンペの変更、中断、終了等)
第7条(損害賠償)
第8条(本規約の変更)
当社は、必要と判断した場合には、参加者に対して事前に通知する(本コンペにかかる当社ウェブサイト上での告知その他当社が適当と認める方法を含みます。)ことにより、いつでも本規約を変更することができるものとします。なお、変更内容の通知後、参加者が当社の定める期間内に本コンペへの参加を取り消す手続をとらなかった場合には、当該参加者は変更後の規約に同意したものとみなされます。当社は、本規約の変更により参加者に生じたすべての損害について一切の責任を負いません。
第9条(その他)
本契約の準拠法は日本法とし、本契約に起因し又は関連する一切の紛争については、当社の本店所在地を管轄する裁判所を第一審の専属的合意管轄裁判所とします。
(制定)2020年6月22日