前年同月 or 同年前月 の価格を予測値として出力 (LB: 0.19131)

Nishika開催された同様のコンペ (※1) では、予測値として 前年同月 or 同年前月 の価格を使うだけ (※2) で4位/1046人でした。本コンペでも同様の方法が使えるのではないかと思い試してみましたので、ご紹介します。

※1 https://competition.nishika.com/competitions/yasai/summary
※2 https://competition.nishika.com/competitions/yasai/topics/353

(2023-07-25更新) RMSLEの定義がsklearnと異なっていたため修正しました。また、予測方法に前年翌月を追加しました。その他、不要なコードを一部削りました。

セットアップ

import numpy as np
import pandas as pd
train_data = pd.read_csv('data/train_data.csv', index_col=0, parse_dates=[0])
sample_submission = pd.read_csv('data/submission.csv')

前処理

あらかじめ価格を対数変換しておきます。

train_data = np.log1p(train_data)

また、この後の処理を書きやすくするため、行と列に分かりやすい名前を付けておきます。

train_data = train_data.rename_axis(index='date', columns='vegetable')
train_data.head()
vegetable えのきだけ_中国 えのきだけ_九州 えのきだけ_北海道 えのきだけ_北陸 えのきだけ_四国 えのきだけ_東北 えのきだけ_東海 えのきだけ_近畿 えのきだけ_関東 かぶ_北海道 ... レタス_関東 生しいたけ_中国 生しいたけ_九州 生しいたけ_北海道 生しいたけ_北陸 生しいたけ_四国 生しいたけ_東北 生しいたけ_東海 生しいたけ_近畿 生しいたけ_関東
date
2016-01-01 5.752573 5.710427 5.720312 5.616771 5.590987 5.365976 5.631212 5.655992 5.634790 4.828314 ... 5.605802 7.002156 6.955593 6.603944 7.116394 6.929517 6.896694 7.021084 7.015712 7.020191
2016-02-01 5.726848 5.758902 5.736572 5.666427 5.683580 5.631212 5.659482 5.690359 5.676754 5.062595 ... 5.743003 6.985642 6.863803 6.621406 7.124478 6.945051 6.872128 7.038784 7.028201 7.018402
2016-03-01 5.505332 5.384495 5.762051 5.393628 5.429346 5.105945 5.351858 5.365976 5.323010 5.225747 ... 5.631212 6.853299 6.606650 6.591674 7.092574 6.787845 6.848005 6.897705 6.843750 6.951772
2016-04-01 5.429346 5.209486 5.720312 5.278115 5.313206 5.036953 5.327876 5.318120 5.247024 5.247024 ... 5.347108 6.775366 6.620073 6.599870 6.922644 6.719013 6.788972 6.829794 6.783325 6.890609
2016-05-01 5.451038 5.313206 5.726848 5.332719 5.375278 5.141664 5.402677 5.398163 5.332719 5.017280 ... 5.111988 6.787845 6.711740 6.566672 6.962243 6.794587 6.766192 6.849066 6.853299 6.898715

5 rows × 340 columns

学習

まず、予測値として前年翌月/前年同月/同年前月の価格を出力する方法を使って学習データに対して予測します。

train_data_pred = pd.concat(
    # 前年翌月の価格を予測値として使う。
    [
        pd.DataFrame({
            'id': train_data.columns,
            'year': year,
            'method': 'lynm',
            'y': train_data.loc[f'{year}-12-01', :],
            'y_pred': train_data.loc[f'{year - 1}-01-01', :]
        })
        for year in [2018, 2017]
    ] +
    # 前年同月の価格を予測値として使う。
    [
        pd.DataFrame({
            'id': train_data.columns,
            'year': year,
            'method': 'lycm',
            'y': train_data.loc[f'{year}-12-01', :],
            'y_pred': train_data.loc[f'{year - 1}-12-01', :]
        })
        for year in [2018, 2017]
    ] +
    # 同年前月の価格を予測値として使う。
    [
        pd.DataFrame({
            'id': train_data.columns,
            'year': year,
            'method': 'cylm',
            'y': train_data.loc[f'{year}-12-01', :],
            'y_pred': train_data.loc[f'{year}-11-01', :]
        })
        for year in [2018, 2017, 2016]
    ],
    ignore_index=True
)
train_data_pred.head()
id year method y y_pred
0 えのきだけ_中国 2018 lynm 5.683580 5.700444
1 えのきだけ_九州 2018 lynm 5.641907 5.590987
2 えのきだけ_北海道 2018 lynm 5.556828 5.730100
3 えのきだけ_北陸 2018 lynm 5.513429 5.609472
4 えのきだけ_四国 2018 lynm 5.587249 5.627621

次に、野菜と地域の組み合わせそれぞれについて、最もRMLSEが小さくなる方を最適な予測方法として選びます。

best_pred_method = (
    train_data_pred
    .assign(rmsle = lambda df: df['y'] - df['y_pred'])
    .groupby(['id', 'method'], as_index=False)['rmsle'].agg(lambda s: np.sqrt(np.mean(s ** 2)))
    .loc[lambda df: df['rmsle'] == df.groupby('id')['rmsle'].transform(np.min), ['id', 'method']]
    .reset_index(drop = True)
)
best_pred_method.head()
id method
0 えのきだけ_中国 cylm
1 えのきだけ_九州 cylm
2 えのきだけ_北海道 cylm
3 えのきだけ_北陸 cylm
4 えのきだけ_四国 cylm

最適な予測方法を使った場合の、学習データに対する予測精度を確認します。

(
    train_data_pred
    .loc[lambda df: df['year'] >= 2017, :]
    .merge(best_pred_method, how='inner', on=['id', 'method'])
    .assign(rmsle = lambda df: df['y'] - df['y_pred'])
    .groupby(['year'], as_index=False)['rmsle'].agg(lambda s: np.sqrt(np.mean(s ** 2)))
)
year rmsle
0 2017 0.209963
1 2018 0.170055

予測

最後に、2019年12月の価格を予測し、提出ファイルを作ります。

submission = (
    pd.concat(
        [
            # 前年翌月の価格を予測値として使う。
            pd.DataFrame({
                'id': train_data.columns,
                'method': 'lynm',
                'y': train_data.loc[f'2018-01-01', :]
            }),
            # 前年同月の価格を予測値として使う。
            pd.DataFrame({
                'id': train_data.columns,
                'method': 'lycm',
                'y': train_data.loc[f'2018-12-01', :]
            }),
            # 同年前月の価格を予測値として使う。
            pd.DataFrame({
                'id': train_data.columns,
                'method': 'cylm',
                'y': train_data.loc[f'2019-11-01', :],
            })
        ],
        ignore_index=True
    )
    # 最適な予測方法で予測した結果だけ抽出する。
    .merge(best_pred_method, how='inner', on=['id', 'method'])
    .drop(columns=['method'])
    # 対数変換してあるので、元に戻す。
    .assign(y = lambda df: np.expm1(df['y']))
    # 予測結果を指定された順序に並び替える。
    .merge(sample_submission[['id']], how='right', on='id')
)
submission.to_csv('submission/submission_20230725_01.csv', index=False, header=True)

添付データ

  • submission_20230724_02.ipynb?X-Amz-Expires=10800&X-Amz-Date=20240423T115913Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIP7GCBGMWPMZ42PQ
  • submission_20230725_01.ipynb?X-Amz-Expires=10800&X-Amz-Date=20240423T115913Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIP7GCBGMWPMZ42PQ
  • Favicon
    new user
    コメントするには 新規登録 もしくは ログイン が必要です。