We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] ThetaForecaster fails to give prediction intevals for pd.DataFrames
**Describe the bug**
@fkiraly , yesterday you mentioned that pd.Series should not appear in the code. I feel that this is somehow connected.

I noticed that `ThetaForecaster` is unable to generate the prediction intervals when it's inside a `ForecastingPipeline`. It turns out that the problem is not with the pipeline but with the input being forced to be a `pd.DataFrame`.
The error i'm getting is:
```python
ValueError: Unable to coerce to Series, length must be 1: given 36
```

The problem is connected with the addition of `y_pred` with `sem` in  `_predict_quantiles`, in theta.py.
I'd offer a work around but I'm sure a temporal change of data type is not the best solution;-).

Btw, is it expected that `sem` is a pd.Float64Index instance?

Here's a toy example:
```python
pd.DataFrame([1,2])+pd.Float64Index([.1,.2])
```
<!--


-->

**To Reproduce**
<!--
Add a Minimal, Complete, and Verifiable example (for more details, see e.g. https://stackoverflow.com/help/mcve

If the code is too long, feel free to put it in a public gist and link it in the issue: https://gist.github.com
-->

```python
import pandas as pd
from sktime.forecasting.theta import ThetaForecaster
from sktime.forecasting.model_selection import temporal_train_test_split
from sktime.datasets import load_airline
y = load_airline()
y_train, y_test = temporal_train_test_split(y)

fp = ThetaForecaster(sp=12)
fp.fit(pd.DataFrame(y_train))
fp.predict_quantiles(fh=y_test.index)
```

**Expected behavior**
<!--
A clear and concise description of what you expected to happen.
-->
```python
import pandas as pd
from sktime.forecasting.theta import ThetaForecaster
from sktime.forecasting.model_selection import temporal_train_test_split
from sktime.datasets import load_airline
y = load_airline()
y_train, y_test = temporal_train_test_split(y)

fp = ThetaForecaster(sp=12)
fp.fit(y_train)
fp.predict_quantiles(fh=y_test.index)
```
**Additional context**
<!--
Add any other context about the problem here.
-->

**Versions**
<details>

<!--
Please run the following code snippet and paste the output here:
 
from sktime import show_versions; show_versions()
-->

System:
    python: 3.8.13 | packaged by conda-forge | (default, Mar 25 2022, 05:59:45) [MSC v.1929 64 bit (AMD64)]
executable: C:\Users\Mi\miniconda3\envs\sktime-dev\python.exe
   machine: Windows-10-10.0.22000-SP0

Python dependencies:
          pip: 22.0.4
   setuptools: 62.1.0
      sklearn: 1.0.2
       sktime: 0.11.2
  statsmodels: 0.13.2
        numpy: 1.22.3
        scipy: 1.8.0
       pandas: 1.4.2
   matplotlib: 3.5.1
       joblib: 1.1.0
        numba: 0.53.0
     pmdarima: 1.8.5
      tsfresh: None

</details>

<!-- Thanks for contributing! -->


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sktime/forecasting/theta.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 """Theta forecaster."""
4 
5 __all__ = ["ThetaForecaster"]
6 __author__ = ["big-o", "mloning", "kejsitake", "fkiraly"]
7 
8 from warnings import warn
9 
10 import numpy as np
11 import pandas as pd
12 from scipy.stats import norm
13 
14 from sktime.forecasting.exp_smoothing import ExponentialSmoothing
15 from sktime.transformations.series.detrend import Deseasonalizer
16 from sktime.utils.slope_and_trend import _fit_trend
17 from sktime.utils.validation.forecasting import check_sp
18 
19 
20 class ThetaForecaster(ExponentialSmoothing):
21     """Theta method for forecasting.
22 
23     The theta method as defined in [1]_ is equivalent to simple exponential
24     smoothing (SES) with drift (as demonstrated in [2]_).
25 
26     The series is tested for seasonality using the test outlined in A&N. If
27     deemed seasonal, the series is seasonally adjusted using a classical
28     multiplicative decomposition before applying the theta method. The
29     resulting forecasts are then reseasonalised.
30 
31     In cases where SES results in a constant forecast, the theta forecaster
32     will revert to predicting the SES constant plus a linear trend derived
33     from the training data.
34 
35     Prediction intervals are computed using the underlying state space model.
36 
37     Parameters
38     ----------
39     initial_level : float, optional
40         The alpha value of the simple exponential smoothing, if the value is
41         set then
42         this will be used, otherwise it will be estimated from the data.
43     deseasonalize : bool, optional (default=True)
44         If True, data is seasonally adjusted.
45     sp : int, optional (default=1)
46         The number of observations that constitute a seasonal period for a
47         multiplicative deseasonaliser, which is used if seasonality is
48         detected in the
49         training data. Ignored if a deseasonaliser transformer is provided.
50         Default is
51         1 (no seasonality).
52 
53     Attributes
54     ----------
55     initial_level_ : float
56         The estimated alpha value of the SES fit.
57     drift_ : float
58         The estimated drift of the fitted model.
59     se_ : float
60         The standard error of the predictions. Used to calculate prediction
61         intervals.
62 
63     References
64     ----------
65     .. [1] Assimakopoulos, V. and Nikolopoulos, K. The theta model: a
66        decomposition approach to forecasting. International Journal of
67        Forecasting 16, 521-530, 2000.
68        https://www.sciencedirect.com/science/article/pii/S0169207000000662
69 
70     .. [2] `Hyndman, Rob J., and Billah, Baki. Unmasking the Theta method.
71        International J. Forecasting, 19, 287-290, 2003.
72        https://www.sciencedirect.com/science/article/pii/S0169207001001431
73 
74     Examples
75     --------
76     >>> from sktime.datasets import load_airline
77     >>> from sktime.forecasting.theta import ThetaForecaster
78     >>> y = load_airline()
79     >>> forecaster = ThetaForecaster(sp=12)
80     >>> forecaster.fit(y)
81     ThetaForecaster(...)
82     >>> y_pred = forecaster.predict(fh=[1,2,3])
83     """
84 
85     _fitted_param_names = ("initial_level", "smoothing_level")
86     _tags = {
87         "scitype:y": "univariate",
88         "ignores-exogeneous-X": True,
89         "capability:pred_int": True,
90         "requires-fh-in-fit": False,
91         "handles-missing-data": False,
92     }
93 
94     def __init__(self, initial_level=None, deseasonalize=True, sp=1):
95 
96         self.sp = sp
97         self.deseasonalize = deseasonalize
98         self.deseasonalizer_ = None
99         self.trend_ = None
100         self.initial_level_ = None
101         self.drift_ = None
102         self.se_ = None
103         super(ThetaForecaster, self).__init__(initial_level=initial_level, sp=sp)
104 
105     def _fit(self, y, X=None, fh=None):
106         """Fit to training data.
107 
108         Parameters
109         ----------
110         y : pd.Series
111             Target time series to which to fit the forecaster.
112         fh : int, list or np.array, optional (default=None)
113             The forecasters horizon with the steps ahead to to predict.
114         X : pd.DataFrame, optional (default=None)
115             Exogenous variables are ignored
116 
117         Returns
118         -------
119         self : returns an instance of self.
120         """
121         sp = check_sp(self.sp)
122         if sp > 1 and not self.deseasonalize:
123             warn("`sp` is ignored when `deseasonalise`=False")
124 
125         if self.deseasonalize:
126             self.deseasonalizer_ = Deseasonalizer(sp=self.sp, model="multiplicative")
127             y = self.deseasonalizer_.fit_transform(y)
128 
129         self.initialization_method = "known" if self.initial_level else "estimated"
130         # fit exponential smoothing forecaster
131         # find theta lines: Theta lines are just SES + drift
132         super(ThetaForecaster, self)._fit(y, fh=fh)
133         self.initial_level_ = self._fitted_forecaster.params["smoothing_level"]
134 
135         # compute and store historical residual standard error
136         self.sigma_ = np.sqrt(self._fitted_forecaster.sse / (len(y) - 1))
137 
138         # compute trend
139         self.trend_ = self._compute_trend(y)
140 
141         return self
142 
143     def _predict(self, fh, X=None):
144         """Make forecasts.
145 
146         Parameters
147         ----------
148         fh : array-like
149             The forecasters horizon with the steps ahead to to predict.
150             Default is
151             one-step ahead forecast, i.e. np.array([1]).
152 
153         Returns
154         -------
155         y_pred : pandas.Series
156             Returns series of predicted values.
157         """
158         y_pred = super(ThetaForecaster, self)._predict(fh, X)
159 
160         # Add drift.
161         drift = self._compute_drift()
162         y_pred += drift
163 
164         if self.deseasonalize:
165             y_pred = self.deseasonalizer_.inverse_transform(y_pred)
166 
167         return y_pred
168 
169     @staticmethod
170     def _compute_trend(y):
171         # Trend calculated through least squares regression.
172         coefs = _fit_trend(y.values.reshape(1, -1), order=1)
173         return coefs[0, 0] / 2
174 
175     def _compute_drift(self):
176         fh = self.fh.to_relative(self.cutoff)
177         if np.isclose(self.initial_level_, 0.0):
178             # SES was constant, so revert to simple trend
179             drift = self.trend_ * fh
180         else:
181             # Calculate drift from SES parameters
182             n_timepoints = len(self._y)
183             drift = self.trend_ * (
184                 fh
185                 + (1 - (1 - self.initial_level_) ** n_timepoints) / self.initial_level_
186             )
187 
188         return drift
189 
190     def _predict_quantiles(self, fh, X=None, alpha=None):
191         """Compute/return prediction quantiles for a forecast.
192 
193         private _predict_quantiles containing the core logic,
194             called from predict_quantiles and predict_interval
195 
196         Parameters
197         ----------
198         fh : int, list, np.array or ForecastingHorizon
199             Forecasting horizon
200         X : pd.DataFrame, optional (default=None)
201             Exogenous time series
202         alpha : list of float, optional (default=[0.5])
203             A list of probabilities at which quantile forecasts are computed.
204 
205         Returns
206         -------
207         quantiles : pd.DataFrame
208             Column has multi-index: first level is variable name from y in fit,
209                 second level being the values of alpha passed to the function.
210             Row index is fh. Entries are quantile forecasts, for var in col index,
211                 at quantile probability in second col index, for the row index.
212         """
213         # prepare return data frame
214         index = pd.MultiIndex.from_product([["Quantiles"], alpha])
215         pred_quantiles = pd.DataFrame(columns=index)
216 
217         sem = self.sigma_ * np.sqrt(
218             self.fh.to_relative(self.cutoff) * self.initial_level_ ** 2 + 1
219         )
220 
221         y_pred = super(ThetaForecaster, self).predict(fh, X)
222 
223         # we assume normal additive noise with sem variance
224         for a in alpha:
225             pred_quantiles[("Quantiles", a)] = y_pred + norm.ppf(a) * sem
226         # todo: should this not increase with the horizon?
227         # i.e., sth like norm.ppf(a) * sem * fh.to_absolute(cutoff) ?
228         # I've just refactored this so will leave it for now
229 
230         return pred_quantiles
231 
232     def _update(self, y, X=None, update_params=True):
233         super(ThetaForecaster, self)._update(
234             y, X, update_params=False
235         )  # use custom update_params routine
236         if update_params:
237             if self.deseasonalize:
238                 y = self.deseasonalizer_.transform(self._y)  # use updated y
239             self.initial_level_ = self._fitted_forecaster.params["smoothing_level"]
240             self.trend_ = self._compute_trend(y)
241         return self
242 
243 
244 def _zscore(level: float, two_tailed: bool = True) -> float:
245     """Calculate a z-score from a confidence level.
246 
247     Parameters
248     ----------
249     level : float
250         A confidence level, in the open interval (0, 1).
251     two_tailed : bool (default=True)
252         If True, return the two-tailed z score.
253 
254     Returns
255     -------
256     z : float
257         The z score.
258     """
259     alpha = 1 - level
260     if two_tailed:
261         alpha /= 2
262     return -norm.ppf(alpha)
263
```
--- 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/sktime/forecasting/theta.py b/sktime/forecasting/theta.py
--- a/sktime/forecasting/theta.py
+++ b/sktime/forecasting/theta.py
@@ -149,6 +149,8 @@
             The forecasters horizon with the steps ahead to to predict.
             Default is
             one-step ahead forecast, i.e. np.array([1]).
+        X : pd.DataFrame, optional (default=None)
+            Exogenous time series
 
         Returns
         -------
@@ -215,10 +217,10 @@
         pred_quantiles = pd.DataFrame(columns=index)
 
         sem = self.sigma_ * np.sqrt(
-            self.fh.to_relative(self.cutoff) * self.initial_level_ ** 2 + 1
+            self.fh.to_relative(self.cutoff) * self.initial_level_**2 + 1
         )
 
-        y_pred = super(ThetaForecaster, self).predict(fh, X)
+        y_pred = super(ThetaForecaster, self)._predict(fh, X)
 
         # we assume normal additive noise with sem variance
         for a in alpha:

{"golden_diff": "diff --git a/sktime/forecasting/theta.py b/sktime/forecasting/theta.py\n--- a/sktime/forecasting/theta.py\n+++ b/sktime/forecasting/theta.py\n@@ -149,6 +149,8 @@\n             The forecasters horizon with the steps ahead to to predict.\n             Default is\n             one-step ahead forecast, i.e. np.array([1]).\n+        X : pd.DataFrame, optional (default=None)\n+            Exogenous time series\n \n         Returns\n         -------\n@@ -215,10 +217,10 @@\n         pred_quantiles = pd.DataFrame(columns=index)\n \n         sem = self.sigma_ * np.sqrt(\n-            self.fh.to_relative(self.cutoff) * self.initial_level_ ** 2 + 1\n+            self.fh.to_relative(self.cutoff) * self.initial_level_**2 + 1\n         )\n \n-        y_pred = super(ThetaForecaster, self).predict(fh, X)\n+        y_pred = super(ThetaForecaster, self)._predict(fh, X)\n \n         # we assume normal additive noise with sem variance\n         for a in alpha:\n", "issue": "[BUG] ThetaForecaster fails to give prediction intevals for pd.DataFrames\n**Describe the bug**\r\n@fkiraly , yesterday you mentioned that pd.Series should not appear in the code. I feel that this is somehow connected.\r\n\r\nI noticed that `ThetaForecaster` is unable to generate the prediction intervals when it's inside a `ForecastingPipeline`. It turns out that the problem is not with the pipeline but with the input being forced to be a `pd.DataFrame`.\r\nThe error i'm getting is:\r\n```python\r\nValueError: Unable to coerce to Series, length must be 1: given 36\r\n```\r\n\r\nThe problem is connected with the addition of `y_pred` with `sem` in  `_predict_quantiles`, in theta.py.\r\nI'd offer a work around but I'm sure a temporal change of data type is not the best solution;-).\r\n\r\nBtw, is it expected that `sem` is a pd.Float64Index instance?\r\n\r\nHere's a toy example:\r\n```python\r\npd.DataFrame([1,2])+pd.Float64Index([.1,.2])\r\n```\r\n<!--\r\n\r\n\r\n-->\r\n\r\n**To Reproduce**\r\n<!--\r\nAdd a Minimal, Complete, and Verifiable example (for more details, see e.g. https://stackoverflow.com/help/mcve\r\n\r\nIf the code is too long, feel free to put it in a public gist and link it in the issue: https://gist.github.com\r\n-->\r\n\r\n```python\r\nimport pandas as pd\r\nfrom sktime.forecasting.theta import ThetaForecaster\r\nfrom sktime.forecasting.model_selection import temporal_train_test_split\r\nfrom sktime.datasets import load_airline\r\ny = load_airline()\r\ny_train, y_test = temporal_train_test_split(y)\r\n\r\nfp = ThetaForecaster(sp=12)\r\nfp.fit(pd.DataFrame(y_train))\r\nfp.predict_quantiles(fh=y_test.index)\r\n```\r\n\r\n**Expected behavior**\r\n<!--\r\nA clear and concise description of what you expected to happen.\r\n-->\r\n```python\r\nimport pandas as pd\r\nfrom sktime.forecasting.theta import ThetaForecaster\r\nfrom sktime.forecasting.model_selection import temporal_train_test_split\r\nfrom sktime.datasets import load_airline\r\ny = load_airline()\r\ny_train, y_test = temporal_train_test_split(y)\r\n\r\nfp = ThetaForecaster(sp=12)\r\nfp.fit(y_train)\r\nfp.predict_quantiles(fh=y_test.index)\r\n```\r\n**Additional context**\r\n<!--\r\nAdd any other context about the problem here.\r\n-->\r\n\r\n**Versions**\r\n<details>\r\n\r\n<!--\r\nPlease run the following code snippet and paste the output here:\r\n \r\nfrom sktime import show_versions; show_versions()\r\n-->\r\n\r\nSystem:\r\n    python: 3.8.13 | packaged by conda-forge | (default, Mar 25 2022, 05:59:45) [MSC v.1929 64 bit (AMD64)]\r\nexecutable: C:\\Users\\Mi\\miniconda3\\envs\\sktime-dev\\python.exe\r\n   machine: Windows-10-10.0.22000-SP0\r\n\r\nPython dependencies:\r\n          pip: 22.0.4\r\n   setuptools: 62.1.0\r\n      sklearn: 1.0.2\r\n       sktime: 0.11.2\r\n  statsmodels: 0.13.2\r\n        numpy: 1.22.3\r\n        scipy: 1.8.0\r\n       pandas: 1.4.2\r\n   matplotlib: 3.5.1\r\n       joblib: 1.1.0\r\n        numba: 0.53.0\r\n     pmdarima: 1.8.5\r\n      tsfresh: None\r\n\r\n</details>\r\n\r\n<!-- Thanks for contributing! -->\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"Theta forecaster.\"\"\"\n\n__all__ = [\"ThetaForecaster\"]\n__author__ = [\"big-o\", \"mloning\", \"kejsitake\", \"fkiraly\"]\n\nfrom warnings import warn\n\nimport numpy as np\nimport pandas as pd\nfrom scipy.stats import norm\n\nfrom sktime.forecasting.exp_smoothing import ExponentialSmoothing\nfrom sktime.transformations.series.detrend import Deseasonalizer\nfrom sktime.utils.slope_and_trend import _fit_trend\nfrom sktime.utils.validation.forecasting import check_sp\n\n\nclass ThetaForecaster(ExponentialSmoothing):\n    \"\"\"Theta method for forecasting.\n\n    The theta method as defined in [1]_ is equivalent to simple exponential\n    smoothing (SES) with drift (as demonstrated in [2]_).\n\n    The series is tested for seasonality using the test outlined in A&N. If\n    deemed seasonal, the series is seasonally adjusted using a classical\n    multiplicative decomposition before applying the theta method. The\n    resulting forecasts are then reseasonalised.\n\n    In cases where SES results in a constant forecast, the theta forecaster\n    will revert to predicting the SES constant plus a linear trend derived\n    from the training data.\n\n    Prediction intervals are computed using the underlying state space model.\n\n    Parameters\n    ----------\n    initial_level : float, optional\n        The alpha value of the simple exponential smoothing, if the value is\n        set then\n        this will be used, otherwise it will be estimated from the data.\n    deseasonalize : bool, optional (default=True)\n        If True, data is seasonally adjusted.\n    sp : int, optional (default=1)\n        The number of observations that constitute a seasonal period for a\n        multiplicative deseasonaliser, which is used if seasonality is\n        detected in the\n        training data. Ignored if a deseasonaliser transformer is provided.\n        Default is\n        1 (no seasonality).\n\n    Attributes\n    ----------\n    initial_level_ : float\n        The estimated alpha value of the SES fit.\n    drift_ : float\n        The estimated drift of the fitted model.\n    se_ : float\n        The standard error of the predictions. Used to calculate prediction\n        intervals.\n\n    References\n    ----------\n    .. [1] Assimakopoulos, V. and Nikolopoulos, K. The theta model: a\n       decomposition approach to forecasting. International Journal of\n       Forecasting 16, 521-530, 2000.\n       https://www.sciencedirect.com/science/article/pii/S0169207000000662\n\n    .. [2] `Hyndman, Rob J., and Billah, Baki. Unmasking the Theta method.\n       International J. Forecasting, 19, 287-290, 2003.\n       https://www.sciencedirect.com/science/article/pii/S0169207001001431\n\n    Examples\n    --------\n    >>> from sktime.datasets import load_airline\n    >>> from sktime.forecasting.theta import ThetaForecaster\n    >>> y = load_airline()\n    >>> forecaster = ThetaForecaster(sp=12)\n    >>> forecaster.fit(y)\n    ThetaForecaster(...)\n    >>> y_pred = forecaster.predict(fh=[1,2,3])\n    \"\"\"\n\n    _fitted_param_names = (\"initial_level\", \"smoothing_level\")\n    _tags = {\n        \"scitype:y\": \"univariate\",\n        \"ignores-exogeneous-X\": True,\n        \"capability:pred_int\": True,\n        \"requires-fh-in-fit\": False,\n        \"handles-missing-data\": False,\n    }\n\n    def __init__(self, initial_level=None, deseasonalize=True, sp=1):\n\n        self.sp = sp\n        self.deseasonalize = deseasonalize\n        self.deseasonalizer_ = None\n        self.trend_ = None\n        self.initial_level_ = None\n        self.drift_ = None\n        self.se_ = None\n        super(ThetaForecaster, self).__init__(initial_level=initial_level, sp=sp)\n\n    def _fit(self, y, X=None, fh=None):\n        \"\"\"Fit to training data.\n\n        Parameters\n        ----------\n        y : pd.Series\n            Target time series to which to fit the forecaster.\n        fh : int, list or np.array, optional (default=None)\n            The forecasters horizon with the steps ahead to to predict.\n        X : pd.DataFrame, optional (default=None)\n            Exogenous variables are ignored\n\n        Returns\n        -------\n        self : returns an instance of self.\n        \"\"\"\n        sp = check_sp(self.sp)\n        if sp > 1 and not self.deseasonalize:\n            warn(\"`sp` is ignored when `deseasonalise`=False\")\n\n        if self.deseasonalize:\n            self.deseasonalizer_ = Deseasonalizer(sp=self.sp, model=\"multiplicative\")\n            y = self.deseasonalizer_.fit_transform(y)\n\n        self.initialization_method = \"known\" if self.initial_level else \"estimated\"\n        # fit exponential smoothing forecaster\n        # find theta lines: Theta lines are just SES + drift\n        super(ThetaForecaster, self)._fit(y, fh=fh)\n        self.initial_level_ = self._fitted_forecaster.params[\"smoothing_level\"]\n\n        # compute and store historical residual standard error\n        self.sigma_ = np.sqrt(self._fitted_forecaster.sse / (len(y) - 1))\n\n        # compute trend\n        self.trend_ = self._compute_trend(y)\n\n        return self\n\n    def _predict(self, fh, X=None):\n        \"\"\"Make forecasts.\n\n        Parameters\n        ----------\n        fh : array-like\n            The forecasters horizon with the steps ahead to to predict.\n            Default is\n            one-step ahead forecast, i.e. np.array([1]).\n\n        Returns\n        -------\n        y_pred : pandas.Series\n            Returns series of predicted values.\n        \"\"\"\n        y_pred = super(ThetaForecaster, self)._predict(fh, X)\n\n        # Add drift.\n        drift = self._compute_drift()\n        y_pred += drift\n\n        if self.deseasonalize:\n            y_pred = self.deseasonalizer_.inverse_transform(y_pred)\n\n        return y_pred\n\n    @staticmethod\n    def _compute_trend(y):\n        # Trend calculated through least squares regression.\n        coefs = _fit_trend(y.values.reshape(1, -1), order=1)\n        return coefs[0, 0] / 2\n\n    def _compute_drift(self):\n        fh = self.fh.to_relative(self.cutoff)\n        if np.isclose(self.initial_level_, 0.0):\n            # SES was constant, so revert to simple trend\n            drift = self.trend_ * fh\n        else:\n            # Calculate drift from SES parameters\n            n_timepoints = len(self._y)\n            drift = self.trend_ * (\n                fh\n                + (1 - (1 - self.initial_level_) ** n_timepoints) / self.initial_level_\n            )\n\n        return drift\n\n    def _predict_quantiles(self, fh, X=None, alpha=None):\n        \"\"\"Compute/return prediction quantiles for a forecast.\n\n        private _predict_quantiles containing the core logic,\n            called from predict_quantiles and predict_interval\n\n        Parameters\n        ----------\n        fh : int, list, np.array or ForecastingHorizon\n            Forecasting horizon\n        X : pd.DataFrame, optional (default=None)\n            Exogenous time series\n        alpha : list of float, optional (default=[0.5])\n            A list of probabilities at which quantile forecasts are computed.\n\n        Returns\n        -------\n        quantiles : pd.DataFrame\n            Column has multi-index: first level is variable name from y in fit,\n                second level being the values of alpha passed to the function.\n            Row index is fh. Entries are quantile forecasts, for var in col index,\n                at quantile probability in second col index, for the row index.\n        \"\"\"\n        # prepare return data frame\n        index = pd.MultiIndex.from_product([[\"Quantiles\"], alpha])\n        pred_quantiles = pd.DataFrame(columns=index)\n\n        sem = self.sigma_ * np.sqrt(\n            self.fh.to_relative(self.cutoff) * self.initial_level_ ** 2 + 1\n        )\n\n        y_pred = super(ThetaForecaster, self).predict(fh, X)\n\n        # we assume normal additive noise with sem variance\n        for a in alpha:\n            pred_quantiles[(\"Quantiles\", a)] = y_pred + norm.ppf(a) * sem\n        # todo: should this not increase with the horizon?\n        # i.e., sth like norm.ppf(a) * sem * fh.to_absolute(cutoff) ?\n        # I've just refactored this so will leave it for now\n\n        return pred_quantiles\n\n    def _update(self, y, X=None, update_params=True):\n        super(ThetaForecaster, self)._update(\n            y, X, update_params=False\n        )  # use custom update_params routine\n        if update_params:\n            if self.deseasonalize:\n                y = self.deseasonalizer_.transform(self._y)  # use updated y\n            self.initial_level_ = self._fitted_forecaster.params[\"smoothing_level\"]\n            self.trend_ = self._compute_trend(y)\n        return self\n\n\ndef _zscore(level: float, two_tailed: bool = True) -> float:\n    \"\"\"Calculate a z-score from a confidence level.\n\n    Parameters\n    ----------\n    level : float\n        A confidence level, in the open interval (0, 1).\n    two_tailed : bool (default=True)\n        If True, return the two-tailed z score.\n\n    Returns\n    -------\n    z : float\n        The z score.\n    \"\"\"\n    alpha = 1 - level\n    if two_tailed:\n        alpha /= 2\n    return -norm.ppf(alpha)\n", "path": "sktime/forecasting/theta.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"Theta forecaster.\"\"\"\n\n__all__ = [\"ThetaForecaster\"]\n__author__ = [\"big-o\", \"mloning\", \"kejsitake\", \"fkiraly\"]\n\nfrom warnings import warn\n\nimport numpy as np\nimport pandas as pd\nfrom scipy.stats import norm\n\nfrom sktime.forecasting.exp_smoothing import ExponentialSmoothing\nfrom sktime.transformations.series.detrend import Deseasonalizer\nfrom sktime.utils.slope_and_trend import _fit_trend\nfrom sktime.utils.validation.forecasting import check_sp\n\n\nclass ThetaForecaster(ExponentialSmoothing):\n    \"\"\"Theta method for forecasting.\n\n    The theta method as defined in [1]_ is equivalent to simple exponential\n    smoothing (SES) with drift (as demonstrated in [2]_).\n\n    The series is tested for seasonality using the test outlined in A&N. If\n    deemed seasonal, the series is seasonally adjusted using a classical\n    multiplicative decomposition before applying the theta method. The\n    resulting forecasts are then reseasonalised.\n\n    In cases where SES results in a constant forecast, the theta forecaster\n    will revert to predicting the SES constant plus a linear trend derived\n    from the training data.\n\n    Prediction intervals are computed using the underlying state space model.\n\n    Parameters\n    ----------\n    initial_level : float, optional\n        The alpha value of the simple exponential smoothing, if the value is\n        set then\n        this will be used, otherwise it will be estimated from the data.\n    deseasonalize : bool, optional (default=True)\n        If True, data is seasonally adjusted.\n    sp : int, optional (default=1)\n        The number of observations that constitute a seasonal period for a\n        multiplicative deseasonaliser, which is used if seasonality is\n        detected in the\n        training data. Ignored if a deseasonaliser transformer is provided.\n        Default is\n        1 (no seasonality).\n\n    Attributes\n    ----------\n    initial_level_ : float\n        The estimated alpha value of the SES fit.\n    drift_ : float\n        The estimated drift of the fitted model.\n    se_ : float\n        The standard error of the predictions. Used to calculate prediction\n        intervals.\n\n    References\n    ----------\n    .. [1] Assimakopoulos, V. and Nikolopoulos, K. The theta model: a\n       decomposition approach to forecasting. International Journal of\n       Forecasting 16, 521-530, 2000.\n       https://www.sciencedirect.com/science/article/pii/S0169207000000662\n\n    .. [2] `Hyndman, Rob J., and Billah, Baki. Unmasking the Theta method.\n       International J. Forecasting, 19, 287-290, 2003.\n       https://www.sciencedirect.com/science/article/pii/S0169207001001431\n\n    Examples\n    --------\n    >>> from sktime.datasets import load_airline\n    >>> from sktime.forecasting.theta import ThetaForecaster\n    >>> y = load_airline()\n    >>> forecaster = ThetaForecaster(sp=12)\n    >>> forecaster.fit(y)\n    ThetaForecaster(...)\n    >>> y_pred = forecaster.predict(fh=[1,2,3])\n    \"\"\"\n\n    _fitted_param_names = (\"initial_level\", \"smoothing_level\")\n    _tags = {\n        \"scitype:y\": \"univariate\",\n        \"ignores-exogeneous-X\": True,\n        \"capability:pred_int\": True,\n        \"requires-fh-in-fit\": False,\n        \"handles-missing-data\": False,\n    }\n\n    def __init__(self, initial_level=None, deseasonalize=True, sp=1):\n\n        self.sp = sp\n        self.deseasonalize = deseasonalize\n        self.deseasonalizer_ = None\n        self.trend_ = None\n        self.initial_level_ = None\n        self.drift_ = None\n        self.se_ = None\n        super(ThetaForecaster, self).__init__(initial_level=initial_level, sp=sp)\n\n    def _fit(self, y, X=None, fh=None):\n        \"\"\"Fit to training data.\n\n        Parameters\n        ----------\n        y : pd.Series\n            Target time series to which to fit the forecaster.\n        fh : int, list or np.array, optional (default=None)\n            The forecasters horizon with the steps ahead to to predict.\n        X : pd.DataFrame, optional (default=None)\n            Exogenous variables are ignored\n\n        Returns\n        -------\n        self : returns an instance of self.\n        \"\"\"\n        sp = check_sp(self.sp)\n        if sp > 1 and not self.deseasonalize:\n            warn(\"`sp` is ignored when `deseasonalise`=False\")\n\n        if self.deseasonalize:\n            self.deseasonalizer_ = Deseasonalizer(sp=self.sp, model=\"multiplicative\")\n            y = self.deseasonalizer_.fit_transform(y)\n\n        self.initialization_method = \"known\" if self.initial_level else \"estimated\"\n        # fit exponential smoothing forecaster\n        # find theta lines: Theta lines are just SES + drift\n        super(ThetaForecaster, self)._fit(y, fh=fh)\n        self.initial_level_ = self._fitted_forecaster.params[\"smoothing_level\"]\n\n        # compute and store historical residual standard error\n        self.sigma_ = np.sqrt(self._fitted_forecaster.sse / (len(y) - 1))\n\n        # compute trend\n        self.trend_ = self._compute_trend(y)\n\n        return self\n\n    def _predict(self, fh, X=None):\n        \"\"\"Make forecasts.\n\n        Parameters\n        ----------\n        fh : array-like\n            The forecasters horizon with the steps ahead to to predict.\n            Default is\n            one-step ahead forecast, i.e. np.array([1]).\n        X : pd.DataFrame, optional (default=None)\n            Exogenous time series\n\n        Returns\n        -------\n        y_pred : pandas.Series\n            Returns series of predicted values.\n        \"\"\"\n        y_pred = super(ThetaForecaster, self)._predict(fh, X)\n\n        # Add drift.\n        drift = self._compute_drift()\n        y_pred += drift\n\n        if self.deseasonalize:\n            y_pred = self.deseasonalizer_.inverse_transform(y_pred)\n\n        return y_pred\n\n    @staticmethod\n    def _compute_trend(y):\n        # Trend calculated through least squares regression.\n        coefs = _fit_trend(y.values.reshape(1, -1), order=1)\n        return coefs[0, 0] / 2\n\n    def _compute_drift(self):\n        fh = self.fh.to_relative(self.cutoff)\n        if np.isclose(self.initial_level_, 0.0):\n            # SES was constant, so revert to simple trend\n            drift = self.trend_ * fh\n        else:\n            # Calculate drift from SES parameters\n            n_timepoints = len(self._y)\n            drift = self.trend_ * (\n                fh\n                + (1 - (1 - self.initial_level_) ** n_timepoints) / self.initial_level_\n            )\n\n        return drift\n\n    def _predict_quantiles(self, fh, X=None, alpha=None):\n        \"\"\"Compute/return prediction quantiles for a forecast.\n\n        private _predict_quantiles containing the core logic,\n            called from predict_quantiles and predict_interval\n\n        Parameters\n        ----------\n        fh : int, list, np.array or ForecastingHorizon\n            Forecasting horizon\n        X : pd.DataFrame, optional (default=None)\n            Exogenous time series\n        alpha : list of float, optional (default=[0.5])\n            A list of probabilities at which quantile forecasts are computed.\n\n        Returns\n        -------\n        quantiles : pd.DataFrame\n            Column has multi-index: first level is variable name from y in fit,\n                second level being the values of alpha passed to the function.\n            Row index is fh. Entries are quantile forecasts, for var in col index,\n                at quantile probability in second col index, for the row index.\n        \"\"\"\n        # prepare return data frame\n        index = pd.MultiIndex.from_product([[\"Quantiles\"], alpha])\n        pred_quantiles = pd.DataFrame(columns=index)\n\n        sem = self.sigma_ * np.sqrt(\n            self.fh.to_relative(self.cutoff) * self.initial_level_**2 + 1\n        )\n\n        y_pred = super(ThetaForecaster, self)._predict(fh, X)\n\n        # we assume normal additive noise with sem variance\n        for a in alpha:\n            pred_quantiles[(\"Quantiles\", a)] = y_pred + norm.ppf(a) * sem\n        # todo: should this not increase with the horizon?\n        # i.e., sth like norm.ppf(a) * sem * fh.to_absolute(cutoff) ?\n        # I've just refactored this so will leave it for now\n\n        return pred_quantiles\n\n    def _update(self, y, X=None, update_params=True):\n        super(ThetaForecaster, self)._update(\n            y, X, update_params=False\n        )  # use custom update_params routine\n        if update_params:\n            if self.deseasonalize:\n                y = self.deseasonalizer_.transform(self._y)  # use updated y\n            self.initial_level_ = self._fitted_forecaster.params[\"smoothing_level\"]\n            self.trend_ = self._compute_trend(y)\n        return self\n\n\ndef _zscore(level: float, two_tailed: bool = True) -> float:\n    \"\"\"Calculate a z-score from a confidence level.\n\n    Parameters\n    ----------\n    level : float\n        A confidence level, in the open interval (0, 1).\n    two_tailed : bool (default=True)\n        If True, return the two-tailed z score.\n\n    Returns\n    -------\n    z : float\n        The z score.\n    \"\"\"\n    alpha = 1 - level\n    if two_tailed:\n        alpha /= 2\n    return -norm.ppf(alpha)\n", "path": "sktime/forecasting/theta.py"}]}