diff --git a/nbs/distributed.timegpt.ipynb b/nbs/distributed.timegpt.ipynb index af86a532..6cea6df4 100644 --- a/nbs/distributed.timegpt.ipynb +++ b/nbs/distributed.timegpt.ipynb @@ -168,7 +168,7 @@ " add_history: bool = False,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " num_partitions: Optional[int] = None,\n", " ) -> fugue.AnyDataFrame:\n", " kwargs = dict(\n", @@ -217,7 +217,7 @@ " validate_token: bool = False,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " num_partitions: Optional[int] = None,\n", " ) -> fugue.AnyDataFrame:\n", " kwargs = dict(\n", @@ -260,7 +260,7 @@ " validate_token: bool = False,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " n_windows: int = 1,\n", " step_size: Optional[int] = None,\n", " num_partitions: Optional[int] = None,\n", @@ -448,7 +448,7 @@ " num_partitions=1,\n", " id_col=id_col,\n", " time_col=time_col,\n", - " model='timegpt-1',\n", + " model='short-horizon',\n", " **fcst_kwargs\n", " )\n", " fcst_df = fa.as_pandas(fcst_df)\n", @@ -458,7 +458,7 @@ " num_partitions=1,\n", " id_col=id_col,\n", " time_col=time_col,\n", - " model='timegpt-1-long-horizon',\n", + " model='long-horizon',\n", " **fcst_kwargs\n", " )\n", " fcst_df_2 = fa.as_pandas(fcst_df_2)\n", @@ -557,7 +557,7 @@ "def test_forecast_dataframe(df: fugue.AnyDataFrame):\n", " test_cv_same_results_num_partitions(df, n_windows=2, step_size=1)\n", " test_cv_same_results_num_partitions(df, n_windows=3, step_size=None, horizon=1)\n", - " test_cv_same_results_num_partitions(df, model='timegpt-1-long-horizon', horizon=1)\n", + " test_cv_same_results_num_partitions(df, model='long-horizon', horizon=1)\n", " test_forecast_diff_results_diff_models(df)\n", " test_forecast(df, num_partitions=1)\n", " test_forecast(df, level=[90, 80], num_partitions=1)\n", @@ -771,7 +771,7 @@ " num_partitions=1,\n", " id_col=id_col,\n", " time_col=time_col,\n", - " model='timegpt-1',\n", + " model='short-horizon',\n", " **anomalies_kwargs\n", " )\n", " anomalies_df = fa.as_pandas(anomalies_df)\n", @@ -780,7 +780,7 @@ " num_partitions=1,\n", " id_col=id_col,\n", " time_col=time_col,\n", - " model='timegpt-1-long-horizon',\n", + " model='long-horizon',\n", " **anomalies_kwargs\n", " )\n", " anomalies_df_2 = fa.as_pandas(anomalies_df_2)\n", diff --git a/nbs/timegpt.ipynb b/nbs/timegpt.ipynb index 89c42045..3d7351a3 100644 --- a/nbs/timegpt.ipynb +++ b/nbs/timegpt.ipynb @@ -206,7 +206,7 @@ " clean_ex_first: bool = True,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " max_retries: int = 6,\n", " retry_interval: int = 10,\n", " max_wait_time: int = 6 * 60,\n", @@ -525,7 +525,7 @@ " \n", " def set_model_params(self):\n", " model_params = self._call_api(\n", - " self.client.timegpt_model_params,\n", + " self.client.model_params,\n", " SingleSeriesForecast(freq=self.freq, model=self.model),\n", " )\n", " model_params = model_params['detail']\n", @@ -589,7 +589,7 @@ " model=self.model,\n", " )\n", " response_timegpt = self._call_api(\n", - " self.client.timegpt_multi_series,\n", + " self.client.forecast_multi_series,\n", " payload,\n", " )\n", " if 'weights_x' in response_timegpt:\n", @@ -602,7 +602,7 @@ " main_logger.info('Calling Historical Forecast Endpoint...')\n", " self.validate_input_size(Y_df=Y_df)\n", " response_timegpt = self._call_api(\n", - " self.client.timegpt_multi_series_historic,\n", + " self.client.historic_forecast_multi_series,\n", " MultiSeriesInsampleForecast(\n", " y=y,\n", " x=x,\n", @@ -632,7 +632,7 @@ " main_logger.info('Calling Anomaly Detector Endpoint...')\n", " y, x = self.dataframes_to_dict(Y_df, X_df)\n", " response_timegpt = self._call_api(\n", - " self.client.timegpt_multi_series_anomalies,\n", + " self.client.anomaly_detection_multi_series,\n", " MultiSeriesAnomaly(\n", " y=y,\n", " x=x,\n", @@ -722,11 +722,21 @@ "#| exporti\n", "def validate_model_parameter(func):\n", " def wrapper(self, *args, **kwargs):\n", - " if 'model' in kwargs and kwargs['model'] not in self.supported_models:\n", - " raise ValueError(\n", - " f'unsupported model: {kwargs[\"model\"]} '\n", - " f'supported models: {\", \".join(self.supported_models)}'\n", - " )\n", + " if 'model' in kwargs:\n", + " model = kwargs['model']\n", + " rename_models_dict = {\n", + " 'timegpt-1': 'short-horizon',\n", + " 'timegpt-1-long-horizon': 'long-horizon',\n", + " }\n", + " if model in rename_models_dict.keys():\n", + " new_model = rename_models_dict[model]\n", + " warnings.warn(f\"'{model}' is deprecated; use '{new_model}' instead.\", FutureWarning)\n", + " model = new_model\n", + " if model not in self.supported_models:\n", + " raise ValueError(\n", + " f'unsupported model: {kwargs[\"model\"]} '\n", + " f'supported models: {\", \".join(self.supported_models)}'\n", + " )\n", " return func(self, *args, **kwargs)\n", " return wrapper" ] @@ -842,7 +852,7 @@ " self.max_retries = max_retries\n", " self.retry_interval = retry_interval\n", " self.max_wait_time = max_wait_time\n", - " self.supported_models = ['timegpt-1', 'timegpt-1-long-horizon']\n", + " self.supported_models = ['short-horizon', 'long-horizon'] \n", " # custom attr\n", " self.weights_x: pd.DataFrame = None\n", "\n", @@ -880,7 +890,7 @@ " add_history: bool = False,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " num_partitions: int = 1,\n", " ):\n", " if validate_token and not self.validate_token(log=False):\n", @@ -924,7 +934,7 @@ " validate_token: bool = False,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " num_partitions: int = 1,\n", " ):\n", " if validate_token and not self.validate_token(log=False):\n", @@ -971,7 +981,7 @@ " clean_ex_first: bool = True,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " num_partitions: int = 1,\n", " ):\n", " if validate_token and not self.validate_token(log=False):\n", @@ -1151,7 +1161,7 @@ " add_history: bool = False,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " num_partitions: Optional[int] = None,\n", " ):\n", " \"\"\"Forecast your time series using TimeGPT.\n", @@ -1214,9 +1224,9 @@ " Apply one-hot encoding to these date features.\n", " If `date_features=True`, then all date features are\n", " one-hot encoded by default.\n", - " model : str (default='timegpt=1')\n", - " Model to use as a string. Options are: `timegpt-1`, and `timegpt-1-long-horizon`. \n", - " We recommend using `timegpt-1-long-horizon` for forecasting \n", + " model : str (default='short-horizon')\n", + " Model to use as a string. Options are: `short-horizon`, and `long-horizon`. \n", + " We recommend using `long-horizon` for forecasting \n", " if you want to predict more than one seasonal \n", " period given the frequency of your data.\n", " num_partitions : int (default=None)\n", @@ -1286,7 +1296,7 @@ " validate_token: bool = False,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " num_partitions: Optional[int] = None,\n", " ):\n", " \"\"\"Detect anomalies in your time series using TimeGPT.\n", @@ -1331,9 +1341,9 @@ " Apply one-hot encoding to these date features.\n", " If `date_features=True`, then all date features are\n", " one-hot encoded by default.\n", - " model : str (default='timegpt=1')\n", - " Model to use as a string. Options are: `timegpt-1`, and `timegpt-1-long-horizon`. \n", - " We recommend using `timegpt-1-long-horizon` for forecasting \n", + " model : str (default='short-horizon')\n", + " Model to use as a string. Options are: `short-horizon`, and `long-horizon`. \n", + " We recommend using `long-horizon` for forecasting \n", " if you want to predict more than one seasonal \n", " period given the frequency of your data.\n", " num_partitions : int (default=None)\n", @@ -1398,7 +1408,7 @@ " clean_ex_first: bool = True,\n", " date_features: Union[bool, List[str]] = False,\n", " date_features_to_one_hot: Union[bool, List[str]] = True,\n", - " model: str = 'timegpt-1',\n", + " model: str = 'short-horizon',\n", " num_partitions: Optional[int] = None,\n", " ):\n", " \"\"\"Perform cross validation in your time series using TimeGPT.\n", @@ -1461,9 +1471,9 @@ " Apply one-hot encoding to these date features.\n", " If `date_features=True`, then all date features are\n", " one-hot encoded by default.\n", - " model : str (default='timegpt=1')\n", - " Model to use as a string. Options are: `timegpt-1`, and `timegpt-1-long-horizon`. \n", - " We recommend using `timegpt-1-long-horizon` for forecasting \n", + " model : str (default='short-horizon')\n", + " Model to use as a string. Options are: `short-horizon`, and `long-horizon`. \n", + " We recommend using `long-horizon` for forecasting \n", " if you want to predict more than one seasonal \n", " period given the frequency of your data.\n", " num_partitions : int (default=None)\n", @@ -1629,7 +1639,7 @@ "#| hide\n", "# test input_size\n", "test_eq(\n", - " timegpt.client.timegpt_model_params(request=SingleSeriesForecast(freq='D'))['data']['detail'],\n", + " timegpt.client.model_params(request=SingleSeriesForecast(freq='D'))['data']['detail'],\n", " {'input_size': 28, 'horizon': 7},\n", ")" ] @@ -1659,10 +1669,34 @@ "outputs": [], "source": [ "#| hide\n", - "# test finetune deprecation\n", + "# test model change\n", "df_test = df.copy()\n", "df_test.columns = [\"ds\", \"y\"]\n", "test_warns(\n", + " lambda: timegpt.forecast(df_test, finetune_steps=2, h=12, model=\"timegpt-1\"),\n", + ")\n", + "test_warns(\n", + " lambda: timegpt.forecast(df_test, finetune_steps=2, h=12, model=\"timegpt-1-long-horizon\"),\n", + ")\n", + "test_eq(\n", + " timegpt.forecast(df_test, h=12, model=\"timegpt-1\"),\n", + " timegpt.forecast(df_test, h=12),\n", + ")\n", + "test_eq(\n", + " timegpt.forecast(df_test, h=12, model=\"timegpt-1-long-horizon\"),\n", + " timegpt.forecast(df_test, h=12, model=\"long-horizon\"),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#| hide\n", + "# test finetune deprecation\n", + "test_warns(\n", " lambda: timegpt.forecast(df_test, finetune_steps=2, h=12),\n", ")\n", "test_warns(\n", @@ -1848,7 +1882,7 @@ " max_wait_time=max_wait_time,\n", " )\n", " init_time = time()\n", - " with patch('nixtlats.client.Nixtla.timegpt_multi_series', side_effect=side_effect):\n", + " with patch('nixtlats.client.Nixtla.forecast_multi_series', side_effect=side_effect):\n", " test_fail(\n", " lambda: mock_timegpt.forecast(df=df, h=12, time_col='timestamp', target_col='value'),\n", " )\n", @@ -2028,8 +2062,8 @@ " df=df_freq,\n", " h=h,\n", " )\n", - " fcst_1_df = test_equal_fcsts_add_history(**{**kwargs, 'model': 'timegpt-1'})\n", - " fcst_2_df = test_equal_fcsts_add_history(**{**kwargs, 'model': 'timegpt-1-long-horizon'})\n", + " fcst_1_df = test_equal_fcsts_add_history(**{**kwargs, 'model': 'short-horizon'})\n", + " fcst_2_df = test_equal_fcsts_add_history(**{**kwargs, 'model': 'long-horizon'})\n", " test_fail(\n", " lambda: pd.testing.assert_frame_equal(fcst_1_df, fcst_2_df),\n", " contains='(column name=\"TimeGPT\") are different',\n", @@ -2088,9 +2122,9 @@ "source": [ "#| hide\n", "# test different results for different models\n", - "fcst_kwargs['model'] = 'timegpt-1'\n", + "fcst_kwargs['model'] = 'short-horizon'\n", "fcst_timegpt_1 = timegpt.forecast(**fcst_kwargs)\n", - "fcst_kwargs['model'] = 'timegpt-1-long-horizon'\n", + "fcst_kwargs['model'] = 'long-horizon'\n", "fcst_timegpt_long = timegpt.forecast(**fcst_kwargs)\n", "test_fail(\n", " lambda: pd.testing.assert_frame_equal(fcst_timegpt_1[['TimeGPT']], fcst_timegpt_long[['TimeGPT']]),\n", @@ -2107,9 +2141,9 @@ "#| hide\n", "# test different results for different models\n", "# anomalies\n", - "anomalies_kwargs['model'] = 'timegpt-1'\n", + "anomalies_kwargs['model'] = 'short-horizon'\n", "anomalies_timegpt_1 = timegpt.detect_anomalies(**anomalies_kwargs)\n", - "anomalies_kwargs['model'] = 'timegpt-1-long-horizon'\n", + "anomalies_kwargs['model'] = 'long-horizon'\n", "anomalies_timegpt_long = timegpt.detect_anomalies(**anomalies_kwargs)\n", "test_fail(\n", " lambda: pd.testing.assert_frame_equal(anomalies_timegpt_1[['TimeGPT']], anomalies_timegpt_long[['TimeGPT']]),\n", @@ -2548,6 +2582,13 @@ "source": [ "show_doc(TimeGPT.detect_anomalies, title_level=2)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/nixtlats/distributed/timegpt.py b/nixtlats/distributed/timegpt.py index 8a9713f5..6f1eae44 100644 --- a/nixtlats/distributed/timegpt.py +++ b/nixtlats/distributed/timegpt.py @@ -133,7 +133,7 @@ def forecast( add_history: bool = False, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", num_partitions: Optional[int] = None, ) -> fugue.AnyDataFrame: kwargs = dict( @@ -182,7 +182,7 @@ def detect_anomalies( validate_token: bool = False, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", num_partitions: Optional[int] = None, ) -> fugue.AnyDataFrame: kwargs = dict( @@ -225,7 +225,7 @@ def cross_validation( validate_token: bool = False, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", n_windows: int = 1, step_size: Optional[int] = None, num_partitions: Optional[int] = None, diff --git a/nixtlats/timegpt.py b/nixtlats/timegpt.py index 656858df..60774f87 100644 --- a/nixtlats/timegpt.py +++ b/nixtlats/timegpt.py @@ -137,7 +137,7 @@ def __init__( clean_ex_first: bool = True, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", max_retries: int = 6, retry_interval: int = 10, max_wait_time: int = 6 * 60, @@ -485,7 +485,7 @@ def dataframes_to_dict(self, Y_df: pd.DataFrame, X_df: pd.DataFrame): def set_model_params(self): model_params = self._call_api( - self.client.timegpt_model_params, + self.client.model_params, SingleSeriesForecast(freq=self.freq, model=self.model), ) model_params = model_params["detail"] @@ -556,7 +556,7 @@ def forecast( model=self.model, ) response_timegpt = self._call_api( - self.client.timegpt_multi_series, + self.client.forecast_multi_series, payload, ) if "weights_x" in response_timegpt: @@ -571,7 +571,7 @@ def forecast( main_logger.info("Calling Historical Forecast Endpoint...") self.validate_input_size(Y_df=Y_df) response_timegpt = self._call_api( - self.client.timegpt_multi_series_historic, + self.client.historic_forecast_multi_series, MultiSeriesInsampleForecast( y=y, x=x, @@ -601,7 +601,7 @@ def detect_anomalies(self, df: pd.DataFrame): main_logger.info("Calling Anomaly Detector Endpoint...") y, x = self.dataframes_to_dict(Y_df, X_df) response_timegpt = self._call_api( - self.client.timegpt_multi_series_anomalies, + self.client.anomaly_detection_multi_series, MultiSeriesAnomaly( y=y, x=x, @@ -690,11 +690,24 @@ def cross_validation( # %% ../nbs/timegpt.ipynb 9 def validate_model_parameter(func): def wrapper(self, *args, **kwargs): - if "model" in kwargs and kwargs["model"] not in self.supported_models: - raise ValueError( - f'unsupported model: {kwargs["model"]} ' - f'supported models: {", ".join(self.supported_models)}' - ) + if "model" in kwargs: + model = kwargs["model"] + rename_models_dict = { + "timegpt-1": "short-horizon", + "timegpt-1-long-horizon": "long-horizon", + } + if model in rename_models_dict.keys(): + new_model = rename_models_dict[model] + warnings.warn( + f"'{model}' is deprecated; use '{new_model}' instead.", + FutureWarning, + ) + model = new_model + if model not in self.supported_models: + raise ValueError( + f'unsupported model: {kwargs["model"]} ' + f'supported models: {", ".join(self.supported_models)}' + ) return func(self, *args, **kwargs) return wrapper @@ -791,7 +804,7 @@ def __init__( self.max_retries = max_retries self.retry_interval = retry_interval self.max_wait_time = max_wait_time - self.supported_models = ["timegpt-1", "timegpt-1-long-horizon"] + self.supported_models = ["short-horizon", "long-horizon"] # custom attr self.weights_x: pd.DataFrame = None @@ -829,7 +842,7 @@ def _forecast( add_history: bool = False, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", num_partitions: int = 1, ): if validate_token and not self.validate_token(log=False): @@ -871,7 +884,7 @@ def _detect_anomalies( validate_token: bool = False, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", num_partitions: int = 1, ): if validate_token and not self.validate_token(log=False): @@ -916,7 +929,7 @@ def _cross_validation( clean_ex_first: bool = True, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", num_partitions: int = 1, ): if validate_token and not self.validate_token(log=False): @@ -1090,7 +1103,7 @@ def forecast( add_history: bool = False, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", num_partitions: Optional[int] = None, ): """Forecast your time series using TimeGPT. @@ -1153,9 +1166,9 @@ def forecast( Apply one-hot encoding to these date features. If `date_features=True`, then all date features are one-hot encoded by default. - model : str (default='timegpt=1') - Model to use as a string. Options are: `timegpt-1`, and `timegpt-1-long-horizon`. - We recommend using `timegpt-1-long-horizon` for forecasting + model : str (default='short-horizon') + Model to use as a string. Options are: `short-horizon`, and `long-horizon`. + We recommend using `long-horizon` for forecasting if you want to predict more than one seasonal period given the frequency of your data. num_partitions : int (default=None) @@ -1225,7 +1238,7 @@ def detect_anomalies( validate_token: bool = False, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", num_partitions: Optional[int] = None, ): """Detect anomalies in your time series using TimeGPT. @@ -1270,9 +1283,9 @@ def detect_anomalies( Apply one-hot encoding to these date features. If `date_features=True`, then all date features are one-hot encoded by default. - model : str (default='timegpt=1') - Model to use as a string. Options are: `timegpt-1`, and `timegpt-1-long-horizon`. - We recommend using `timegpt-1-long-horizon` for forecasting + model : str (default='short-horizon') + Model to use as a string. Options are: `short-horizon`, and `long-horizon`. + We recommend using `long-horizon` for forecasting if you want to predict more than one seasonal period given the frequency of your data. num_partitions : int (default=None) @@ -1337,7 +1350,7 @@ def cross_validation( clean_ex_first: bool = True, date_features: Union[bool, List[str]] = False, date_features_to_one_hot: Union[bool, List[str]] = True, - model: str = "timegpt-1", + model: str = "short-horizon", num_partitions: Optional[int] = None, ): """Perform cross validation in your time series using TimeGPT. @@ -1400,9 +1413,9 @@ def cross_validation( Apply one-hot encoding to these date features. If `date_features=True`, then all date features are one-hot encoded by default. - model : str (default='timegpt=1') - Model to use as a string. Options are: `timegpt-1`, and `timegpt-1-long-horizon`. - We recommend using `timegpt-1-long-horizon` for forecasting + model : str (default='short-horizon') + Model to use as a string. Options are: `short-horizon`, and `long-horizon`. + We recommend using `long-horizon` for forecasting if you want to predict more than one seasonal period given the frequency of your data. num_partitions : int (default=None)