Probabilistic forecast flow


# import libraries
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import pandas as pd
# allow max rows to be displayed
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', 50)
# ignore warnings
import warnings
warnings.filterwarnings('ignore')
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
from catboost import CatBoostRegressor
from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet
from sklearn.model_selection import TimeSeriesSplit, KFold
import pandas as pd
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error, root_mean_squared_error
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.pipeline import Pipeline       
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestRegressor, HistGradientBoostingRegressor, AdaBoostRegressor, GradientBoostingRegressor
import numpy as np
from cubist import Cubist
from hyperopt import fmin, tpe, hp, Trials, STATUS_OK, space_eval
from hyperopt.pyll import scope
pd.set_option('display.max_rows', 150)
import pickle # for saving and loading models
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from statsmodels.tsa.seasonal import seasonal_decompose, MSTL
from sklearn.tree import DecisionTreeRegressor

from peshbeen.models import (ml_forecaster, ml_bidirect_forecaster, VARModel, MsHmmRegression, MsHmmVar)
from peshbeen.model_selection import (cross_validate,  mv_cross_validate,
                                      cv_tune, mv_cv_tune, prob_param_forecasts,
                                      tune_ets, tune_sarima, ParametricTimeSeriesSplit,
                                      forward_feature_selection, backward_feature_selection,
                                      mv_forward_feature_selection, mv_backward_feature_selection,
                                      hmm_forward_feature_selection, hmm_backward_feature_selection,
                                      hmm_mv_forward_feature_selection, hmm_mv_backward_feature_selection,
                                      hmm_cross_validate, hmm_mv_cross_validate, cv_lag_tune, 
                                      cv_hmm_lag_tune)
from peshbeen.statplots import (plot_ccf, plot_PACF_ACF)
from peshbeen.stattools import (unit_root_test, cross_autocorrelation,
                                lr_trend_model, forecast_trend, pacf_strength, ccf_strength)
from peshbeen.transformations import (fourier_terms, rolling_quantile,
                        rolling_mean, rolling_std, expanding_mean, expanding_std,
                        expanding_quantile, expanding_ets, box_cox_transform,
                        back_box_cox_transform,undiff_ts, seasonal_diff, invert_seasonal_diff,
                        nzInterval, zeroCumulative, kfold_target_encoder, target_encoder_for_test)
from peshbeen.metrics import (MAPE, MASE, MSE, MAE, RMSE, SMAPE, CFE, CFE_ABS, WMAPE, SRMSE, RMSSE, SMAE)
from peshbeen.prob_forecast import (ml_prob_forecasts, var_prob_forecasts, hmm_prob_forecasts, ets_prob_forecasts, arima_prob_forecasts, naive_prob_forecasts)
from sktime.transformations.series.boxcox import BoxCoxTransformer
sns.set_context("talk")
occup = pd.read_excel('data/occup_train_clean.xlsx', index_col=0)
occup["day_of_week"] = occup.index.day_name()
occup["month"] = occup.index.month_name()
cat_cols = ["day_of_week", "month", "is_holiday"]
cat_col_f = ["day_of_week", "is_holiday"]
hmm_params = {'blake': {'best_states': 2, 'best_lag': 2, 'best_k': 1},
 'mulberry': {'best_states': 2, 'best_lag': 3, 'best_k': 1},
 'juniper': {'best_states': 2, 'best_lag': 3, 'best_k': 1},
 'magnolia': {'best_states': 6, 'best_lag': 3, 'best_k': 2},
 'clare': {'best_states': 2, 'best_lag': 3, 'best_k': 1},
 'anderson': {'best_states': 2, 'best_lag': 3, 'best_k': 1},
 'other': {'best_states': 2, 'best_lag': 2, 'best_k': 2}}

def data_prep_f(ward, fourier_k):
    ward_train = occup[[ward, "time"]+cat_col_f]
    ft = fourier_terms(start_end_index=(ward_train.index.min(), ward_train.index.max()),
                period=365.25, num_terms=fourier_k)
    return ward_train.merge(ft, left_index=True, right_index=True, how="left")
ward_df= data_prep_f("blake", 1)

train = ward_df.iloc[:-3*84]
test = ward_df.iloc[-3*84:-2*84]
col = "blake"
hm_model_ = MsHmmRegression(n_components=2, target_col=col, cat_variables=cat_col_f, lags=2,
                            random_state=42, n_iter=300, tol=1e-2, ridge=0, verbose=False)
# fit_df = df_[:-360]
hm_model_.fit_em(train)
np.float64(360.66690918204046)
with open('tables/comb_opt_cdf.pkl', 'rb') as f:
    comb_opt_cdf = pickle.load(f)



comb_opt_cdf['total_shortage'] = comb_opt_cdf['total_shortage'].astype(int)

cutoff = 26
markers = ['o', 's', '^', 'D', 'v', 'P', 'X', '*', 'h', '<', '>']

# --------------------------------------------------
# 1. Dominance score: P(0 understaffed days)
# --------------------------------------------------
dominance_score = (
    comb_opt_cdf
    .groupby('Model')['total_shortage']
    .apply(lambda x: (x == 0).mean())
    .sort_values(ascending=False)
)
comb_opt_cdf
total_shortage Model CDF
0 0 AR-MSR 0.000794
1 0 AR-MSR 0.001587
2 0 AR-MSR 0.002381
3 0 AR-MSR 0.003175
4 0 AR-MSR 0.003968
... ... ... ...
1255 27 AR-MSR (Det-Best Point Forecast) 0.996825
1256 27 AR-MSR (Det-Best Point Forecast) 0.997619
1257 27 AR-MSR (Det-Best Point Forecast) 0.998413
1258 28 AR-MSR (Det-Best Point Forecast) 0.999206
1259 32 AR-MSR (Det-Best Point Forecast) 1.000000

12600 rows × 3 columns

g = comb_opt_cdf[comb_opt_cdf['Model'] == "AR-MSR"]
comb_opt_cdf
total_shortage Model CDF
0 0 AR-MSR 0.000794
1 0 AR-MSR 0.001587
2 0 AR-MSR 0.002381
3 0 AR-MSR 0.003175
4 0 AR-MSR 0.003968
... ... ... ...
1255 27 AR-MSR (Det-Best Point Forecast) 0.996825
1256 27 AR-MSR (Det-Best Point Forecast) 0.997619
1257 27 AR-MSR (Det-Best Point Forecast) 0.998413
1258 28 AR-MSR (Det-Best Point Forecast) 0.999206
1259 32 AR-MSR (Det-Best Point Forecast) 1.000000

12600 rows × 3 columns

with open('tables/comb_opt_cdf.pkl', 'rb') as f:
    comb_opt_cdf = pickle.load(f)

plt.figure(figsize=(27, 9))

comb_opt_cdf['total_shortage'] = comb_opt_cdf['total_shortage'].astype(int)

cutoff = 17
markers = ['o', 's', '^', 'D', 'v', 'P', 'X', '*', 'h', '<', '>']

# --------------------------------------------------
# 1. Dominance score: P(0 understaffed days)
# --------------------------------------------------
dominance_score = (
    comb_opt_cdf
    .groupby('Model')['total_shortage']
    .apply(lambda x: (x == 0).mean())
    .sort_values(ascending=False)
)[:-1]

# --------------------------------------------------
# 2. Fixed annotation layout (TOP 5)
# --------------------------------------------------
annot_models = dominance_score.index[:5]
y_positions = np.linspace(0.78, 0.62, len(annot_models))  # evenly spaced

# --------------------------------------------------
# 3. Plot in dominance order
# --------------------------------------------------
for i, model in enumerate(dominance_score.index):

    g = comb_opt_cdf[comb_opt_cdf['Model'] == model]

    x_full = np.arange(
        g['total_shortage'].min(),
        g['total_shortage'].max() + 1
    )

    counts = (
        g['total_shortage']
        .value_counts()
        .reindex(x_full, fill_value=0)
        .sort_index()
    )

    cdf_full = counts.cumsum() / counts.sum()

    mask = x_full <= cutoff
    x_plot = x_full[mask]
    cdf_plot = cdf_full[mask]

    plt.plot(
        x_plot,
        cdf_plot,
        marker=markers[i % len(markers)],
        linestyle='-',
        label=model,
        linewidth=1
    )
    plt.xticks(range(0, cutoff + 1, 1), fontsize=10)


## add vertical line for 0 x axis
plt.axvline(x=0, color='#BF505C', linestyle='--', linewidth=3)

# # --------------------------------------------------
# # 4. Add aligned annotations (axis coordinates)
# # --------------------------------------------------
# ax = plt.gca()

# for y, model in zip(y_positions, annot_models):
#     zero_prob = dominance_score[model]

#     ax.text(
#         0.02, y,                         # left margin in axes coords
#         f"{model}: {zero_prob:.2%}",
#         transform=ax.transAxes,
#         fontsize=24,
#         va='center',
#         ha='left'
#     )

# --------------------------------------------------
# 5. Formatting
# --------------------------------------------------
# plt.annotate(
#     'Zero-Shortage Reliability\nP(0 Understaffed Days):',
#     xy=(0.02, 0.28),
#     xytext=(-0.7, 0.86),
#     fontweight='bold',
#     fontsize=24
# )
plt.xlabel("Number of understaffed days", fontsize=30)
plt.xticks(fontsize=24)
plt.yticks(fontsize=24)
plt.ylabel("Probability of ≤ x Understaffed Days", fontsize=30)
plt.legend(title="Model", fontsize=24)
plt.grid(True)
plt.tight_layout()
plt.show()

comb_opt_cdf = comb_opt_cdf.iloc[[0, -1]]
comb_opt_cdf
total_shortage Model CDF
0 0 AR-MSR 0.000794
1259 32 AR-MSR (Det-Best Point Forecast) 1.000000
# # Save to file
# with open('exp_results/hm_model_.pkl', 'wb') as f:
#     pickle.dump(hm_model_, f)# 

# Load from file
with open('exp_results/hm_model_.pkl', 'rb') as f:
    hm_model_ = pickle.load(f)
point_forecasts = hm_model_.forecast(H=84, exog=test.drop(columns=[col]))
hmm_prob = hmm_prob_forecasts(model=hm_model_, n_calibration=360, H=84, sliding_window=1, n_iter=100, verbose=False)
hmm_prob.calibrate(train)
# # Save to file
# with open('exp_results/hmm_prob.pkl', 'wb') as f:
#     pickle.dump(hmm_prob, f)# 

# Load from file
with open('exp_results/hmm_prob.pkl', 'rb') as f:
    hmm_prob = pickle.load(f)
boost = hmm_prob.simulate_correlated_forecasts(train, samples=1000, future_exog=test.drop(columns=[col]))
prob_forecasts = boost.correlated_forecasts
## Generate point forecasts
## Generate probabilistic forecasts
from datetime import timedelta


scenarios_hmm = np.array(prob_forecasts)

## plot scenarios agains train and test
plt.figure(figsize=(12, 6))
plt.plot(train[col].index[-120:], train[col][-120:], label='Train', color='C0')
plt.plot(test.index, test[col], label='Actual', color='C2', linewidth=2, alpha=1)
plt.plot(test.index, point_forecasts, label='Point Forecast', color='C3')
# Define decision time (start of forecast horizon)
decision_time = test.index[0]+ timedelta(days=-1)  # first day of test/forecast period
# for i in range(scenarios_hmm.shape[0]-1):
#     plt.plot(test.index, scenarios_hmm[i], color='C7', alpha=0.1)
# plt.plot(test.index, scenarios_hmm[-1], color='C7', alpha=0.1, label ='All Scenarios (1000)')

# 1. Vertical dashed line
plt.axvline(decision_time, color='black', linestyle='--', linewidth=1.5)

# 2. "Decision time" label at top
plt.text(
    decision_time,
    plt.ylim()[1] * 0.98,
    "Decision time",
    ha='center',
    va='top',
    fontsize=12,
    fontweight='bold',
    color='black'
)

# 3. Explanation text box
plt.text(
    decision_time,
    plt.ylim()[1] * 0.90,
    "Decision:\nHow many nurses\nshould I schedule\nfor each day of next 42-84 days of forecasting horizon?",
    ha='center',
    va='top',
    fontsize=11,
    color='black',
    bbox=dict(facecolor='white', alpha=0.4, edgecolor='none')
)


# Choose a low y-level automatically (5% above bottom of plot)
hline_y = plt.ylim()[0] + 0.05 * (plt.ylim()[1] - plt.ylim()[0])

# Horizontal line spanning forecasting period
plt.hlines(
    y=hline_y,
    xmin=test.index[42],
    xmax=test.index[-1],
    color='C5',
    linestyle='--',
    linewidth=2,
    alpha=0.8
)

# Label next to the horizontal line
plt.text(
    test.index[62],
    hline_y,
    "Uncertainty in occupancy levels\nfor the next 42 days",
    ha='center',
    va='bottom',
    fontsize=11,
    color='black'
)


plt.ylabel('Occupancy')
plt.xlabel('Date')
plt.grid(True)
plt.legend(fontsize=12)
plt.show()

## Generate point forecasts
## Generate probabilistic forecasts
from datetime import timedelta


scenarios_hmm = np.array(prob_forecasts)

## plot scenarios agains train and test
plt.figure(figsize=(12, 6))
plt.plot(train[col].index[-120:], train[col][-120:], label='Train', color='C0')
plt.plot(test.index, test[col], label='Actual', color='C2', linewidth=2, alpha=1)
plt.plot(test.index, point_forecasts, label='Point Forecast', color='C3')
# Define decision time (start of forecast horizon)
decision_time = test.index[0]+ timedelta(days=-1)  # first day of test/forecast period
# for i in range(scenarios_hmm[0:3].shape[0]-1):
#     plt.plot(test.index, scenarios_hmm[i], color='C7', alpha=0.5)
plt.plot(test.index, scenarios_hmm[-1], color='C7', alpha=0.5, label ='All Scenarios (1000)')

# 1. Vertical dashed line
plt.axvline(decision_time, color='black', linestyle='--', linewidth=1.5)

# 2. "Decision time" label at top
plt.text(
    decision_time,
    plt.ylim()[1] * 0.98,
    "Decision time",
    ha='center',
    va='top',
    fontsize=12,
    fontweight='bold',
    color='black'
)

# 3. Explanation text box
plt.text(
    decision_time,
    plt.ylim()[1] * 0.90,
    "Decision:\nHow many nurses\nshould I schedule\nfor each day of next 42-84 days of forecasting horizon?",
    ha='center',
    va='top',
    fontsize=11,
    color='black',
    bbox=dict(facecolor='white', alpha=0.4, edgecolor='none')
)


# Choose a low y-level automatically (5% above bottom of plot)
hline_y = plt.ylim()[0] + 0.05 * (plt.ylim()[1] - plt.ylim()[0])

# Horizontal line spanning forecasting period
plt.hlines(
    y=hline_y,
    xmin=test.index[42],
    xmax=test.index[-1],
    color='C5',
    linestyle='--',
    linewidth=2,
    alpha=0.8
)

# Label next to the horizontal line
plt.text(
    test.index[62],
    hline_y,
    "Uncertainty in occupancy levels\nfor the next 42 days",
    ha='center',
    va='bottom',
    fontsize=11,
    color='black'
)


plt.ylabel('Occupancy')
plt.xlabel('Date')
plt.grid(True)
plt.legend(fontsize=12)
plt.show()

tables

from great_tables import *
f_metrics = pd.read_csv("tables/model_ward_sgain_forecasts.csv")
f_metrics.replace({"model": {"PermEntropy": ""}}, inplace=True)
f_metrics
model Ward A Ward B Ward C Ward D Ward E Ward F Ward G overall metric
0 AR-MSR 3.373633 4.947921 3.260366 3.910015 2.800352 3.782174 2.105290 3.454250 RMSE
1 LASSO 3.793185 5.341442 4.075063 2.318951 3.067318 3.685791 2.084345 3.480871 RMSE
2 LR 3.623391 5.664509 3.700904 2.199278 3.335767 3.983707 2.080061 3.512517 RMSE
3 RF 3.956461 5.071211 4.382106 2.639701 2.867912 4.090270 2.098558 3.586603 RMSE
4 ETS 3.698820 5.486072 3.888890 2.606520 3.602168 3.948612 2.048124 3.611315 RMSE
5 TimeGPT 4.095112 5.507926 3.779924 2.758942 3.407333 3.660007 2.513029 3.674610 RMSE
6 ARIMA 3.662656 5.729906 4.063827 2.463221 3.730140 3.975073 2.198374 3.689028 RMSE
7 NAIVE 4.260987 5.399300 3.892615 2.899999 3.448369 3.706992 2.580958 3.741317 RMSE
8 XGB 4.224786 5.253307 5.149105 2.571125 3.643394 3.943408 2.011702 3.828118 RMSE
9 LGB 3.984276 5.500140 4.400395 4.086323 3.289454 3.862070 1.999413 3.874582 RMSE
10 AR-MSR 1.045588 1.525511 0.964952 0.868996 0.871215 1.215376 0.600589 1.013175 Pinball
11 LR 1.012719 1.498275 0.963025 0.968045 0.845042 1.294412 0.611702 1.027603 Pinball
12 LASSO 1.136555 1.606141 1.058391 0.912206 0.874879 1.190900 0.610077 1.055593 Pinball
13 RF 1.249672 1.640816 1.221365 0.916781 0.865144 1.353165 0.637384 1.126332 Pinball
14 XGB 1.226932 1.677229 1.325663 0.813778 0.992039 1.348910 0.633346 1.145414 Pinball
15 ETS 1.245543 1.806563 1.107668 0.942603 1.091017 1.351288 0.644271 1.169851 Pinball
16 NAIVE 1.383129 1.746438 1.221968 0.965408 1.100163 1.128023 0.803762 1.192699 Pinball
17 ARIMA 1.163442 1.866146 1.277825 1.000289 1.187604 1.209706 0.668664 1.196239 Pinball
18 LGB 1.186391 1.833974 1.185235 1.414645 0.995072 1.304311 0.631901 1.221647 Pinball
19 TimeGPT 1.448103 1.955222 1.334803 0.976975 1.238214 1.294727 0.867477 1.302217 Pinball
20 0.760015 0.723240 0.712319 0.657460 0.613129 0.613726 0.563251 0.663306 PermEntropy
21 AR-MSR 0.115079 1.617460 0.422222 0.010317 0.404762 0.979365 0.162698 3.711905 STO
22 LR 0.214286 1.712698 0.512698 0.003968 0.238889 1.169841 0.155556 4.007937 STO
23 LASSO 0.429365 1.565873 0.938889 0.009524 0.160317 1.280159 0.158730 4.542857 STO
24 ARIMA 0.731746 1.737302 0.352381 0.015873 0.535714 1.250000 0.312698 4.935714 STO
25 ETS 0.270635 1.139683 0.681746 0.008730 1.371429 1.595238 0.158730 5.226190 STO
26 RF 0.442063 1.718254 1.230159 0.068254 0.469048 1.220635 0.144444 5.292857 STO
27 NAIVE 1.136508 1.582540 0.409524 0.108730 0.534921 1.212698 0.449206 5.434127 STO
28 XGB 0.595238 1.227778 1.391270 0.024603 0.743651 1.595238 0.153968 5.731746 STO
29 LGB 0.557143 1.619048 1.284921 0.013492 0.903968 1.595238 0.164286 6.138095 STO
30 AR-MSR-PointForecasts 1.165873 3.584127 1.342063 0.138889 1.079365 1.595238 0.153968 9.059524 STO
31 AR-MSR 833.571429 1333.650794 836.190476 657.698413 669.841270 822.857143 447.857143 5601.666667 VSS
32 LASSO 883.253968 1319.126984 931.825397 676.349206 615.555556 841.349206 446.825397 5714.285714 VSS
33 LR 849.047619 1340.476190 850.634921 739.603175 641.666667 870.476190 452.063492 5743.968253 VSS
34 ETS 858.968254 1261.428571 883.571429 650.079365 811.428571 878.571429 447.301587 5791.349206 VSS
35 RF 883.095238 1329.761905 987.777778 653.968254 675.793651 836.984127 453.333333 5820.714286 VSS
36 XGB 910.476190 1262.936508 1017.380952 633.253968 745.476190 878.571429 446.190476 5894.285714 VSS
37 ARIMA 932.698413 1398.968254 909.206349 699.841270 725.793651 845.158730 494.285715 6005.952382 VSS
38 NAIVE 1041.746032 1376.666667 903.174603 661.190476 730.317460 817.619048 555.396825 6086.111111 VSS
39 LGB 899.047619 1353.968254 1005.476190 766.428571 750.079365 878.571429 448.492063 6102.063492 VSS
40 AR-MSR-PointForecasts 1022.619048 1746.984127 1006.746032 613.095238 768.571429 878.571429 446.190476 6482.777778 VSS
f_metrics = pd.read_csv("tables/model_ward_sgain_forecasts.csv")
group_map = {
    "RMSE": "Point Forecast (RMSE)",
    "Pinball": "Probabilistic (Pinball Loss)",
    "PermEntropy": "Permutation Entropy",
    "STO": "Average Understaffed Number of Patients",
    "VSS": "Value of Stochastic Solution"
}

f_metrics_view = f_metrics.assign(metric=f_metrics["metric"].replace(group_map))

GT(f_metrics_view).tab_stub(groupname_col="metric").tab_style(
    style=style.fill(color="#FBFAF4"),
    locations=loc.body()
).tab_style(
    style=style.fill(color="#20808D"),
    locations=loc.column_header()
).tab_style(
    style=style.text(color="#FBFAF4", weight="bold"),
    locations=loc.column_labels()
).tab_style(
    # Targeting the row group labels specifically
    style=[
        style.fill(color="#20808D"), 
        style.text(color="#FBFAF4", weight="bold")
    ],
    locations=loc.row_groups()
).tab_style(
        style=style.text(size="38px", weight="bold"),
        locations=loc.column_labels()
    ).tab_style(
        style=style.text(size="36px"),
        locations=loc.body()
    )
model Ward A Ward B Ward C Ward D Ward E Ward F Ward G overall
Point Forecast (RMSE)
AR-MSR 3.3736325222806594 4.9479213593117475 3.2603664196995603 3.910014573851342 2.8003516710737717 3.78217417220872 2.1052900735969664 3.4542501131461094
LASSO 3.793185217596648 5.3414415585736315 4.075062540070708 2.318951228444537 3.0673181364467195 3.6857912371812858 2.0843451115078797 3.480870718545915
LR 3.6233911417590305 5.664509055807773 3.7009041794708417 2.199278262453863 3.335766512790936 3.983706623960853 2.0800614715029813 3.51251674967804
RF 3.956460859156175 5.071210553765478 4.382106481001265 2.639701270318182 2.867911956206467 4.090270065963361 2.09855798820284 3.586602739230538
ETS 3.698820423579899 5.486072037184399 3.8888902836483767 2.6065201189737772 3.602168307178825 3.948612055940692 2.0481244272801713 3.611315379112306
TimeGPT 4.095111878591425 5.507925562611942 3.779924129775609 2.758941878046337 3.407332852275019 3.660006772990124 2.5130292584559366 3.674610333249484
ARIMA 3.662655832476581 5.729905546461908 4.063826627378163 2.463220867810203 3.730139616106371 3.975072518826525 2.1983735929913717 3.689027800293018
NAIVE 4.260987040964574 5.399299752142806 3.8926151003546345 2.8999988369509304 3.4483687089421675 3.7069920924559177 2.5809578448760058 3.7413170538124336
XGB 4.22478572823843 5.253307070988415 5.14910494514314 2.5711249060260664 3.643394453862358 3.943407713572969 2.011702066389987 3.828118126317338
LGB 3.9842762875188433 5.500139533944931 4.4003954354861134 4.0863227950048095 3.28945411312878 3.862069763025887 1.9994134519250544 3.8745816257192023
Probabilistic (Pinball Loss)
AR-MSR 1.0455875579861142 1.5255110475719946 0.9649518069053143 0.8689964743838985 0.8712152071667197 1.2153756722484013 0.6005890560222211 1.0131752603263806
LR 1.0127193110566717 1.4982747879743534 0.9630251847086618 0.968044519050438 0.8450424718575041 1.2944118782428118 0.611701816919458 1.0276028528299856
LASSO 1.1365554539073948 1.60614119344363 1.058390926918535 0.9122055708463676 0.8748794130505652 1.190900129362254 0.6100767774966529 1.0555927807179144
RF 1.2496724479447865 1.640816120670067 1.221364718972858 0.9167805244756492 0.8651444806631923 1.3531649571401048 0.63738417749596 1.1263324896232312
XGB 1.226931581221036 1.6772289492956132 1.3256634397400446 0.813778291420804 0.9920388979708644 1.3489098199301053 0.6333462498260695 1.145413889914934
ETS 1.245543412227771 1.8065633587027037 1.107667894194592 0.9426031403408556 1.091016965316723 1.3512878351742563 0.6442709168543923 1.1698505032587565
NAIVE 1.3831290847885516 1.7464382527182014 1.2219682238106846 0.965408082750928 1.1001625417693714 1.1280228171112354 0.803762038793292 1.1926987202488948
ARIMA 1.1634417415669212 1.866146428689427 1.27782549302118 1.0002886569303275 1.187604198455248 1.2097061055732412 0.668663566465396 1.1962394558145346
LGB 1.1863909289942072 1.8339742459478732 1.1852349778567757 1.4146446159996908 0.9950716168133968 1.304311002239472 0.6319007856533069 1.221646881929246
TimeGPT 1.448102945413886 1.9552222822739664 1.3348032419839704 0.976974874457276 1.2382143427588694 1.294727288827358 0.8674771408778873 1.302217445227602
Permutation Entropy
PermEntropy 0.7600151178826429 0.723239948259235 0.7123187468675354 0.657460223717832 0.6131288328640623 0.6137260493073763 0.5632510909790247 0.6633057156968155
Average Understaffed Number of Patients
AR-MSR 0.115079365079365 1.6174603174603177 0.4222222222222222 0.0103174603174603 0.4047619047619047 0.9793650793650792 0.1626984126984127 3.711904761904762
LR 0.2142857142857142 1.7126984107051404 0.5126984126984127 0.0039682539682539 0.2388888888888889 1.16984126984127 0.1555555555555555 4.007936505943236
LASSO 0.4293650793650793 1.565873015873016 0.9388888888888888 0.0095238095238095 0.1603174603174603 1.28015873015873 0.1587301587301587 4.542857142857143
ARIMA 0.7317460317460317 1.7373015873015871 0.3523809523809524 0.0158730158730158 0.5357142857142857 1.25 0.3126984167550957 4.935714289770968
ETS 0.2706349206349206 1.1396825396825396 0.6817460317460318 0.0087301587301587 1.3714285714285714 1.5952380952380951 0.1587301587301587 5.226190476190476
RF 0.442063492063492 1.718253968253968 1.2301587301587302 0.0682539682539682 0.469047619047619 1.2206349206349207 0.1444444444444444 5.292857142857143
NAIVE 1.1365079365079365 1.5825396825396825 0.4095238095238095 0.1087301587301587 0.5349206349206349 1.2126984126984126 0.4492063492063492 5.434126984126984
XGB 0.5952380952380952 1.227777777777778 1.3912698412698412 0.0246031746031746 0.7436507936507937 1.5952380952380951 0.1539682539682539 5.731746031746032
LGB 0.5571428571428572 1.619047619047619 1.284920634920635 0.0134920634920634 0.903968253968254 1.5952380952380951 0.1642857142857142 6.1380952380952385
AR-MSR-PointForecasts 1.165873015873016 3.5841269841269843 1.342063492063492 0.1388888888888889 1.0793650793650793 1.5952380952380951 0.1539682539682539 9.05952380952381
Value of Stochastic Solution
AR-MSR 833.5714285714286 1333.6507936507935 836.1904761904761 657.6984126984127 669.8412698412699 822.8571428571429 447.8571428571428 5601.666666666667
LASSO 883.2539682539683 1319.126984126984 931.8253968253968 676.3492063492064 615.5555555555555 841.3492063492064 446.8253968253968 5714.285714285715
LR 849.047619047619 1340.4761899579398 850.6349206349206 739.6031746031746 641.6666666666666 870.4761904761905 452.0634920634921 5743.968253450003
ETS 858.968253968254 1261.428571428571 883.5714285714286 650.0793650793651 811.4285714285714 878.5714285714286 447.3015873015873 5791.349206349207
RF 883.0952380952381 1329.7619047619048 987.7777777777778 653.968253968254 675.7936507936508 836.984126984127 453.3333333333333 5820.714285714285
XGB 910.4761904761904 1262.936507936508 1017.3809523809524 633.2539682539683 745.4761904761905 878.5714285714286 446.1904761904762 5894.285714285715
ARIMA 932.6984126984128 1398.968253968254 909.2063492063492 699.8412698412699 725.7936507936508 845.1587301587301 494.285715299885 6005.952381966551
NAIVE 1041.7460317460318 1376.6666666666667 903.1746031746032 661.1904761904761 730.3174603174604 817.6190476190476 555.3968253968254 6086.111111111111
LGB 899.047619047619 1353.968253968254 1005.4761904761904 766.4285714285714 750.0793650793651 878.5714285714286 448.4920634920635 6102.063492063492
AR-MSR-PointForecasts 1022.6190476190476 1746.984126984127 1006.7460317460316 613.0952380952381 768.5714285714286 878.5714285714286 446.1904761904762 6482.777777777777