9
9

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 5 years have passed since last update.

まだ解法のまとめ中なので、随時更新していきます。

大会の概要

客の予約時のデータと実際に来店した際のデータを使用して、将来の各レストランに訪れる客の数を予測するというもの。

評価指標

このコンペでの評価指標は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")

日付ごとの来客数.png

途中で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()

時間帯ごとの予約客数.png

店舗のジャンル

ジャンルごとの店舗数は以下のように確認します。

air_store.air_genre_name.value_counts().sort_values().plot(kind='barh')

じぇんるごとの店舗数.png

居酒屋と喫茶店が多くを占めています。

店舗の位置

店舗がより多く存在している地点は以下のように確認します。

air_store.air_area_name.value_counts()[:15].sort_values().plot(kind='barh')

場所ごとの店舗数.png

解法

検証データの作成法

このデータセットは時系列なので、通常のk-foldを使用することができません。もしランダムにデータを抽出してしまうと、過去のデータと未来のデータが混じってしまい、リークが生じていますからです。

そこで時系列に沿ってデータを分割していきます。

時系列の分割法.png

使用された特徴量

  • store_id

  • year, month, day of month, day of week

  • 来店客の統計情報

    • 日曜でのMin, Max, Mean, Median, ...
  • 休日フラグ

  • 予約数

  • 予約時と来店時の時間差

  • 移動平均などのWindow情報

  • 近隣の店舗の人気度

  • 県名と市区町村名

  • 経度、緯度

  • ...

参考資料

9
9
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
9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?