ノートブックの概要¶

  • 特徴量を追加
  • モデルはsklearnのGradientBoostingRegressor
  • 交差検証は年ごとで区切ったTimeSeriesSplit
  • パラメータチューニングはOptuna

前処理¶

In [1]:
import polars as pl
pl.Config.set_fmt_str_lengths(100)
pl.Config.set_tbl_cols(100)
import numpy as np

import matplotlib.pyplot as plt
import japanize_matplotlib
import seaborn as sns
%matplotlib inline

from datetime import date
import jpholiday

from prophet import Prophet

from sklearn.metrics import mean_absolute_error
from sklearn.ensemble import GradientBoostingRegressor
In [2]:
# データ読み込みから前処理まで

# データ読み込み
train = pl.read_csv("../data/input/train.csv")
test = pl.read_csv("../data/input/test.csv")

# id列関連の前処理
# 元の"datetime" 列(str型)のコピーを "id" という名前で新しい列として先頭に追加する。その後、"datetime" 列をdate型に変換する。
train = train.insert_column(0, train["datetime"].alias("id")).with_columns(
    pl.col("datetime").str.strptime(dtype=pl.Date)
)
test = test.insert_column(0, test["datetime"].alias("id")).with_columns(
    pl.col("datetime").str.strptime(dtype=pl.Date)
)

# 休業日(close = 1)(と、お盆だけど休業していなくて結局引っ越し数0だった日)を分離する
train = train.filter(
    (pl.col("close") != 1)
    & (pl.col("id") != "2010-08-18")
    & (pl.col("id") != "2011-08-14")
)
test_close = test.filter(pl.col("close") == 1)[["id"]]
test_close = test_close.with_columns(pl.Series("y", [0.0] * len(test_close)))
test = test.filter(pl.col("close") != 1)

# closeをカラムごと削除
train = train.drop("close")
test = test.drop("close")

# 2010年のデータは学習から削除。料金区分に関する情報が欠損している
train = train.filter(pl.col("datetime") >= date(2011, 1, 1))

# 目的変数を対数変換する
# 現状のコードだと、trainのyに0が含まれているとエラーになる。なので休業日関連の前処理を先に行う必要がある。
train = train.insert_column(3, train["y"].log().alias("y_ln"))

# 前処理後のデータ確認
print(train.head(5))
shape: (5, 7)
┌────────────┬────────────┬─────┬──────────┬────────┬──────────┬──────────┐
│ id         ┆ datetime   ┆ y   ┆ y_ln     ┆ client ┆ price_am ┆ price_pm │
│ ---        ┆ ---        ┆ --- ┆ ---      ┆ ---    ┆ ---      ┆ ---      │
│ str        ┆ date       ┆ i64 ┆ f64      ┆ i64    ┆ i64      ┆ i64      │
╞════════════╪════════════╪═════╪══════════╪════════╪══════════╪══════════╡
│ 2011-01-04 ┆ 2011-01-04 ┆ 16  ┆ 2.772589 ┆ 0      ┆ 0        ┆ 0        │
│ 2011-01-05 ┆ 2011-01-05 ┆ 16  ┆ 2.772589 ┆ 0      ┆ 0        ┆ 0        │
│ 2011-01-06 ┆ 2011-01-06 ┆ 13  ┆ 2.564949 ┆ 0      ┆ 0        ┆ 0        │
│ 2011-01-07 ┆ 2011-01-07 ┆ 14  ┆ 2.639057 ┆ 0      ┆ 0        ┆ 0        │
│ 2011-01-08 ┆ 2011-01-08 ┆ 16  ┆ 2.772589 ┆ 0      ┆ 0        ┆ 0        │
└────────────┴────────────┴─────┴──────────┴────────┴──────────┴──────────┘

休業日について。コンペへのデータ提出などの際は、最終的には以下の通り結合する

pl.concat(
    [submit, test_close],
    how = "vertical_relaxed"
).sort("id")

モデル構築¶

prophetによるベースライン作成、残差列追加¶

In [3]:
# 対数変換した目的変数y_lnをProphetで学習させる

# polarsのdatetime型をpandasのdatetime型に変換する必要がある
# 訓練データ
train_pandas = (
    train.select(["datetime", "y_ln"])
    .rename({"datetime": "ds", "y_ln": "y"})
    .to_pandas()
)
# テストデータ
test_pandas = test.select(["datetime"]).rename({"datetime": "ds"}).to_pandas()
# 学習
model = Prophet()
model.fit(train_pandas)

# 予測
forecast_train = model.predict(train_pandas)
forecast_test = model.predict(test_pandas)

# 予測結果の可視化
fig, ax = plt.subplots(
    figsize=(12, 5),
    constrained_layout=True,
)
ax.set_title(
    "Prophetによる予測(Train=青, Test=赤)(縦軸:y_ln、横軸:datetime)", fontsize=16
)

# train側は Prophet の標準描画(青・点群付き)
model.plot(model.predict(train_pandas), ax=ax)

# test側は後から重ね描き(赤)
ax.plot(
    forecast_test["ds"],
    forecast_test["yhat"],
    color="red",
    linewidth=2,
    label="Test yhat",
)
ax.fill_between(
    forecast_test["ds"],
    forecast_test["yhat_lower"],
    forecast_test["yhat_upper"],
    color="red",
    alpha=0.2,
    label="Test interval",
)

ax.legend()
plt.show()
10:23:22 - cmdstanpy - INFO - Chain [1] start processing
10:23:22 - cmdstanpy - INFO - Chain [1] done processing
No description has been provided for this image
In [4]:
# 推定値の算出
forecast_train_pl = pl.Series("y_ln_prophet", forecast_train["yhat"].values)
forecast_test_pl = pl.Series("y_ln_prophet", forecast_test["yhat"].values)

# 推定値をDataFrameにまとめる
train = train.insert_column(4, forecast_train_pl)
test = test.insert_column(3, forecast_test_pl)

# 残差列を追加
train = train.with_columns(
    (train["y_ln"] - train["y_ln_prophet"]).alias("y_ln_difference")
)

特徴量を追加¶

In [5]:
# 思いつく限りの特徴量を追加
# 今後の運用を考えると、任意の日時について自動でフラグを取得できる必要がある。要改善

# 祝日
holiday_list = []
for pair in jpholiday.between(date(2010, 1, 1), date(2017, 12, 31)):
    # print(pair)
    holiday_list.append(pair[0])

# その他の休日
# GW
# https://9rando.info/j-holiday/gw/2011/
gw_list = [
    date(2011, 4, 29),
    date(2011, 4, 30),
    date(2011, 5, 1),
    date(2011, 5, 2),  # 有給休暇の可能性
    date(2011, 5, 3),
    date(2011, 5, 4),
    date(2011, 5, 5),
    date(2012, 4, 28),
    date(2012, 4, 29),
    date(2012, 4, 30),
    date(2012, 5, 1),
    date(2012, 5, 2),  # 有休
    date(2012, 5, 3),
    date(2012, 5, 4),
    date(2012, 5, 5),
    date(2012, 5, 5),
    date(2013, 4, 27),
    date(2013, 4, 28),
    date(2013, 4, 29),
    date(2013, 4, 30),
    date(2013, 5, 1),
    date(2013, 5, 2),  # 有休
    date(2013, 5, 3),
    date(2013, 5, 4),
    date(2013, 5, 5),
    date(2013, 5, 5),
    date(2013, 5, 6),
    date(2014, 4, 29),
    date(2014, 4, 30),
    date(2014, 5, 1),
    date(2014, 5, 2),  # 有休
    date(2014, 5, 3),
    date(2014, 5, 4),
    date(2014, 5, 5),
    date(2014, 5, 5),
    date(2014, 5, 6),
    date(2015, 4, 29),
    date(2015, 4, 30),
    date(2015, 5, 1),  # 有休
    date(2015, 5, 2),
    date(2015, 5, 3),
    date(2015, 5, 4),
    date(2015, 5, 5),
    date(2015, 5, 5),
    date(2015, 5, 6),
    date(2016, 4, 29),
    date(2016, 4, 30),
    date(2016, 5, 1),
    date(2016, 5, 2),  # 有休
    date(2016, 5, 3),
    date(2016, 5, 4),
    date(2016, 5, 5),
    date(2017, 4, 29),
    date(2017, 4, 30),
    date(2017, 5, 1),
    date(2017, 5, 2),  # 有休
    date(2017, 5, 3),
    date(2017, 5, 4),
    date(2017, 5, 5),
    date(2017, 5, 6),
    date(2017, 5, 7),
]

# お盆
# https://9rando.info/j-holiday/obon/2011/
obon_list = [
    date(2011, 8, 13),
    date(2011, 8, 14),
    date(2011, 8, 15),
    date(2011, 8, 16),
    date(2012, 8, 13),
    date(2012, 8, 14),
    date(2012, 8, 15),
    date(2012, 8, 16),
    date(2013, 8, 13),
    date(2013, 8, 14),
    date(2013, 8, 15),
    date(2013, 8, 16),
    date(2014, 8, 13),
    date(2014, 8, 14),
    date(2014, 8, 15),
    date(2014, 8, 16),
    date(2015, 8, 13),
    date(2015, 8, 14),
    date(2015, 8, 15),
    date(2015, 8, 16),
    date(2016, 8, 13),
    date(2016, 8, 14),
    date(2016, 8, 15),
    date(2016, 8, 16),
    date(2017, 8, 13),
    date(2017, 8, 14),
    date(2017, 8, 15),
    date(2017, 8, 16),
]

# シルバーウィーク
# https://9rando.info/j-holiday/sw/2011/
sw_list = [
    date(2011, 9, 17),
    date(2011, 9, 18),
    date(2011, 9, 19),
    date(2011, 9, 20),
    date(2011, 9, 21),
    date(2011, 9, 22),  # 有休
    date(2011, 9, 23),
    date(2011, 9, 24),
    date(2011, 9, 25),
    date(2012, 9, 15),
    date(2012, 9, 16),
    date(2012, 9, 17),
    date(2012, 9, 18),
    date(2012, 9, 19),
    date(2012, 9, 20),
    date(2012, 9, 21),  # 有休
    date(2012, 9, 22),
    date(2012, 9, 23),
    date(2012, 9, 24),
    date(2013, 9, 14),
    date(2013, 9, 15),
    date(2013, 9, 16),
    date(2013, 9, 17),
    date(2013, 9, 18),
    date(2013, 9, 19),
    date(2013, 9, 20),  # 有休
    date(2013, 9, 21),
    date(2013, 9, 22),
    date(2013, 9, 23),
    date(2014, 9, 13),
    date(2014, 9, 14),
    date(2014, 9, 15),
    date(2014, 9, 16),
    date(2014, 9, 17),
    date(2014, 9, 18),
    date(2014, 9, 19),  # 有休
    date(2014, 9, 20),
    date(2014, 9, 21),
    date(2014, 9, 22),
    date(2015, 9, 19),
    date(2015, 9, 20),
    date(2015, 9, 21),
    date(2015, 9, 22),
    date(2015, 9, 23),
    date(2016, 9, 17),
    date(2016, 9, 18),
    date(2016, 9, 19),
    date(2016, 9, 20),
    date(2016, 9, 21),  # 有休
    date(2016, 9, 22),
    date(2017, 9, 16),
    date(2017, 9, 17),
    date(2017, 9, 18),
    date(2017, 9, 19),
    date(2017, 9, 20),
    date(2017, 9, 21),
    date(2017, 9, 22),  # 有休
    date(2017, 9, 23),
    date(2017, 9, 24),
]
In [6]:
def add_features(df):
    return (
        df
        # 日付の基本要素の追加
        .with_columns(
            [
                # 年、四半期、月
                pl.col("datetime").dt.year().alias("year"),
                pl.col("datetime").dt.quarter().alias("quarter"),
                pl.col("datetime").dt.month().alias("month"),
                # 年始から数えて何週目か
                pl.col("datetime").dt.week().alias("week"),
                # 年始から数えて何日目か
                pl.col("datetime").dt.ordinal_day().alias("ordinal_day"),
                # その月の何日か
                pl.col("datetime").dt.day().alias("day"),
                # その月の何週目か
                (pl.col("datetime").dt.day() // 7 + 1).alias("week_of_month"),
                # 曜日(月曜日が1、日曜日が6)
                pl.col("datetime").dt.weekday().alias("day_of_week"),
            ]
        )
        # 四半期はじめ・終わり、月はじめ・終わりのフラグ
        .with_columns(
            [
                pl.when((pl.col("month").is_in([1, 4, 7, 10])) & (pl.col("day") == 1))
                .then(1)
                .otherwise(0)
                .alias("quarter_start"),
                pl.when(
                    (pl.col("month").is_in([3, 6, 9, 12]))
                    & (pl.col("datetime") == pl.col("datetime").dt.month_end())
                )
                .then(1)
                .otherwise(0)
                .alias("quarter_end"),
                pl.when(pl.col("day") == 1).then(1).otherwise(0).alias("month_start"),
                pl.when(pl.col("datetime") == pl.col("datetime").dt.month_end())
                .then(1)
                .otherwise(0)
                .alias("month_end"),
            ]
        )
        # 祝日・休日・土日フラグ
        .with_columns(
            [
                pl.when(pl.col("datetime").is_in(holiday_list))
                .then(1)
                .otherwise(0)
                .alias("holiday"),
                pl.when(pl.col("datetime").is_in(gw_list))
                .then(1)
                .otherwise(0)
                .alias("GW"),
                pl.when(pl.col("datetime").is_in(sw_list))
                .then(1)
                .otherwise(0)
                .alias("SW"),
                pl.when(pl.col("datetime").is_in(obon_list))
                .then(1)
                .otherwise(0)
                .alias("obon"),
                pl.when(pl.col("datetime").dt.weekday() >= 6)
                .then(1)
                .otherwise(0)
                .alias("sat_sun"),
            ]
        )
        # 価格の和・差・積
        .with_columns(
            [
                (pl.col("price_am") + pl.col("price_pm")).alias("price_sum"),
                (pl.col("price_am") - pl.col("price_pm")).alias("price_difference"),
                # price = -1が欠損、price = 0は最安価格であった。price同士をそのまま乗算しまうと最安値のときに必ず積が0になってしまうのでその他の情報が消えてしまい、都合が良くない
                # price + 1とすることでこれを回避する。なお、欠損がある場合は積は必ず0となる。これはこれで良いフラグかもしれない?
                ((pl.col("price_am") + 1) * (pl.col("price_pm") + 1)).alias(
                    "price_product"
                ),
            ]
        )
    )


train = add_features(train)
test = add_features(test)

del holiday_list, gw_list, sw_list, obon_list

学習と予測¶

In [7]:
# 目的変数と特徴量
target_column = "y_ln_difference"
feature_columns = [
    "client",
    "price_am",
    "price_pm",
    "year",
    "quarter",
    "month",
    "week",
    "ordinal_day",
    "day",
    "week_of_month",
    "day_of_week",
    "quarter_start",
    "quarter_end",
    "month_start",
    "month_end",
    "holiday",
    "GW",
    "SW",
    "obon",
    "sat_sun",
    "price_sum",
    "price_difference",
    "price_product",
]
In [8]:
# 交差検証とパラメータチューニングをしながらモデル学習

import optuna
from sklearn.model_selection import TimeSeriesSplit, cross_val_score
from sklearn.ensemble import GradientBoostingRegressor
import numpy as np

# 機械学習ライブラリと親和性の高いPandasに変換する
train_pandas = train.to_pandas().set_index("id")
train_pandas = train_pandas.sort_values("datetime").reset_index(
    drop=True
)  # 念の為ここでソート


def year_split_positional(df, dt_col="datetime"):
    years = df[dt_col].dt.year.to_numpy()
    uniq = np.unique(years)
    for i in range(1, len(uniq)):
        train_idx = np.flatnonzero(years < uniq[i])  # ← 整数位置
        test_idx = np.flatnonzero(years == uniq[i])  # ← 整数位置
        yield train_idx, test_idx


X = train_pandas[feature_columns]
y = train_pandas[target_column]
cv_splits = list(year_split_positional(train_pandas, "datetime"))


# objective内でのスコアにはRMSEを使用中。コンペの評価指標(MAE)と合わせる方法は検討中
# 前処理で対数変換を行っていたり、prophetで捉えたベースラインとの差分を扱っていることを考慮する必要あり
# 両方にベースラインを加えて、指数関数でyを復元。その後改めてMAEを計算。といった流れ?
def objective(trial):
    params = {
        "n_estimators": trial.suggest_int("n_estimators", 100, 1200),
        "max_depth": trial.suggest_int("max_depth", 2, 10),
        "learning_rate": trial.suggest_float("learning_rate", 1e-3, 0.2, log=True),
        "subsample": trial.suggest_float("subsample", 0.5, 1.0),
        "min_samples_split": trial.suggest_int("min_samples_split", 2, 50),
        "min_samples_leaf": trial.suggest_int("min_samples_leaf", 1, 50),
        "random_state": 42,
    }
    model = GradientBoostingRegressor(**params)
    scores = cross_val_score(
        model,
        X,
        y,
        cv=cv_splits,
        scoring="neg_root_mean_squared_error",
        n_jobs=-1,
    )
    return -scores.mean()


study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=10, timeout=60)  # デバッグ用に短く設定中
best_params = study.best_params.copy()

print("Best params:", best_params)

# 最適パラメータで全訓練データを学習・予測
model = GradientBoostingRegressor(**best_params).fit(X, y)
y_pred = np.exp(model.predict(X) + train_pandas["y_ln_prophet"])
[I 2025-10-03 10:23:24,539] A new study created in memory with name: no-name-cf9368cc-a07d-4d0d-8a99-b89a0598900a
[I 2025-10-03 10:23:29,164] Trial 0 finished with value: 0.22512705385476153 and parameters: {'n_estimators': 355, 'max_depth': 3, 'learning_rate': 0.017284935293097388, 'subsample': 0.9868039704312895, 'min_samples_split': 30, 'min_samples_leaf': 26}. Best is trial 0 with value: 0.22512705385476153.
[I 2025-10-03 10:23:31,054] Trial 1 finished with value: 0.23772540096805556 and parameters: {'n_estimators': 132, 'max_depth': 3, 'learning_rate': 0.020283665268912298, 'subsample': 0.9739927619755564, 'min_samples_split': 22, 'min_samples_leaf': 46}. Best is trial 0 with value: 0.22512705385476153.
[I 2025-10-03 10:23:33,131] Trial 2 finished with value: 0.2380330915959096 and parameters: {'n_estimators': 687, 'max_depth': 8, 'learning_rate': 0.00254266381459453, 'subsample': 0.5852475385244971, 'min_samples_split': 50, 'min_samples_leaf': 33}. Best is trial 0 with value: 0.22512705385476153.
[I 2025-10-03 10:23:35,138] Trial 3 finished with value: 0.22572091871879357 and parameters: {'n_estimators': 755, 'max_depth': 8, 'learning_rate': 0.03385831413027628, 'subsample': 0.5088218919366347, 'min_samples_split': 20, 'min_samples_leaf': 48}. Best is trial 0 with value: 0.22512705385476153.
[I 2025-10-03 10:23:37,711] Trial 4 finished with value: 0.22125402801771604 and parameters: {'n_estimators': 520, 'max_depth': 9, 'learning_rate': 0.02442727519969622, 'subsample': 0.9129750337412155, 'min_samples_split': 16, 'min_samples_leaf': 26}. Best is trial 4 with value: 0.22125402801771604.
[I 2025-10-03 10:23:38,310] Trial 5 finished with value: 0.30745837025852996 and parameters: {'n_estimators': 207, 'max_depth': 5, 'learning_rate': 0.002017537717323288, 'subsample': 0.6389466692371684, 'min_samples_split': 3, 'min_samples_leaf': 33}. Best is trial 4 with value: 0.22125402801771604.
[I 2025-10-03 10:23:38,823] Trial 6 finished with value: 0.3128856169236336 and parameters: {'n_estimators': 170, 'max_depth': 8, 'learning_rate': 0.0023031748576365775, 'subsample': 0.6250019779515898, 'min_samples_split': 15, 'min_samples_leaf': 39}. Best is trial 4 with value: 0.22125402801771604.
[I 2025-10-03 10:23:39,720] Trial 7 finished with value: 0.2313852034374094 and parameters: {'n_estimators': 376, 'max_depth': 4, 'learning_rate': 0.004621032463099953, 'subsample': 0.5360109877340127, 'min_samples_split': 20, 'min_samples_leaf': 6}. Best is trial 4 with value: 0.22125402801771604.
[I 2025-10-03 10:23:40,766] Trial 8 finished with value: 0.23243863540314363 and parameters: {'n_estimators': 222, 'max_depth': 9, 'learning_rate': 0.005896062946971857, 'subsample': 0.8130376346240138, 'min_samples_split': 43, 'min_samples_leaf': 5}. Best is trial 4 with value: 0.22125402801771604.
[I 2025-10-03 10:23:42,864] Trial 9 finished with value: 0.2229792942614938 and parameters: {'n_estimators': 666, 'max_depth': 6, 'learning_rate': 0.0041189814804672075, 'subsample': 0.6303152256827693, 'min_samples_split': 9, 'min_samples_leaf': 22}. Best is trial 4 with value: 0.22125402801771604.
Best params: {'n_estimators': 520, 'max_depth': 9, 'learning_rate': 0.02442727519969622, 'subsample': 0.9129750337412155, 'min_samples_split': 16, 'min_samples_leaf': 26}
In [9]:
print(
    "訓練データ全体でのMAE:",
    np.round(mean_absolute_error(train_pandas["y"], y_pred), decimals=7),
)  # 小数第7位はコンペスコアの有効数字

fig, axes = plt.subplots(
    nrows=3,
    ncols=2,
    height_ratios=[1, 1, 1],
    width_ratios=[2, 1],
    figsize=(15, 12),
    constrained_layout=True,
)
fig.suptitle("モデル学習後の予測結果(元データ, 学習データ, 検証データ)", fontsize=16)

palette = sns.color_palette("tab10")

axes[0, 0].set_title("推移")
sns.lineplot(
    data=train_pandas, x="datetime", y="y", label="y", color=palette[0], ax=axes[0, 0]
)
sns.lineplot(
    data=train_pandas,
    x="datetime",
    y=y_pred,
    label="y_pred",
    color=palette[1],
    ax=axes[0, 0],
)

axes[0, 1].set_title("分布")
sns.histplot(
    data=train_pandas, x="y", binwidth=2, label="y", color=palette[0], ax=axes[0, 1]
)
sns.histplot(
    data=train_pandas,
    x=y_pred,
    binwidth=2,
    label="y_pred",
    color=palette[1],
    ax=axes[0, 1],
)

axes[1, 0].set_title("予測誤差")
axes[1, 0].set_ylabel("誤差")
sns.scatterplot(
    data=train_pandas,
    x="datetime",
    y=train_pandas["y"] - y_pred,
    marker="+",
    label="y_pred_err",
    color=palette[2],
    ax=axes[1, 0],
)

axes[1, 1].set_title("予測誤差の分布")
sns.histplot(
    data=train_pandas,
    x=train_pandas["y"] - y_pred,
    binwidth=2,
    color=palette[2],
    ax=axes[1, 1],
)

axes[2, 0].set_title("特徴量重要度")
sns.barplot(
    x=model.feature_importances_, y=feature_columns, color=palette[3], ax=axes[2, 0]
)

axes[2, 1].axis("off")

plt.show()
訓練データ全体でのMAE: 3.2550382
No description has been provided for this image
  • 精度
    • MAEで見た平均的な予測精度は良好。
    • ただ本モデルを実用することを考えると、繁忙期のピークへの追随が弱いのが気になる。これでは、実務上重要な「特に繁忙期にどれくらいの需要が来そうか」の予測精度が微妙か?
      • 改善のためには、学習の際の評価指標を変える?他に適切なものは?
  • 特徴量重要度について。特に強いもの
    • ordinal_day:年始から数えて何日か。つまり、一年の中での時期を表している
    • day:その月の中で、何日か
    • day_of_week:曜日
    • price_sum、price_product:午前・午後の価格の和・積

提出データ出力¶

In [10]:
# 提出前に、全ての訓練データを使って学習させておく
X, y = train_pandas[feature_columns], train_pandas[target_column]
model = GradientBoostingRegressor(random_state=1192).fit(X, y)

# 機械学習ライブラリと親和性の高いPandasに変換する
test_pandas = test.to_pandas().set_index("id")

# 説明変数の分割
X_test = test_pandas[feature_columns]

# 予測
y_pred_test = np.exp(model.predict(X_test) + test_pandas["y_ln_prophet"])
In [11]:
import ipynbname
from pathlib import Path

# 提出用DataFrame
submit = pl.DataFrame({"id": X_test.index.values, "y": y_pred_test.values})

# 退避していた休業日のデータを結合
submit = pl.concat([submit, test_close], how="vertical_relaxed").sort("id")

notebook_name = ipynbname.name()
output_path = Path("../data/output") / f"submit_{notebook_name}.csv"

# 提出ファイルを保存する
# submit.write_csv(output_path, include_header = False)
# print(f"Saved: {output_path}")
In [12]:
fig, axes = plt.subplots(
    nrows=1,
    ncols=2,
    height_ratios=[1],
    width_ratios=[2, 1],
    figsize=(12, 4),
    constrained_layout=True,
)
fig.suptitle("テストデータに対する予測")

ax = axes[0].set_title("推移")
sns.lineplot(data=train_pandas, x="datetime", y="y", ax=axes[0])
sns.lineplot(data=test_pandas, x="datetime", y=y_pred_test, ax=axes[0])

ax = axes[1].set_title("分布")
sns.histplot(data=train_pandas, x="y", binwidth=2, ax=axes[1])
sns.histplot(data=test_pandas, x=y_pred_test, binwidth=2, ax=axes[1])

plt.show()
No description has been provided for this image

参考サイト¶

  • 【SOTA】アップル 引越し需要予測
  • Tremendous1192様 SIGNATE SOTA アップル 引越し需要予測 備忘録

htmlに変換したものを出力¶

In [13]:
# import ipynbname
# from pathlib import Path

# output_dir = Path("html")

# !jupyter nbconvert --to html "{str(ipynbname.path())}" --output-dir "{output_dir}"