Repository: sktime/sktime

Issue:
[ENH] AutoETS to ignore multiplicative components when time series is not strictly positive
**Is your feature request related to a problem? Please describe.**
If a time series is not strictly positive, ETS models (from statsmodels) with multiplicative components will raise the following error:
```
ValueError: endog must be strictly positive when usingmultiplicative error, trend or seasonal components.
```

Currently AutoETS will raise the same error because it tries all the available ETS models. Ideally AutoETS will skip the models that result in this error and will try all the other ones. 

**Describe the solution you'd like**
One solution would be to add a try/except in the [_fit](https://github.com/alan-turing-institute/sktime/blob/2d1eaa22854beecdbe63fcd076aa486140f35193/sktime/forecasting/ets.py#L278) method. The other solution I can think of is to check whether the time series is strictly positive and if it's not remove the multiplicative components from [_iter](https://github.com/alan-turing-institute/sktime/blob/2d1eaa22854beecdbe63fcd076aa486140f35193/sktime/forecasting/ets.py#L258).

**The following code reproduces the error:** 
```python
import pandas as pd
import numpy as np
from sktime.forecasting.ets import AutoETS
from sktime.utils.plotting import plot_series

ts = pd.Series(
    10*np.sin(np.array(range(0,264))/10)+10, 
    pd.date_range("2017-01-01", periods=264,  freq="W")
)
ts[ts<1]=0
forecaster = AutoETS(auto=True, sp=52, n_jobs=-1, ignore_inf_ic=True)
plot_series(ts)
forecaster.fit(ts)
```
<img width="935" alt="image" src="https://user-images.githubusercontent.com/64217214/132105553-92e8925b-45d9-4e7a-8bd2-b618749381ef.png">



Discussion:
@ltsaprounis @fkiraly Can we work on this issue?

> Can we work on this issue?

If this is a Royal "we", certainly - or do you mean, you plus @ltsaprounis? Then you would have to ask @ltsaprounis.
Either way, it may good to choosing the target solution here before you start.

My preference would be the second option, what do you think, @aiwalter, @mloning?

@fkiraly actually we are a team so that's why I used 'we', I am looking at the issue and once I get a heads up from @aiwalter and @mloning. I will raise the PR.
Thanks @mathco-wf! 
@fkiraly I prefer the second option too. `(ts<=0).any()` should work well for the check. 
alright, let's do that (if there are no dissenting opinions from @mloning or @aiwalter)
Sounds good to me - @HYang1996 would appreciate your opinion if you can find some time?
@mathco-wf - are you still keen to fix this? 
If yes you can start with a draft PR. 
> Sounds good to me - @HYang1996 would appreciate your opinion if you can find some time?

@mloning, I'm sorry for the late reply. The [fable version in R](https://github.com/tidyverts/fable/blob/7868ca73aea2b6bec8c9e15ad3ef84566151c3f1/R/ets.R#L34-L47) seems to check for this error as well so I think the second approach makes sense.
Thanks @HYang1996! So I suggest we implement that check in our version too and add a short note to the docstring. 

Resolving patch:
diff --git a/sktime/forecasting/ets.py b/sktime/forecasting/ets.py
--- a/sktime/forecasting/ets.py
+++ b/sktime/forecasting/ets.py
@@ -6,6 +6,7 @@
 __all__ = ["AutoETS"]
 __author__ = ["Hongyi Yang"]
 
+import warnings
 from itertools import product
 
 import numpy as np
@@ -236,15 +237,27 @@ def _fit_forecaster(self, y, X=None):
         # Select model automatically
         if self.auto:
             # Initialise parameter ranges
-            error_range = ["add", "mul"]
-            if self.allow_multiplicative_trend:
+            if (y > 0).all():
+                error_range = ["add", "mul"]
+            else:
+                warnings.warn(
+                    "Warning: time series is not strictly positive,"
+                    "multiplicative components are ommitted"
+                )
+                error_range = ["add"]
+
+            if self.allow_multiplicative_trend and (y > 0).all():
                 trend_range = ["add", "mul", None]
             else:
                 trend_range = ["add", None]
+
             if self.sp <= 1 or self.sp is None:
                 seasonal_range = [None]
-            else:
+            elif (y > 0).all():
                 seasonal_range = ["add", "mul", None]
+            else:
+                seasonal_range = ["add", None]
+
             damped_range = [True, False]
 
             # Check information criterion input


Test patch: