まだ解法のまとめ中なので、随時更新していきます。
大会の概要
客の予約時のデータと実際に来店した際のデータを使用して、将来の各レストランに訪れる客の数を予測するというもの。
評価指標
このコンペでの評価指標はRMSLE
でした。
$$
\sqrt{\frac{1}{n} \sum_{i=1}^{n} (log(p_{i}+1) - log(a_{i}+1))^{2}}
$$
この指標はRMSE
と比較して以下の違いが存在します。
under-estimate vs over-estimate
予測値が実際の値よりも低い場合と、高い場合を見てみます。
- 実測値=1000, 予測値=600
- RMSE=400, RMSLE=0.5108
- 実測値=1000, 予測値=600
- RMSE=400, RMSLE=0.3365
となっており予測値が実際の値よりも低い場合により大きな損失を与えます。
これは店舗の在庫などが余ってしまわないような状況を想定していると考えられます。
magnitude of numbers
予測値と実際の値との差が同じで、元の数値の大きさが異なる場合を考えてみます。
- 実測値=1000, 予測値=1400
- RMSE=400, RMSLE=0.3365
- 実測値=10000, 予測値=10400
- RMSE=400, RMSLE=0.0392
これは実際の店舗を考えてみると、元の店舗のキャパシティが大きければ大きいほど、ある程度誤差に寛容にできることを意味しています。
データ
データの構造は以下のようになっています。
-
予約データ(HPG)
- Store id
- 来店した時間
- 予約が行われた時間
- 予約した人数
-
レストランの情報(HPG & AIR)
- Store id
- 店舗のジャンル(例:Italyan / French)
- 場所
- 緯度
- 経度
-
来店者の情報(AIR)
- 日付と時間
- Store id
- 来店者数
-
カレンダーの情報
- 日付
- 曜日
- 休日かどうか
ホットペッパー(HPG)の情報とairレストラン(AIR)の情報を合わせたものです。
それぞれ店舗の数は以下のように確認を行います。
print(os.listdir("../input"))
>> ['air_store_info.csv', 'hpg_store_info.csv', 'sample_submission.csv',
'date_info.csv', 'hpg_reserve.csv', 'air_visit_data.csv',
'air_reserve.csv', 'store_id_relation.csv']
df_air_store = pd.read_csv('air_store_info.csv')
print(len(df_air_store['air_store_id'].unique()))
>> 829
df_hpg_store = pd.read_csv('hpg_store_info.csv')
print(df_hpg_store['hpg_store_id'].unique())
>> 4690
特徴量の可視化
与えられたデータの期間
sample_submission
のデータに含まれているid
には店舗のIDと日付の情報が混ざっており、まずはこれを分けた後に日付の情報などを確認します。
df_train = pd.read_csv('air_visit_data.csv')
df_test = pd.read_csv('sample_submission.csv')
df_test['air_store_id'] = df['id'].str[:-11]
df_test['visit_date'] = df['id'].str[-10:]
df_test.drop('id', axis='columns', inplace=True)
print('訓練データの開始日は', df_train.visit_date.iloc[0])
>> '2016-01-13'
print('訓練データの終了日は', df_train.visit_date.iloc[-1])
>> '2017-04-22'
print('テストデータの開始日は', df_test.visit_date.iloc[0])
>> '2017-04-23'
print('テストデータの終了日は', df_test.visit_date.iloc[-1])
>> '2017-05-31'
問題点としては、テストデータの期間にゴールデンウィークが含まれていることであり、この期間の処理には注意を払う必要があります。
実際の来店者数
実際の来店者数と予測数を確認してみる。
#Visitor each day
f,ax = plt.subplots(1,1,figsize=(15,8))
plt1 = df_train_air.groupby(['visit_date'], as_index=False).agg({'visitors': np.sum})
plt1=plt1.set_index('visit_date')
plt1.plot(color='salmon', kind='area', ax=ax)
plt.ylabel("Sum of Visitors")
plt.title("Visitor and Reservations")
途中でvisitors
が急に増加していることと、季節や週レベルでのトレンドが存在していることが確認できます。
つまり、それらの特徴量を組み込むと有効だと思えます。
時間帯ごとに
air_reserve = pd.read_csv('../input/air_reserve.csv')
air_reserve['visit_hour'] = pd.to_datetime(air_reserve['visit_datetime']).dt.hour
air_reserve.groupby('visit_hour')['reserve_visitors'].sum().plot.bar()
店舗のジャンル
ジャンルごとの店舗数は以下のように確認します。
air_store.air_genre_name.value_counts().sort_values().plot(kind='barh')
居酒屋と喫茶店が多くを占めています。
店舗の位置
店舗がより多く存在している地点は以下のように確認します。
air_store.air_area_name.value_counts()[:15].sort_values().plot(kind='barh')
解法
検証データの作成法
このデータセットは時系列なので、通常のk-fold
を使用することができません。もしランダムにデータを抽出してしまうと、過去のデータと未来のデータが混じってしまい、リークが生じていますからです。
そこで時系列に沿ってデータを分割していきます。
使用された特徴量
-
store_id
-
year, month, day of month, day of week
-
来店客の統計情報
- 日曜でのMin, Max, Mean, Median, ...
-
休日フラグ
-
予約数
-
予約時と来店時の時間差
-
移動平均などのWindow情報
-
近隣の店舗の人気度
-
県名と市区町村名
-
経度、緯度
-
...