We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Prophet not compatible with `Period`
## Description
TypeError is raised when using a PandasDataset with the ProphetPredictor.

The problem seems to come from the `pd.to_datetime` function which does not accept Period. I guess it broke when this was done: https://github.com/awslabs/gluon-ts/pull/1980

## To Reproduce
Using a simple PandasDataset than can be found [here](https://ts.gluon.ai/v0.10.x/tutorials/data_manipulation/pandasdataframes.html#Use-case-1---Loading-data-from-a-long-dataframe) we can reproduce the error easily:

```python
import pandas as pd
from gluonts.dataset.pandas import PandasDataset
from gluonts.model.prophet import ProphetPredictor

# Load Data
url = (
    "https://gist.githubusercontent.com/rsnirwan/a8b424085c9f44ef2598da74ce43e7a3"
    "/raw/b6fdef21fe1f654787fa0493846c546b7f9c4df2/ts_long.csv"
)
df = pd.read_csv(url, index_col=0, parse_dates=True)

# Create Dataset
ds = PandasDataset.from_long_dataframe(df, target="target", item_id="item_id")

# Init predictor
predictor = ProphetPredictor(prediction_length=1)

# Run forecast
next(predictor.predict(ds))
```

## Error message or code output
(Paste the complete error message, including stack trace, or the undesired output that the above snippet produces.)

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
----> 1 next(predictor.predict(ds))

~/.venv/lib/python3.7/site-packages/gluonts/model/prophet/_predictor.py in predict(self, dataset, num_samples, **kwargs)
    155             data = self._make_prophet_data_entry(entry)
    156 
--> 157             forecast_samples = self._run_prophet(data, params)
    158 
    159             yield SampleForecast(

~/.venv/lib/python3.7/site-packages/gluonts/model/prophet/_predictor.py in _run_prophet(self, data, params)
    174             prophet.add_regressor(feat_name(i))
    175 
--> 176         prophet.fit(data.prophet_training_data)
    177 
    178         future_df = prophet.make_future_dataframe(

~/.venv/lib/python3.7/site-packages/prophet/forecaster.py in fit(self, df, **kwargs)
   1112         if history.shape[0] < 2:
   1113             raise ValueError('Dataframe has less than 2 non-NaN rows.')
-> 1114         self.history_dates = pd.to_datetime(pd.Series(df['ds'].unique(), name='ds')).sort_values()
   1115 
   1116         history = self.setup_dataframe(history, initialize_scales=True)

~/.venv/lib/python3.7/site-packages/pandas/core/tools/datetimes.py in to_datetime(arg, errors, dayfirst, yearfirst, utc, format, exact, unit, infer_datetime_format, origin, cache)
    885             result = arg.map(cache_array)
    886         else:
--> 887             values = convert_listlike(arg._values, format)
    888             result = arg._constructor(values, index=arg.index, name=arg.name)
    889     elif isinstance(arg, (ABCDataFrame, abc.MutableMapping)):

~/.venv/lib/python3.7/site-packages/pandas/core/tools/datetimes.py in _convert_listlike_datetimes(arg, format, name, tz, unit, errors, infer_datetime_format, dayfirst, yearfirst, exact)
    364     orig_arg = arg
    365     try:
--> 366         arg, _ = maybe_convert_dtype(arg, copy=False)
    367     except TypeError:
    368         if errors == "coerce":

~/.venv/lib/python3.7/site-packages/pandas/core/arrays/datetimes.py in maybe_convert_dtype(data, copy)
   2260         #  test_setops.test_join_does_not_recur fails
   2261         raise TypeError(
-> 2262             "Passing PeriodDtype data is invalid. Use `data.to_timestamp()` instead"
   2263         )
   2264 

TypeError: Passing PeriodDtype data is invalid. Use `data.to_timestamp()` instead
```

## Environment
- Operating system: WSL2 - Ubuntu
- Python version: 3.7.12
- GluonTS version: 0.10.2
- MXNet version: 1.8.0.post0 (cu110)
- Prophet version: 1.1
- Pandas version: 1.3.5


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/gluonts/model/prophet/_predictor.py`
Content:
```
1 # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License").
4 # You may not use this file except in compliance with the License.
5 # A copy of the License is located at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # or in the "license" file accompanying this file. This file is distributed
10 # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11 # express or implied. See the License for the specific language governing
12 # permissions and limitations under the License.
13 
14 from typing import Callable, Dict, Iterator, List, NamedTuple, Optional
15 
16 import numpy as np
17 import pandas as pd
18 import toolz
19 
20 from gluonts.core.component import validated
21 from gluonts.dataset.common import DataEntry, Dataset
22 from gluonts.model.forecast import SampleForecast
23 from gluonts.model.predictor import RepresentablePredictor
24 
25 try:
26     from prophet import Prophet
27 except ImportError:
28     Prophet = None
29 
30 PROPHET_IS_INSTALLED = Prophet is not None
31 
32 USAGE_MESSAGE = """
33 Cannot import `prophet`.
34 
35 The `ProphetPredictor` is a thin wrapper for calling the `prophet` package.
36 In order to use it you need to install it using one of the following two
37 methods:
38 
39     # 1) install prophet directly
40     pip install prophet
41 
42     # 2) install gluonts with the Prophet extras
43     pip install gluonts[Prophet]
44 """
45 
46 
47 def feat_name(i: int) -> str:
48     """
49     The canonical name of a feature with index `i`.
50     """
51     return f"feat_dynamic_real_{i:03d}"
52 
53 
54 class ProphetDataEntry(NamedTuple):
55     """
56     A named tuple containing relevant base and derived data that is required in
57     order to call Prophet.
58     """
59 
60     train_length: int
61     prediction_length: int
62     start: pd.Period
63     target: np.ndarray
64     feat_dynamic_real: List[np.ndarray]
65 
66     @property
67     def prophet_training_data(self) -> pd.DataFrame:
68         return pd.DataFrame(
69             data={
70                 **{
71                     "ds": pd.period_range(
72                         start=self.start,
73                         periods=self.train_length,
74                         freq=self.start.freq,
75                     ),
76                     "y": self.target,
77                 },
78                 **{
79                     feat_name(i): feature[: self.train_length]
80                     for i, feature in enumerate(self.feat_dynamic_real)
81                 },
82             }
83         )
84 
85     @property
86     def forecast_start(self) -> pd.Period:
87         return self.start + self.train_length * self.start.freq
88 
89     @property
90     def freq(self):
91         return self.start.freq
92 
93 
94 class ProphetPredictor(RepresentablePredictor):
95     """
96     Wrapper around `Prophet <https://github.com/facebook/prophet>`_.
97 
98     The `ProphetPredictor` is a thin wrapper for calling the `prophet`
99     package. In order to use it you need to install the package::
100 
101         # you can either install Prophet directly
102         pip install prophet
103 
104         # or install gluonts with the Prophet extras
105         pip install gluonts[Prophet]
106 
107     Parameters
108     ----------
109     prediction_length
110         Number of time points to predict
111     prophet_params
112         Parameters to pass when instantiating the prophet model.
113     init_model
114         An optional function that will be called with the configured model.
115         This can be used to configure more complex setups, e.g.
116 
117         >>> def configure_model(model):
118         ...     model.add_seasonality(
119         ...         name='weekly', period=7, fourier_order=3, prior_scale=0.1
120         ...     )
121         ...     return model
122     """
123 
124     @validated()
125     def __init__(
126         self,
127         prediction_length: int,
128         prophet_params: Optional[Dict] = None,
129         init_model: Callable = toolz.identity,
130     ) -> None:
131         super().__init__(prediction_length=prediction_length)
132 
133         if not PROPHET_IS_INSTALLED:
134             raise ImportError(USAGE_MESSAGE)
135 
136         if prophet_params is None:
137             prophet_params = {}
138 
139         assert "uncertainty_samples" not in prophet_params, (
140             "Parameter 'uncertainty_samples' should not be set directly. "
141             "Please use 'num_samples' in the 'predict' method instead."
142         )
143 
144         self.prophet_params = prophet_params
145         self.init_model = init_model
146 
147     def predict(
148         self, dataset: Dataset, num_samples: int = 100, **kwargs
149     ) -> Iterator[SampleForecast]:
150 
151         params = self.prophet_params.copy()
152         params.update(uncertainty_samples=num_samples)
153 
154         for entry in dataset:
155             data = self._make_prophet_data_entry(entry)
156 
157             forecast_samples = self._run_prophet(data, params)
158 
159             yield SampleForecast(
160                 samples=forecast_samples,
161                 start_date=data.forecast_start,
162             )
163 
164     def _run_prophet(self, data: ProphetDataEntry, params: dict) -> np.ndarray:
165         """
166         Construct and run a :class:`Prophet` model on the given
167         :class:`ProphetDataEntry` and return the resulting array of samples.
168         """
169 
170         prophet = self.init_model(Prophet(**params))
171 
172         # Register dynamic features as regressors to the model
173         for i in range(len(data.feat_dynamic_real)):
174             prophet.add_regressor(feat_name(i))
175 
176         prophet.fit(data.prophet_training_data)
177 
178         future_df = prophet.make_future_dataframe(
179             periods=self.prediction_length,
180             freq=data.freq,
181             include_history=False,
182         )
183 
184         # Add dynamic features in the prediction range
185         for i, feature in enumerate(data.feat_dynamic_real):
186             future_df[feat_name(i)] = feature[data.train_length :]
187 
188         prophet_result = prophet.predictive_samples(future_df)
189 
190         return prophet_result["yhat"].T
191 
192     def _make_prophet_data_entry(self, entry: DataEntry) -> ProphetDataEntry:
193         """
194         Construct a :class:`ProphetDataEntry` from a regular
195         :class:`DataEntry`.
196         """
197 
198         train_length = len(entry["target"])
199         prediction_length = self.prediction_length
200         start = entry["start"]
201         target = entry["target"]
202         feat_dynamic_real = entry.get("feat_dynamic_real", [])
203 
204         # make sure each dynamic feature has the desired length
205         for i, feature in enumerate(feat_dynamic_real):
206             assert len(feature) == train_length + prediction_length, (
207                 f"Length mismatch for dynamic real-valued feature #{i}: "
208                 f"expected {train_length + prediction_length}, "
209                 f"got {len(feature)}"
210             )
211 
212         return ProphetDataEntry(
213             train_length=train_length,
214             prediction_length=prediction_length,
215             start=start,
216             target=target,
217             feat_dynamic_real=feat_dynamic_real,
218         )
219
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/gluonts/model/prophet/_predictor.py b/src/gluonts/model/prophet/_predictor.py
--- a/src/gluonts/model/prophet/_predictor.py
+++ b/src/gluonts/model/prophet/_predictor.py
@@ -72,7 +72,7 @@
                         start=self.start,
                         periods=self.train_length,
                         freq=self.start.freq,
-                    ),
+                    ).to_timestamp(),
                     "y": self.target,
                 },
                 **{

{"golden_diff": "diff --git a/src/gluonts/model/prophet/_predictor.py b/src/gluonts/model/prophet/_predictor.py\n--- a/src/gluonts/model/prophet/_predictor.py\n+++ b/src/gluonts/model/prophet/_predictor.py\n@@ -72,7 +72,7 @@\n                         start=self.start,\n                         periods=self.train_length,\n                         freq=self.start.freq,\n-                    ),\n+                    ).to_timestamp(),\n                     \"y\": self.target,\n                 },\n                 **{\n", "issue": "Prophet not compatible with `Period`\n## Description\r\nTypeError is raised when using a PandasDataset with the ProphetPredictor.\r\n\r\nThe problem seems to come from the `pd.to_datetime` function which does not accept Period. I guess it broke when this was done: https://github.com/awslabs/gluon-ts/pull/1980\r\n\r\n## To Reproduce\r\nUsing a simple PandasDataset than can be found [here](https://ts.gluon.ai/v0.10.x/tutorials/data_manipulation/pandasdataframes.html#Use-case-1---Loading-data-from-a-long-dataframe) we can reproduce the error easily:\r\n\r\n```python\r\nimport pandas as pd\r\nfrom gluonts.dataset.pandas import PandasDataset\r\nfrom gluonts.model.prophet import ProphetPredictor\r\n\r\n# Load Data\r\nurl = (\r\n    \"https://gist.githubusercontent.com/rsnirwan/a8b424085c9f44ef2598da74ce43e7a3\"\r\n    \"/raw/b6fdef21fe1f654787fa0493846c546b7f9c4df2/ts_long.csv\"\r\n)\r\ndf = pd.read_csv(url, index_col=0, parse_dates=True)\r\n\r\n# Create Dataset\r\nds = PandasDataset.from_long_dataframe(df, target=\"target\", item_id=\"item_id\")\r\n\r\n# Init predictor\r\npredictor = ProphetPredictor(prediction_length=1)\r\n\r\n# Run forecast\r\nnext(predictor.predict(ds))\r\n```\r\n\r\n## Error message or code output\r\n(Paste the complete error message, including stack trace, or the undesired output that the above snippet produces.)\r\n\r\n```\r\n---------------------------------------------------------------------------\r\nTypeError                                 Traceback (most recent call last)\r\n----> 1 next(predictor.predict(ds))\r\n\r\n~/.venv/lib/python3.7/site-packages/gluonts/model/prophet/_predictor.py in predict(self, dataset, num_samples, **kwargs)\r\n    155             data = self._make_prophet_data_entry(entry)\r\n    156 \r\n--> 157             forecast_samples = self._run_prophet(data, params)\r\n    158 \r\n    159             yield SampleForecast(\r\n\r\n~/.venv/lib/python3.7/site-packages/gluonts/model/prophet/_predictor.py in _run_prophet(self, data, params)\r\n    174             prophet.add_regressor(feat_name(i))\r\n    175 \r\n--> 176         prophet.fit(data.prophet_training_data)\r\n    177 \r\n    178         future_df = prophet.make_future_dataframe(\r\n\r\n~/.venv/lib/python3.7/site-packages/prophet/forecaster.py in fit(self, df, **kwargs)\r\n   1112         if history.shape[0] < 2:\r\n   1113             raise ValueError('Dataframe has less than 2 non-NaN rows.')\r\n-> 1114         self.history_dates = pd.to_datetime(pd.Series(df['ds'].unique(), name='ds')).sort_values()\r\n   1115 \r\n   1116         history = self.setup_dataframe(history, initialize_scales=True)\r\n\r\n~/.venv/lib/python3.7/site-packages/pandas/core/tools/datetimes.py in to_datetime(arg, errors, dayfirst, yearfirst, utc, format, exact, unit, infer_datetime_format, origin, cache)\r\n    885             result = arg.map(cache_array)\r\n    886         else:\r\n--> 887             values = convert_listlike(arg._values, format)\r\n    888             result = arg._constructor(values, index=arg.index, name=arg.name)\r\n    889     elif isinstance(arg, (ABCDataFrame, abc.MutableMapping)):\r\n\r\n~/.venv/lib/python3.7/site-packages/pandas/core/tools/datetimes.py in _convert_listlike_datetimes(arg, format, name, tz, unit, errors, infer_datetime_format, dayfirst, yearfirst, exact)\r\n    364     orig_arg = arg\r\n    365     try:\r\n--> 366         arg, _ = maybe_convert_dtype(arg, copy=False)\r\n    367     except TypeError:\r\n    368         if errors == \"coerce\":\r\n\r\n~/.venv/lib/python3.7/site-packages/pandas/core/arrays/datetimes.py in maybe_convert_dtype(data, copy)\r\n   2260         #  test_setops.test_join_does_not_recur fails\r\n   2261         raise TypeError(\r\n-> 2262             \"Passing PeriodDtype data is invalid. Use `data.to_timestamp()` instead\"\r\n   2263         )\r\n   2264 \r\n\r\nTypeError: Passing PeriodDtype data is invalid. Use `data.to_timestamp()` instead\r\n```\r\n\r\n## Environment\r\n- Operating system: WSL2 - Ubuntu\r\n- Python version: 3.7.12\r\n- GluonTS version: 0.10.2\r\n- MXNet version: 1.8.0.post0 (cu110)\r\n- Prophet version: 1.1\r\n- Pandas version: 1.3.5\r\n\n", "before_files": [{"content": "# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\").\n# You may not use this file except in compliance with the License.\n# A copy of the License is located at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# or in the \"license\" file accompanying this file. This file is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n# express or implied. See the License for the specific language governing\n# permissions and limitations under the License.\n\nfrom typing import Callable, Dict, Iterator, List, NamedTuple, Optional\n\nimport numpy as np\nimport pandas as pd\nimport toolz\n\nfrom gluonts.core.component import validated\nfrom gluonts.dataset.common import DataEntry, Dataset\nfrom gluonts.model.forecast import SampleForecast\nfrom gluonts.model.predictor import RepresentablePredictor\n\ntry:\n    from prophet import Prophet\nexcept ImportError:\n    Prophet = None\n\nPROPHET_IS_INSTALLED = Prophet is not None\n\nUSAGE_MESSAGE = \"\"\"\nCannot import `prophet`.\n\nThe `ProphetPredictor` is a thin wrapper for calling the `prophet` package.\nIn order to use it you need to install it using one of the following two\nmethods:\n\n    # 1) install prophet directly\n    pip install prophet\n\n    # 2) install gluonts with the Prophet extras\n    pip install gluonts[Prophet]\n\"\"\"\n\n\ndef feat_name(i: int) -> str:\n    \"\"\"\n    The canonical name of a feature with index `i`.\n    \"\"\"\n    return f\"feat_dynamic_real_{i:03d}\"\n\n\nclass ProphetDataEntry(NamedTuple):\n    \"\"\"\n    A named tuple containing relevant base and derived data that is required in\n    order to call Prophet.\n    \"\"\"\n\n    train_length: int\n    prediction_length: int\n    start: pd.Period\n    target: np.ndarray\n    feat_dynamic_real: List[np.ndarray]\n\n    @property\n    def prophet_training_data(self) -> pd.DataFrame:\n        return pd.DataFrame(\n            data={\n                **{\n                    \"ds\": pd.period_range(\n                        start=self.start,\n                        periods=self.train_length,\n                        freq=self.start.freq,\n                    ),\n                    \"y\": self.target,\n                },\n                **{\n                    feat_name(i): feature[: self.train_length]\n                    for i, feature in enumerate(self.feat_dynamic_real)\n                },\n            }\n        )\n\n    @property\n    def forecast_start(self) -> pd.Period:\n        return self.start + self.train_length * self.start.freq\n\n    @property\n    def freq(self):\n        return self.start.freq\n\n\nclass ProphetPredictor(RepresentablePredictor):\n    \"\"\"\n    Wrapper around `Prophet <https://github.com/facebook/prophet>`_.\n\n    The `ProphetPredictor` is a thin wrapper for calling the `prophet`\n    package. In order to use it you need to install the package::\n\n        # you can either install Prophet directly\n        pip install prophet\n\n        # or install gluonts with the Prophet extras\n        pip install gluonts[Prophet]\n\n    Parameters\n    ----------\n    prediction_length\n        Number of time points to predict\n    prophet_params\n        Parameters to pass when instantiating the prophet model.\n    init_model\n        An optional function that will be called with the configured model.\n        This can be used to configure more complex setups, e.g.\n\n        >>> def configure_model(model):\n        ...     model.add_seasonality(\n        ...         name='weekly', period=7, fourier_order=3, prior_scale=0.1\n        ...     )\n        ...     return model\n    \"\"\"\n\n    @validated()\n    def __init__(\n        self,\n        prediction_length: int,\n        prophet_params: Optional[Dict] = None,\n        init_model: Callable = toolz.identity,\n    ) -> None:\n        super().__init__(prediction_length=prediction_length)\n\n        if not PROPHET_IS_INSTALLED:\n            raise ImportError(USAGE_MESSAGE)\n\n        if prophet_params is None:\n            prophet_params = {}\n\n        assert \"uncertainty_samples\" not in prophet_params, (\n            \"Parameter 'uncertainty_samples' should not be set directly. \"\n            \"Please use 'num_samples' in the 'predict' method instead.\"\n        )\n\n        self.prophet_params = prophet_params\n        self.init_model = init_model\n\n    def predict(\n        self, dataset: Dataset, num_samples: int = 100, **kwargs\n    ) -> Iterator[SampleForecast]:\n\n        params = self.prophet_params.copy()\n        params.update(uncertainty_samples=num_samples)\n\n        for entry in dataset:\n            data = self._make_prophet_data_entry(entry)\n\n            forecast_samples = self._run_prophet(data, params)\n\n            yield SampleForecast(\n                samples=forecast_samples,\n                start_date=data.forecast_start,\n            )\n\n    def _run_prophet(self, data: ProphetDataEntry, params: dict) -> np.ndarray:\n        \"\"\"\n        Construct and run a :class:`Prophet` model on the given\n        :class:`ProphetDataEntry` and return the resulting array of samples.\n        \"\"\"\n\n        prophet = self.init_model(Prophet(**params))\n\n        # Register dynamic features as regressors to the model\n        for i in range(len(data.feat_dynamic_real)):\n            prophet.add_regressor(feat_name(i))\n\n        prophet.fit(data.prophet_training_data)\n\n        future_df = prophet.make_future_dataframe(\n            periods=self.prediction_length,\n            freq=data.freq,\n            include_history=False,\n        )\n\n        # Add dynamic features in the prediction range\n        for i, feature in enumerate(data.feat_dynamic_real):\n            future_df[feat_name(i)] = feature[data.train_length :]\n\n        prophet_result = prophet.predictive_samples(future_df)\n\n        return prophet_result[\"yhat\"].T\n\n    def _make_prophet_data_entry(self, entry: DataEntry) -> ProphetDataEntry:\n        \"\"\"\n        Construct a :class:`ProphetDataEntry` from a regular\n        :class:`DataEntry`.\n        \"\"\"\n\n        train_length = len(entry[\"target\"])\n        prediction_length = self.prediction_length\n        start = entry[\"start\"]\n        target = entry[\"target\"]\n        feat_dynamic_real = entry.get(\"feat_dynamic_real\", [])\n\n        # make sure each dynamic feature has the desired length\n        for i, feature in enumerate(feat_dynamic_real):\n            assert len(feature) == train_length + prediction_length, (\n                f\"Length mismatch for dynamic real-valued feature #{i}: \"\n                f\"expected {train_length + prediction_length}, \"\n                f\"got {len(feature)}\"\n            )\n\n        return ProphetDataEntry(\n            train_length=train_length,\n            prediction_length=prediction_length,\n            start=start,\n            target=target,\n            feat_dynamic_real=feat_dynamic_real,\n        )\n", "path": "src/gluonts/model/prophet/_predictor.py"}], "after_files": [{"content": "# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\").\n# You may not use this file except in compliance with the License.\n# A copy of the License is located at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# or in the \"license\" file accompanying this file. This file is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n# express or implied. See the License for the specific language governing\n# permissions and limitations under the License.\n\nfrom typing import Callable, Dict, Iterator, List, NamedTuple, Optional\n\nimport numpy as np\nimport pandas as pd\nimport toolz\n\nfrom gluonts.core.component import validated\nfrom gluonts.dataset.common import DataEntry, Dataset\nfrom gluonts.model.forecast import SampleForecast\nfrom gluonts.model.predictor import RepresentablePredictor\n\ntry:\n    from prophet import Prophet\nexcept ImportError:\n    Prophet = None\n\nPROPHET_IS_INSTALLED = Prophet is not None\n\nUSAGE_MESSAGE = \"\"\"\nCannot import `prophet`.\n\nThe `ProphetPredictor` is a thin wrapper for calling the `prophet` package.\nIn order to use it you need to install it using one of the following two\nmethods:\n\n    # 1) install prophet directly\n    pip install prophet\n\n    # 2) install gluonts with the Prophet extras\n    pip install gluonts[Prophet]\n\"\"\"\n\n\ndef feat_name(i: int) -> str:\n    \"\"\"\n    The canonical name of a feature with index `i`.\n    \"\"\"\n    return f\"feat_dynamic_real_{i:03d}\"\n\n\nclass ProphetDataEntry(NamedTuple):\n    \"\"\"\n    A named tuple containing relevant base and derived data that is required in\n    order to call Prophet.\n    \"\"\"\n\n    train_length: int\n    prediction_length: int\n    start: pd.Period\n    target: np.ndarray\n    feat_dynamic_real: List[np.ndarray]\n\n    @property\n    def prophet_training_data(self) -> pd.DataFrame:\n        return pd.DataFrame(\n            data={\n                **{\n                    \"ds\": pd.period_range(\n                        start=self.start,\n                        periods=self.train_length,\n                        freq=self.start.freq,\n                    ).to_timestamp(),\n                    \"y\": self.target,\n                },\n                **{\n                    feat_name(i): feature[: self.train_length]\n                    for i, feature in enumerate(self.feat_dynamic_real)\n                },\n            }\n        )\n\n    @property\n    def forecast_start(self) -> pd.Period:\n        return self.start + self.train_length * self.start.freq\n\n    @property\n    def freq(self):\n        return self.start.freq\n\n\nclass ProphetPredictor(RepresentablePredictor):\n    \"\"\"\n    Wrapper around `Prophet <https://github.com/facebook/prophet>`_.\n\n    The `ProphetPredictor` is a thin wrapper for calling the `prophet`\n    package. In order to use it you need to install the package::\n\n        # you can either install Prophet directly\n        pip install prophet\n\n        # or install gluonts with the Prophet extras\n        pip install gluonts[Prophet]\n\n    Parameters\n    ----------\n    prediction_length\n        Number of time points to predict\n    prophet_params\n        Parameters to pass when instantiating the prophet model.\n    init_model\n        An optional function that will be called with the configured model.\n        This can be used to configure more complex setups, e.g.\n\n        >>> def configure_model(model):\n        ...     model.add_seasonality(\n        ...         name='weekly', period=7, fourier_order=3, prior_scale=0.1\n        ...     )\n        ...     return model\n    \"\"\"\n\n    @validated()\n    def __init__(\n        self,\n        prediction_length: int,\n        prophet_params: Optional[Dict] = None,\n        init_model: Callable = toolz.identity,\n    ) -> None:\n        super().__init__(prediction_length=prediction_length)\n\n        if not PROPHET_IS_INSTALLED:\n            raise ImportError(USAGE_MESSAGE)\n\n        if prophet_params is None:\n            prophet_params = {}\n\n        assert \"uncertainty_samples\" not in prophet_params, (\n            \"Parameter 'uncertainty_samples' should not be set directly. \"\n            \"Please use 'num_samples' in the 'predict' method instead.\"\n        )\n\n        self.prophet_params = prophet_params\n        self.init_model = init_model\n\n    def predict(\n        self, dataset: Dataset, num_samples: int = 100, **kwargs\n    ) -> Iterator[SampleForecast]:\n\n        params = self.prophet_params.copy()\n        params.update(uncertainty_samples=num_samples)\n\n        for entry in dataset:\n            data = self._make_prophet_data_entry(entry)\n\n            forecast_samples = self._run_prophet(data, params)\n\n            yield SampleForecast(\n                samples=forecast_samples,\n                start_date=data.forecast_start,\n            )\n\n    def _run_prophet(self, data: ProphetDataEntry, params: dict) -> np.ndarray:\n        \"\"\"\n        Construct and run a :class:`Prophet` model on the given\n        :class:`ProphetDataEntry` and return the resulting array of samples.\n        \"\"\"\n\n        prophet = self.init_model(Prophet(**params))\n\n        # Register dynamic features as regressors to the model\n        for i in range(len(data.feat_dynamic_real)):\n            prophet.add_regressor(feat_name(i))\n\n        prophet.fit(data.prophet_training_data)\n\n        future_df = prophet.make_future_dataframe(\n            periods=self.prediction_length,\n            freq=data.freq,\n            include_history=False,\n        )\n\n        # Add dynamic features in the prediction range\n        for i, feature in enumerate(data.feat_dynamic_real):\n            future_df[feat_name(i)] = feature[data.train_length :]\n\n        prophet_result = prophet.predictive_samples(future_df)\n\n        return prophet_result[\"yhat\"].T\n\n    def _make_prophet_data_entry(self, entry: DataEntry) -> ProphetDataEntry:\n        \"\"\"\n        Construct a :class:`ProphetDataEntry` from a regular\n        :class:`DataEntry`.\n        \"\"\"\n\n        train_length = len(entry[\"target\"])\n        prediction_length = self.prediction_length\n        start = entry[\"start\"]\n        target = entry[\"target\"]\n        feat_dynamic_real = entry.get(\"feat_dynamic_real\", [])\n\n        # make sure each dynamic feature has the desired length\n        for i, feature in enumerate(feat_dynamic_real):\n            assert len(feature) == train_length + prediction_length, (\n                f\"Length mismatch for dynamic real-valued feature #{i}: \"\n                f\"expected {train_length + prediction_length}, \"\n                f\"got {len(feature)}\"\n            )\n\n        return ProphetDataEntry(\n            train_length=train_length,\n            prediction_length=prediction_length,\n            start=start,\n            target=target,\n            feat_dynamic_real=feat_dynamic_real,\n        )\n", "path": "src/gluonts/model/prophet/_predictor.py"}]}