diff --git a/nbs/core.ipynb b/nbs/core.ipynb index 36c0e75fc..ee0bed745 100644 --- a/nbs/core.ipynb +++ b/nbs/core.ipynb @@ -1678,12 +1678,12 @@ " raise AttributeError('Please rerun the `fit` method passing a valid prediction_interval setting to compute conformity scores')\n", " \n", " min_size = ufp.counts_by_id(df, id_col)['counts'].min()\n", - " min_samples = self.h * self.prediction_intervals.n_windows + 1\n", + " min_samples = self.h + self.prediction_intervals.n_windows * self.prediction_intervals.step_size\n", " if min_size < min_samples:\n", " raise ValueError(\n", " \"Minimum required samples in each serie for the prediction intervals \"\n", " f\"settings are: {min_samples}, shortest serie has: {min_size}. \"\n", - " \"Please reduce the number of windows, horizon or remove those series.\"\n", + " \"Please reduce the number of windows, horizon, step_size or remove those series.\"\n", " )\n", " \n", " self._add_level = True\n", @@ -1691,6 +1691,7 @@ " df=df,\n", " static_df=static_df,\n", " n_windows=self.prediction_intervals.n_windows,\n", + " step_size=self.prediction_intervals.step_size,\n", " id_col=id_col,\n", " time_col=time_col,\n", " target_col=target_col,\n", @@ -3676,6 +3677,30 @@ " 'LSTM', 'LSTM1', 'LSTM1-median', 'LSTM2_ql0.5', 'TSMixer', 'TSMixer1',\n", " 'TSMixer1-median', 'TSMixer2_ql0.5']" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e03292d2", + "metadata": {}, + "outputs": [], + "source": [ + "#| hide\n", + "# test conformal prediction with step_size argument\n", + "\n", + "models = []\n", + "for nf_model in [NHITS, RNN, TSMixer]:\n", + " params = {\"h\": 12, \"input_size\": 24, \"max_steps\": 1}\n", + " if nf_model.__name__ == \"TSMixer\":\n", + " params.update({\"n_series\": 2})\n", + " models.append(nf_model(**params))\n", + "\n", + "# Case 1: step_size = None\n", + "prediction_intervals = PredictionIntervals(method=\"conformal_error\", step_size=2)\n", + "nf = NeuralForecast(models=models, freq='M')\n", + "nf.fit(AirPassengersPanel_train, prediction_intervals=prediction_intervals)\n", + "preds = nf.predict(futr_df=AirPassengersPanel_test, level=[90])" + ] } ], "metadata": { diff --git a/nbs/utils.ipynb b/nbs/utils.ipynb index 4fe037298..c92469d62 100644 --- a/nbs/utils.ipynb +++ b/nbs/utils.ipynb @@ -577,6 +577,7 @@ " def __init__(\n", " self,\n", " n_windows: int = 2,\n", + " step_size: int = 1,\n", " method: str = \"conformal_distribution\",\n", " ):\n", " \"\"\" \n", @@ -585,6 +586,8 @@ " method : str, default is conformal_distribution\n", " One of the supported methods for the computation of prediction intervals:\n", " conformal_error or conformal_distribution\n", + " step_size : int, default is 1\n", + " Step size for the prediction intervals. \n", " \"\"\"\n", " if n_windows < 2:\n", " raise ValueError(\n", @@ -595,9 +598,10 @@ " raise ValueError(f\"method must be one of {allowed_methods}\")\n", " self.n_windows = n_windows\n", " self.method = method\n", + " self.step_size = step_size\n", "\n", " def __repr__(self):\n", - " return f\"PredictionIntervals(n_windows={self.n_windows}, method='{self.method}')\"" + " return f\"PredictionIntervals(n_windows={self.n_windows}, method='{self.method}', step_size={self.step_size})\"" ] }, { diff --git a/neuralforecast/core.py b/neuralforecast/core.py index 6e8fe07f8..07962c8fe 100644 --- a/neuralforecast/core.py +++ b/neuralforecast/core.py @@ -1688,12 +1688,15 @@ def _conformity_scores( ) min_size = ufp.counts_by_id(df, id_col)["counts"].min() - min_samples = self.h * self.prediction_intervals.n_windows + 1 + min_samples = ( + self.h + + self.prediction_intervals.n_windows * self.prediction_intervals.step_size + ) if min_size < min_samples: raise ValueError( "Minimum required samples in each serie for the prediction intervals " f"settings are: {min_samples}, shortest serie has: {min_size}. " - "Please reduce the number of windows, horizon or remove those series." + "Please reduce the number of windows, horizon, step_size or remove those series." ) self._add_level = True @@ -1701,6 +1704,7 @@ def _conformity_scores( df=df, static_df=static_df, n_windows=self.prediction_intervals.n_windows, + step_size=self.prediction_intervals.step_size, id_col=id_col, time_col=time_col, target_col=target_col, diff --git a/neuralforecast/utils.py b/neuralforecast/utils.py index 6e95cbcb2..7e826d671 100644 --- a/neuralforecast/utils.py +++ b/neuralforecast/utils.py @@ -457,6 +457,7 @@ class PredictionIntervals: def __init__( self, n_windows: int = 2, + step_size: int = 1, method: str = "conformal_distribution", ): """ @@ -465,6 +466,8 @@ def __init__( method : str, default is conformal_distribution One of the supported methods for the computation of prediction intervals: conformal_error or conformal_distribution + step_size : int, default is 1 + Step size for the prediction intervals. """ if n_windows < 2: raise ValueError( @@ -475,11 +478,10 @@ def __init__( raise ValueError(f"method must be one of {allowed_methods}") self.n_windows = n_windows self.method = method + self.step_size = step_size def __repr__(self): - return ( - f"PredictionIntervals(n_windows={self.n_windows}, method='{self.method}')" - ) + return f"PredictionIntervals(n_windows={self.n_windows}, method='{self.method}', step_size={self.step_size})" # %% ../nbs/utils.ipynb 32 def add_conformal_distribution_intervals(