Repository: sktime/sktime
Pull request #5779 (2024-01-18 23:00:10)

[ENH] `FallbackForecaster`, fallback upon fail with multiple forecaster chain
<!--
Welcome to sktime, and thanks for contributing!
Please have a look at our contribution guide:
https://www.sktime.net/en/latest/get_involved/contributing.html
-->

#### Reference Issues/PRs
https://github.com/sktime/sktime/issues/5775
https://github.com/sktime/sktime/discussions/5729


#### What does this implement/fix? Explain your changes.
The `FallbackForecaster` in the sktime package enhances forecasting robustness by sequentially fitting a user-specified list of forecasters, ensuring reliability even when certain models fail during fitting or prediction. This approach addresses the unpredictability of time series data and model-specific behavior, providing a systematic fallback mechanism to handle failures gracefully.

#### Does your contribution introduce a new dependency? If yes, which one?

No

#### What should a reviewer concentrate their feedback on?

Reviewers should focus their feedback on the effectiveness and efficiency of the `FallbackForecaster`'s mechanism for handling model failures during the fitting and prediction stages. Attention should be paid to its ability to integrate seamlessly within the `sktime` forecasting pipelines, the clarity and robustness of error handling, and its overall impact on improving forecasting reliability. Reviewers might consider suggesting additional parameterization to increase usability. 


#### Did you add any tests for the change?

Introduced `sktime/forecasting/compose/tests/test_fallback.py`

#### Any other comments?
Open to including additional parameterization or functionality.

#### PR checklist


##### For all contributions
- [x] I've added myself to the [list of contributors](https://github.com/sktime/sktime/blob/main/CONTRIBUTORS.md) with any new badges I've earned :-)
  How to: add yourself to the [all-contributors file](https://github.com/sktime/sktime/blob/main/.all-contributorsrc) in the `sktime` root directory (not the `CONTRIBUTORS.md`). Common badges: `code` - fixing a bug, or adding code logic. `doc` - writing or improving documentation or docstrings. `bug` - reporting or diagnosing a bug (get this plus `code` if you also fixed the bug in the PR).`maintenance` - CI, test framework, release.
  See here for [full badge reference](https://allcontributors.org/docs/en/emoji-key)
- [ ] Optionally, I've added myself and possibly others to the [CODEOWNERS](https://github.com/sktime/sktime/blob/main/CODEOWNERS) file - do this if you want to become the owner or maintainer of an estimator you added.
  See here for further details on the [algorithm maintainer role](https://www.sktime.net/en/latest/get_involved/governance.html#algorithm-maintainers).
- [x] The PR title starts with either [ENH], [MNT], [DOC], or [BUG]. [BUG] - bugfix, [MNT] - CI, test framework, [ENH] - adding or improving code, [DOC] - writing or improving documentation or docstrings.

##### For new estimators
- [ ] I've added the estimator to the API reference - in `docs/source/api_reference/taskname.rst`, follow the pattern.
- [x] I've added one or more illustrative usage examples to the docstring, in a pydocstyle compliant `Examples` section.
- [ ] If the estimator relies on a soft dependency, I've set the `python_dependencies` tag and ensured
  dependency isolation, see the [estimator dependencies guide](https://www.sktime.net/en/latest/developer_guide/dependencies.html#adding-a-soft-dependency).

<!--
Thanks for contributing!
-->


fkiraly: > `sktime` does not thourough data validation for the inputs, as long as it's one of the supported types, e.g. `pd.DataFrame`, of supported mtypes,

It does to data validation along the lines of `sklearn` - if it passes, usually it is guaranteed that assumptions of the algos are satisfied.
yarnabrina: > It does to data validation along the lines of `sklearn` - if it passes, usually it is guaranteed that assumptions of the algos are satisfied.

I meant failures like these:

```python
from sktime.datasets import load_longley
from sktime.forecasting.sarimax import SARIMAX
from sktime.split import temporal_train_test_split

y, X = load_longley()
y_train, y_test, X_train, X_test = temporal_train_test_split(y, X)

forecaster = SARIMAX()
forecaster.fit(y_train, X=X_train)

# user forgot to pass exogenous features -> failure
forecaster.predict(fh=[1, 2, 3, 4], X=X_test.drop(columns=["GNPDEFL", "GNP"]))

# user forgot to pass a subset of exogenous features -> failure
forecaster.predict(fh=[1, 2, 3, 4], X=X_test.drop(columns=["GNPDEFL", "GNP"]))
```

We have deployed inference endpoints with `sklearn` models with `kserve`, and frequently we see end users pass wrong inputs and other minor mistakes etc.
ninedigits: > I added some minor comments for the time being, but have some concerns/confusions about the decisions to remove trained forecaster just because of failure in a single prediction and to initiate training of next.

@yarnabrina I appreciate the comments and concerns! I'll try my best to address them.

> a lot of the times end users may pass wrong inputs by mistake and this will re-initiate training, which may be really time taking. 

Your concern here is related to user error. If the user makes a mistake, I think the best thing we can do to address it is to make the error as clear as possible or to add in a validation check. Furthermore, `FallbackForecaster` might not be an appropriate use case for some scenarios. 

The motivation for FallbackForecaster is to handle scenarios where numerous forecasts must be individually trained on distinct time series datasets. It's particularly useful in scenarios involving hundreds or thousands of forecasts, which may have different starting points and patterns within the data. These forecasts are sufficiently similar to employ a uniform set of forecasters. However, it's recognized that certain forecasters may fail due to a variety of reasons, and the FallbackForecaster is designed to accommodate such eventualities efficiently.

> Also, the user will still have to request prediction one more time. Is this a suitable behaviour for majority of workflows? (For my office works, it's not.)

Predict should only need to be called once. If the predict step fails, it will move to the next forecaster and perform the fit and predict steps.

```python3
forecaster = FallbackForecaster(
    [
        ("forecaster1", DummyForecaster(raise_at="predict")),
        ("forecaster1", NaiveForecaster()),
    ]
)
forecaster.fit(y)               # Fits forecaster1 first
y_pred = forecaster.predict()   # Tries to predict forecaster1, but fails. Moves to forecaster2, fits, then predicts.
```

> Also, please run `pre-commit` locally once to fix the formatting/linting issues.

Apologies, I wasn't sure exactly how to do this. I was running `black` and `flake8` from the terminal. I can review how to use pre-commit.

fkiraly: Instructions to set up linting on your local computer are in this section:
https://www.sktime.net/en/stable/developer_guide/coding_standards.html
let us know if you would like help setting this up, you can join the meet-ups for screenshare.
ninedigits: @yarnabrina 

Here’s an example scenario to explain where I’m coming from with this idea.

Consider the task of forecasting liabilities for 1000 accounts. The goal is to ensure the business maintains enough cash reserves to meet these liabilities. Each account's liability forms a time series, and accurate forecasts are crucial for maintaining financial stability and avoiding over or underestimation of required cash reserves.

The business relies on these forecasts to ensure it has sufficient liquidity to cover liabilities as they come due and that it does not hold excess capital in reserves that could otherwise be put to use elsewehre. Accuracy is paramount, but given the scale (1000 accounts, and 36 backtests => 36,000 iterations), the forecasting model must also be robust against sporadic failures.

After evaluating various models, I've found that an ensemble of AutoARIMA and AutoETS with seasonal components generally delivers the most accurate predictions. However, AutoARIMA is known to fail unpredictably. The next best option is a similar ensemble without seasonal components, which is more stable but slightly less accurate. As a fail-safe, the NaiveForecaster is the least precise but guarantees a forecast, ensuring that the business is never without a cash reserve projection. Of note, there can be additional fallback forecasters added in, this is just a generic example.

The backtesting framework is designed to handle and scale this task. It iterates through each account, attempting to forecast liabilities using the best-performing models first. If a model fails, it seamlessly transitions to the next option, ensuring that every account has a forecast, and the business can confidently plan its cash reserves.


```python3
def mymodel():
    for account_liabilities in all_accounts:
        for liability_date in list_backtest_dates:
            y_train, y_test = train_test_split(account_liabilities, first_forecast_date=liability_date, n_forecasts=1)
            
            try:
                forecaster = EnsembleForecaster((AutoARIMA(sp=12), AutoETS(sp=12)))
                forecaster.fit(y_train)
                return forecaster.predict(fh=[1])
            except ForecastingError:
                pass  # AutoARIMA or AutoETS failed, try the next option

            try:
                # Attempt with the next best forecaster
                ...
```
In this context, the `FallbackForecaster` ensures that, despite the inherent unpredictability of certain models, our liability forecasting process remains uninterrupted and reliable, enabling the business to maintain the necessary liquidity for all accounts.

As the business continues it's operations, real time results are evaluated, and if it determines that the backtesting results drift from the desired accuracy, the poorest performers are examined and new models are considered as either a replacement or addition to the pipeline.

> Suppose you have 100 series and have 5 models, and each of them is suitable for roughly 20 series. If you want to use this estimator, how do you ensure that the "suitable" series for first estimator are the first ones, so that one does not get deleted before failing for the next set of series where it's not suitable and so on?

In your example, the hope is that estimator 1 will work for all 100 series. All following estimators are backups. We want to avoid specifying estimators for specific accounts because this becomes harder to maintain and can be prone overfitting.


yarnabrina: Thanks a lot @ninedigits for the detailed explanation, I now understand what I was thinking differently.

I was imagining that your data is in panel or hierarchical data format - multi index with last one bein temporal identifier of some sort and previous ones being series/hierarchy identifiers, and different columns are the different endogenous or exogenous features. Then you initiate a single forecaster object, and call on the entire dataset taking advantage of vectorisation provided by sktime to simulate a global model type of functionality. It's something along these lines:

```python
# load from csv, database, etc.
raw_data = ...

# convert to sktime mtype
sktime_compatible_data = raw_data.set_index(["region", "account", "week"])

# extract features
endo = sktime_compatible_data["reserve"]
exo = sktime_compatible_data.drop(columns=["reserve"])

# split subsets
y_train, y_test, X_train, X_test = temporal_train_test_split(endo, X=exo)

# initiate model
forecaster = EnsembleForecaster((AutoARIMA(sp=12), AutoETS(sp=12)))

# train model
forecaster.fit(y_train, X=X_train, fh=range(1, 4 + 1))

# predict from model
y_pred = forecaster.predict(X=X_test)

# evaluate predictions
score = ...
```

In this format, `sktime` takes responsibility to loop over different region-account combinations. Based on my understanding of your current implementation, failure in prediction for one series will delete a trained model completely, which is what I would definitely want for my usecase which trains for hours. Currently also we don't have a way to predict for a single series (I think @benHeid had proposed that some time back, can't find the issue now), and if anything fails even for one series, all it fails entirely.

Improving this behaviour will be super useful and that was what I thought you're trying to address. But since you are using forecasters independently for each time series of interest in a for loop, it does not have the issues I was thinking of, and it's a very different type of use. Apologies for the misunderstanding.
fkiraly: Yes, I was imagining that the fallback is broadcast across many individual series, all or a certain larger group of, ensuring that one breaking model does not break 100s.
ninedigits: Ran the latest revisions with `pre-commit` beforehand, thanks for showing me how to do that.

Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 699cde009..5b4285e89 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2545,6 +2545,17 @@
         "maintenance"
       ]
     },
+    {
+      "login": "ninedigits",
+      "name": "Max Frohlich",
+      "avatar_url": "https://avatars.githubusercontent.com/u/16393653?v=4",
+      "profile": "https://www.linkedin.com/in/maxfrohlich/",
+      "contributions": [
+        "code",
+        "ideas",
+        "maintenance"
+      ]
+    },
     {
       "login": "steenrotsman",
       "name": "Stijn J. Rotman",
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 5b9b9183e..5f938eba3 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -6,7 +6,7 @@ Contributors
 <!-- Please add your badges to .all-contibutorsrc -->
 
 <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
-[![All Contributors](https://img.shields.io/badge/all_contributors-251-orange.svg)](#contributors)
+[![All Contributors](https://img.shields.io/badge/all_contributors-252-orange.svg)](#contributors)
 <!-- ALL-CONTRIBUTORS-BADGE:END -->
 
 This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
@@ -189,12 +189,13 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/MCRE-BE"><img src="https://avatars.githubusercontent.com/u/99316631?s=100" width="100px;" alt="Mathias Creemers"/><br /><sub><b>Mathias Creemers</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3AMCRE-BE" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=MCRE-BE" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="http://www.timeseriesclassification.com"><img src="https://avatars0.githubusercontent.com/u/25731235?v=4?s=100" width="100px;" alt="Matthew Middlehurst"/><br /><sub><b>Matthew Middlehurst</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=MatthewMiddlehurst" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=MatthewMiddlehurst" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=MatthewMiddlehurst" title="Tests">⚠️</a> <a href="#tutorial-MatthewMiddlehurst" title="Tutorials">✅</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3AMatthewMiddlehurst" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sktime/sktime/issues?q=author%3AMatthewMiddlehurst" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/tvdboom"><img src="https://avatars.githubusercontent.com/u/32366550?v=4?s=100" width="100px;" alt="Mavs"/><br /><sub><b>Mavs</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=tvdboom" title="Code">💻</a></td>
+      <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/maxfrohlich/"><img src="https://avatars.githubusercontent.com/u/16393653?v=4?s=100" width="100px;" alt="Max Frohlich"/><br /><sub><b>Max Frohlich</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ninedigits" title="Code">💻</a> <a href="#ideas-ninedigits" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ninedigits" title="Maintenance">🚧</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/solen0id"><img src="https://avatars.githubusercontent.com/u/20767606?v=4?s=100" width="100px;" alt="Max Patzelt"/><br /><sub><b>Max Patzelt</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=solen0id" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Hephaest"><img src="https://avatars2.githubusercontent.com/u/37981444?v=4?s=100" width="100px;" alt="Miao Cai"/><br /><sub><b>Miao Cai</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3AHephaest" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=Hephaest" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="michaelfeil.eu"><img src="https://avatars.githubusercontent.com/u/63565275?v=4?s=100" width="100px;" alt="Michael Feil"/><br /><sub><b>Michael Feil</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=michaelfeil" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=michaelfeil" title="Tests">⚠️</a> <a href="#ideas-michaelfeil" title="Ideas, Planning, & Feedback">🤔</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/mgazian000"><img src="https://avatars.githubusercontent.com/mgazian000?s=100" width="100px;" alt="Michael Gaziani"/><br /><sub><b>Michael Gaziani</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=mgazian000" title="Documentation">📖</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/mgazian000"><img src="https://avatars.githubusercontent.com/mgazian000?s=100" width="100px;" alt="Michael Gaziani"/><br /><sub><b>Michael Gaziani</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=mgazian000" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/MichalChromcak"><img src="https://avatars1.githubusercontent.com/u/12393430?v=4?s=100" width="100px;" alt="Michal Chromcak"/><br /><sub><b>Michal Chromcak</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=MichalChromcak" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=MichalChromcak" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=MichalChromcak" title="Tests">⚠️</a> <a href="#tutorial-MichalChromcak" title="Tutorials">✅</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/miraep8"><img src="https://avatars.githubusercontent.com/u/10511777?s=400&u=10a774fd4be767fa3b23a82a98bbfe102c17f0f3&v=4?s=100" width="100px;" alt="Mirae Parker"/><br /><sub><b>Mirae Parker</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=miraep8" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=miraep8" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/MBristle"><img src="https://avatars.githubusercontent.com/MBristle?s=100" width="100px;" alt="Mirko Bristle"/><br /><sub><b>Mirko Bristle</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=MBristle" title="Documentation">📖</a></td>
@@ -203,9 +204,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Multivin12"><img src="https://avatars3.githubusercontent.com/u/36476633?v=4?s=100" width="100px;" alt="Multivin12"/><br /><sub><b>Multivin12</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Multivin12" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=Multivin12" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/marcio55afr"><img src="https://avatars.githubusercontent.com/u/42646282?v=4?s=100" width="100px;" alt="Márcio A. Freitas Jr"/><br /><sub><b>Márcio A. Freitas Jr</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=marcio55afr" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/niekvanderlaan"><img src="https://avatars.githubusercontent.com/u/9962825?v=4?s=100" width="100px;" alt="Niek van der Laan"/><br /><sub><b>Niek van der Laan</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=niekvanderlaan" title="Code">💻</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/ngupta23"><img src="https://avatars0.githubusercontent.com/u/33585645?v=4?s=100" width="100px;" alt="Nikhil Gupta"/><br /><sub><b>Nikhil Gupta</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ngupta23" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Angupta23" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=ngupta23" title="Documentation">📖</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/ngupta23"><img src="https://avatars0.githubusercontent.com/u/33585645?v=4?s=100" width="100px;" alt="Nikhil Gupta"/><br /><sub><b>Nikhil Gupta</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ngupta23" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Angupta23" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=ngupta23" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/nshahpazov/"><img src="https://avatars.githubusercontent.com/nshahpazov?s=100" width="100px;" alt="Nikola Shahpazov"/><br /><sub><b>Nikola Shahpazov</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=nshahpazov" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/nilesh05apr"><img src="https://avatars.githubusercontent.com/u/65773314?v=4?s=100" width="100px;" alt="Nilesh Kumar"/><br /><sub><b>Nilesh Kumar</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=nilesh05apr" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/ninfueng"><img src="https://avatars2.githubusercontent.com/u/28499769?v=4?s=100" width="100px;" alt="Ninnart Fuengfusin"/><br /><sub><b>Ninnart Fuengfusin</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ninfueng" title="Code">💻</a></td>
@@ -214,9 +215,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/kachayev"><img src="https://avatars.githubusercontent.com/u/485647?v=4?s=100" width="100px;" alt="Oleksii Kachaiev"/><br /><sub><b>Oleksii Kachaiev</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=kachayev" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=kachayev" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/olivermatthews"><img src="https://avatars.githubusercontent.com/u/31141490?v=4?s=100" width="100px;" alt="Oliver Matthews"/><br /><sub><b>Oliver Matthews</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=OliverMatthews" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/prockenschaub"><img src="https://avatars0.githubusercontent.com/u/15381732?v=4?s=100" width="100px;" alt="Patrick Rockenschaub"/><br /><sub><b>Patrick Rockenschaub</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=prockenschaub" title="Code">💻</a> <a href="#design-prockenschaub" title="Design">🎨</a> <a href="#ideas-prockenschaub" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sktime/sktime/commits?author=prockenschaub" title="Tests">⚠️</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="http://www2.informatik.hu-berlin.de/~schaefpa/"><img src="https://avatars0.githubusercontent.com/u/7783034?v=4?s=100" width="100px;" alt="Patrick Schäfer"/><br /><sub><b>Patrick Schäfer</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=patrickzib" title="Code">💻</a> <a href="#tutorial-patrickzib" title="Tutorials">✅</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="http://www2.informatik.hu-berlin.de/~schaefpa/"><img src="https://avatars0.githubusercontent.com/u/7783034?v=4?s=100" width="100px;" alt="Patrick Schäfer"/><br /><sub><b>Patrick Schäfer</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=patrickzib" title="Code">💻</a> <a href="#tutorial-patrickzib" title="Tutorials">✅</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://ber.gp"><img src="https://avatars1.githubusercontent.com/u/9824244?v=4?s=100" width="100px;" alt="Paul"/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Pangoraw" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/paulyim97/"><img src="https://avatars.githubusercontent.com/pyyim?s=100" width="100px;" alt="Paul Yim"/><br /><sub><b>Paul Yim</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=pyyim" title="Code">💻</a> <a href="#example-pyyim" title="Examples">💡</a> <a href="https://github.com/sktime/sktime/commits?author=pyyim" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.imes.uni-hannover.de/de/institut/team/m-sc-karl-philipp-kortmann/"><img src="https://avatars.githubusercontent.com/u/20466981?v=4?s=100" width="100px;" alt="Philipp Kortmann"/><br /><sub><b>Philipp Kortmann</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=MrPr3ntice" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=MrPr3ntice" title="Documentation">📖</a></td>
@@ -225,9 +226,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/pranav-prajapati-a5b413226/"><img src="https://avatars.githubusercontent.com/u/94780581?v=4?s=100" width="100px;" alt="Pranav Prajapati"/><br /><sub><b>Pranav Prajapati</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=pranavvp16" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=pranavvp16" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/pul95"><img src="https://avatars.githubusercontent.com/pul95?s=100" width="100px;" alt="Pulkit Verma"/><br /><sub><b>Pulkit Verma</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=pul95" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Quaterion"><img src="https://avatars2.githubusercontent.com/u/23200273?v=4?s=100" width="100px;" alt="Quaterion"/><br /><sub><b>Quaterion</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3AQuaterion" title="Bug reports">🐛</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/rakshitha123"><img src="https://avatars.githubusercontent.com/u/7654679?v=4?s=100" width="100px;" alt="Rakshitha Godahewa"/><br /><sub><b>Rakshitha Godahewa</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=rakshitha123" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=rakshitha123" title="Documentation">📖</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/rakshitha123"><img src="https://avatars.githubusercontent.com/u/7654679?v=4?s=100" width="100px;" alt="Rakshitha Godahewa"/><br /><sub><b>Rakshitha Godahewa</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=rakshitha123" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=rakshitha123" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Ram0nB"><img src="https://avatars.githubusercontent.com/u/45173421?s=100" width="100px;" alt="Ramon Bussing"/><br /><sub><b>Ramon Bussing</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Ram0nB" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=Ram0nB" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/RavenRudi"><img src="https://avatars.githubusercontent.com/u/46402968?v=4?s=100" width="100px;" alt="RavenRudi"/><br /><sub><b>RavenRudi</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=RavenRudi" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/wolph"><img src="?s=100" width="100px;" alt="Rick van Hattem"/><br /><sub><b>Rick van Hattem</b></sub></a><br /><a href="#infra-wolph" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
@@ -236,9 +237,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><img src="https://avatars.githubusercontent.com/u/55790848?v=4?s=100" width="100px;" alt="Riya Elizabeth John"/><br /><sub><b>Riya Elizabeth John</b></sub><br /><a href="https://github.com/sktime/sktime/commits?author=Riyabelle25" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Riyabelle25"><img src="https://avatars.githubusercontent.com/u/55790848?v=4?s=100" width="100px;" alt="Riya Elizabeth John"/><br /><sub><b>Riya Elizabeth John</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Riyabelle25" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=Riyabelle25" title="Tests">⚠️</a> <a href="https://github.com/sktime/sktime/commits?author=Riyabelle25" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/romanlutz/"><img src="https://avatars.githubusercontent.com/u/10245648?v=4?s=100" width="100px;" alt="Roman Lutz"/><br /><sub><b>Roman Lutz</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=romanlutz" title="Documentation">📖</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/ronnie-llamado"><img src="https://avatars.githubusercontent.com/ronnie-llamado?s=100" width="100px;" alt="Ronnie Llamado"/><br /><sub><b>Ronnie Llamado</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ronnie-llamado" title="Documentation">📖</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/ronnie-llamado"><img src="https://avatars.githubusercontent.com/ronnie-llamado?s=100" width="100px;" alt="Ronnie Llamado"/><br /><sub><b>Ronnie Llamado</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ronnie-llamado" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/rnkuhns"><img src="https://avatars0.githubusercontent.com/u/26907244?v=4?s=100" width="100px;" alt="Ryan Kuhns"/><br /><sub><b>Ryan Kuhns</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=RNKuhns" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=RNKuhns" title="Documentation">📖</a> <a href="#tutorial-RNKuhns" title="Tutorials">✅</a> <a href="#example-RNKuhns" title="Examples">💡</a> <a href="#ideas-RNKuhns" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3ARNKuhns" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sktime/sktime/commits?author=RNKuhns" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/achieveordie"><img src="https://avatars.githubusercontent.com/u/54197164?v=4?s=100" width="100px;" alt="Sagar Mishra"/><br /><sub><b>Sagar Mishra</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Aachieveordie" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=achieveordie" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=achieveordie" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://sajay.online"><img src="https://avatars2.githubusercontent.com/u/25329624?v=4?s=100" width="100px;" alt="Sajaysurya Ganesh"/><br /><sub><b>Sajaysurya Ganesh</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=sajaysurya" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=sajaysurya" title="Documentation">📖</a> <a href="#design-sajaysurya" title="Design">🎨</a> <a href="#example-sajaysurya" title="Examples">💡</a> <a href="#ideas-sajaysurya" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sktime/sktime/commits?author=sajaysurya" title="Tests">⚠️</a> <a href="#tutorial-sajaysurya" title="Tutorials">✅</a></td>
@@ -247,9 +248,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Saransh-cpp"><img src="https://avatars.githubusercontent.com/u/74055102?v=4?s=100" width="100px;" alt="Saransh Chopra"/><br /><sub><b>Saransh Chopra</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Saransh-cpp" title="Documentation">📖</a> <a href="#infra-Saransh-cpp" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/satya-pattnaik-77a430144/"><img src="https://avatars.githubusercontent.com/u/22102468?v=4?s=100" width="100px;" alt="Satya Prakash Pattnaik"/><br /><sub><b>Satya Prakash Pattnaik</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=satya-pattnaik" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/dasgupsa"><img src="https://avatars2.githubusercontent.com/u/10398956?v=4?s=100" width="100px;" alt="Saurabh Dasgupta"/><br /><sub><b>Saurabh Dasgupta</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=dasgupsa" title="Code">💻</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/SebasKoel"><img src="https://avatars3.githubusercontent.com/u/66252156?v=4?s=100" width="100px;" alt="Sebastiaan Koel"/><br /><sub><b>Sebastiaan Koel</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=SebasKoel" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=SebasKoel" title="Documentation">📖</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/SebasKoel"><img src="https://avatars3.githubusercontent.com/u/66252156?v=4?s=100" width="100px;" alt="Sebastiaan Koel"/><br /><sub><b>Sebastiaan Koel</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=SebasKoel" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=SebasKoel" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/shagn"><img src="https://avatars.githubusercontent.com/u/16029092?v=4?s=100" width="100px;" alt="Sebastian Hagn"/><br /><sub><b>Sebastian Hagn</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=shagn" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/ShivamPathak99"><img src="https://avatars.githubusercontent.com/u/98941325?s=400&v=4?s=100" width="100px;" alt="Shivam Pathak"/><br /><sub><b>Shivam Pathak</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ShivamPathak99" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/AurumnPegasus"><img src="https://avatars.githubusercontent.com/u/54315149?v=4?s=100" width="100px;" alt="Shivansh Subramanian"/><br /><sub><b>Shivansh Subramanian</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=AurumnPegasus" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=AurumnPegasus" title="Code">💻</a></td>
@@ -258,9 +259,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/khrapovs"><img src="https://avatars.githubusercontent.com/u/3774663?v=4?s=100" width="100px;" alt="Stanislav Khrapov"/><br /><sub><b>Stanislav Khrapov</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=khrapovs" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/steenrotsman"><img src="https://avatars.githubusercontent.com/u/78110080?s=400&v=4?s=100" width="100px;" alt="Stijn J. Rotman"/><br /><sub><b>Stijn J. Rotman</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=steenrotsman" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=steenrotsman" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/SveaMeyer13"><img src="https://avatars.githubusercontent.com/u/46671894?v=4?s=100" width="100px;" alt="Svea Marie Meyer"/><br /><sub><b>Svea Marie Meyer</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=SveaMeyer13" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=SveaMeyer13" title="Code">💻</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/TNTran92"><img src="https://avatars.githubusercontent.com/u/55965636?v=4?s=100" width="100px;" alt="TNTran92"/><br /><sub><b>TNTran92</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=TNTran92" title="Code">💻</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/TNTran92"><img src="https://avatars.githubusercontent.com/u/55965636?v=4?s=100" width="100px;" alt="TNTran92"/><br /><sub><b>TNTran92</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=TNTran92" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Taise228"><img src="https://avatars.githubusercontent.com/u/95762401?s=400&v=4?s=100" width="100px;" alt="Taisei Yamamoto"/><br /><sub><b>Taisei Yamamoto</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Taise228" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://thayeylolu.github.io/portfolio/"><img src="https://avatars.githubusercontent.com/u/13348874?v=4?s=100" width="100px;" alt="Taiwo Owoseni"/><br /><sub><b>Taiwo Owoseni</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=thayeylolu" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/lnthach"><img src="https://avatars0.githubusercontent.com/u/7788363?v=4?s=100" width="100px;" alt="Thach Le Nguyen"/><br /><sub><b>Thach Le Nguyen</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=lnthach" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=lnthach" title="Tests">⚠️</a></td>
@@ -269,9 +270,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/xxl4tomxu98"><img src="https://avatars.githubusercontent.com/u/62292177?s=40&v=4?s=100" width="100px;" alt="Tom Xu"/><br /><sub><b>Tom Xu</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=xxl4tomxu98" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=xxl4tomxu98" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/tpvasconcelos"><img src="https://avatars.githubusercontent.com/u/17701527?v=4?s=100" width="100px;" alt="Tomas P. de Vasconcelos"/><br /><sub><b>Tomas P. de Vasconcelos</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Atpvasconcelos" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=tpvasconcelos" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/tch"><img src="https://avatars3.githubusercontent.com/u/184076?v=4?s=100" width="100px;" alt="Tomasz Chodakowski"/><br /><sub><b>Tomasz Chodakowski</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=tch" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=tch" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Atch" title="Bug reports">🐛</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="http://www.timeseriesclassification.com"><img src="https://avatars1.githubusercontent.com/u/9594042?v=4?s=100" width="100px;" alt="Tony Bagnall"/><br /><sub><b>Tony Bagnall</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=TonyBagnall" title="Code">💻</a> <a href="#business-TonyBagnall" title="Business development">💼</a> <a href="https://github.com/sktime/sktime/commits?author=TonyBagnall" title="Documentation">📖</a> <a href="#design-TonyBagnall" title="Design">🎨</a> <a href="#eventOrganizing-TonyBagnall" title="Event Organizing">📋</a> <a href="#fundingFinding-TonyBagnall" title="Funding Finding">🔍</a> <a href="#ideas-TonyBagnall" title="Ideas, Planning, & Feedback">🤔</a> <a href="#projectManagement-TonyBagnall" title="Project Management">📆</a> <a href="#question-TonyBagnall" title="Answering Questions">💬</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3ATonyBagnall" title="Reviewed Pull Requests">👀</a> <a href="#talk-TonyBagnall" title="Talks">📢</a> <a href="#data-TonyBagnall" title="Data">🔣</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="http://www.timeseriesclassification.com"><img src="https://avatars1.githubusercontent.com/u/9594042?v=4?s=100" width="100px;" alt="Tony Bagnall"/><br /><sub><b>Tony Bagnall</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=TonyBagnall" title="Code">💻</a> <a href="#business-TonyBagnall" title="Business development">💼</a> <a href="https://github.com/sktime/sktime/commits?author=TonyBagnall" title="Documentation">📖</a> <a href="#design-TonyBagnall" title="Design">🎨</a> <a href="#eventOrganizing-TonyBagnall" title="Event Organizing">📋</a> <a href="#fundingFinding-TonyBagnall" title="Funding Finding">🔍</a> <a href="#ideas-TonyBagnall" title="Ideas, Planning, & Feedback">🤔</a> <a href="#projectManagement-TonyBagnall" title="Project Management">📆</a> <a href="#question-TonyBagnall" title="Answering Questions">💬</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3ATonyBagnall" title="Reviewed Pull Requests">👀</a> <a href="#talk-TonyBagnall" title="Talks">📢</a> <a href="#data-TonyBagnall" title="Data">🔣</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/utsavcoding"><img src="https://avatars3.githubusercontent.com/u/55446385?v=4?s=100" width="100px;" alt="Utsav Kumar Tiwari"/><br /><sub><b>Utsav Kumar Tiwari</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=utsavcoding" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=utsavcoding" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Vasudeva-bit"><img src="https://avatars.githubusercontent.com/u/70791259?v=4?s=100" width="100px;" alt="Vasudeva Kilaru"/><br /><sub><b>Vasudeva Kilaru</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Vasudeva-bit" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=Vasudeva-bit" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/victordremov"><img src="https://avatars.githubusercontent.com/u/32140716?s=100" width="100px;" alt="Viktor Dremov"/><br /><sub><b>Viktor Dremov</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=victordremov" title="Code">💻</a></td>
@@ -280,9 +281,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/VyomkeshVyas"><img src="?s=100" width="100px;" alt="Vyomkesh Vyas"/><br /><sub><b>Vyomkesh Vyas</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=VyomkeshVyas" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=VyomkeshVyas" title="Documentation">📖</a> <a href="#example-VyomkeshVyas" title="Examples">💡</a> <a href="https://github.com/sktime/sktime/commits?author=VyomkeshVyas" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/wayneadams"><img src="https://avatars.githubusercontent.com/u/15034841?s=400&u=d717e9945910bcc844c5e64cd56d570c6cc4e8e6&v=4?s=100" width="100px;" alt="Wayne Adams"/><br /><sub><b>Wayne Adams</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=wayneadams" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/templierw/"><img src="https://github.com/templierw.png?s=100" width="100px;" alt="William Templier"/><br /><sub><b>William Templier</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=templierw" title="Documentation">📖</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/magittan"><img src="https://avatars0.githubusercontent.com/u/14024202?v=4?s=100" width="100px;" alt="William Zheng"/><br /><sub><b>William Zheng</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=magittan" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=magittan" title="Tests">⚠️</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/magittan"><img src="https://avatars0.githubusercontent.com/u/14024202?v=4?s=100" width="100px;" alt="William Zheng"/><br /><sub><b>William Zheng</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=magittan" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=magittan" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/yairbeer"><img src="https://avatars.githubusercontent.com/yairbeer?s=100" width="100px;" alt="Yair Beer"/><br /><sub><b>Yair Beer</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=yairbeer" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/yann-hallouard/"><img src="https://avatars.githubusercontent.com/YHallouard?s=100" width="100px;" alt="Yann Hallouard"/><br /><sub><b>Yann Hallouard</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=YHallouard" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=YHallouard" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/yashlamba"><img src="https://avatars.githubusercontent.com/u/44164398?v=4?s=100" width="100px;" alt="Yash Lamba"/><br /><sub><b>Yash Lamba</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=yashlamba" title="Code">💻</a></td>
@@ -291,9 +292,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/ZiyaoWei"><img src="https://avatars.githubusercontent.com/u/940823?v=4?s=100" width="100px;" alt="Ziyao Wei"/><br /><sub><b>Ziyao Wei</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ZiyaoWei" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/aa25desh"><img src="https://avatars1.githubusercontent.com/u/29518290?v=4?s=100" width="100px;" alt="aa25desh"/><br /><sub><b>aa25desh</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=aa25desh" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Aaa25desh" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/abandus"><img src="https://avatars2.githubusercontent.com/u/46486474?v=4?s=100" width="100px;" alt="abandus"/><br /><sub><b>abandus</b></sub></a><br /><a href="#ideas-abandus" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sktime/sktime/commits?author=abandus" title="Code">💻</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/adoherty21"><img src="https://avatars.githubusercontent.com/u/52799751?s=400&v=4?s=100" width="100px;" alt="adoherty21"/><br /><sub><b>adoherty21</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Aadoherty21" title="Bug reports">🐛</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/adoherty21"><img src="https://avatars.githubusercontent.com/u/52799751?s=400&v=4?s=100" width="100px;" alt="adoherty21"/><br /><sub><b>adoherty21</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Aadoherty21" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/bethrice44"><img src="https://avatars.githubusercontent.com/u/11226988?v=4?s=100" width="100px;" alt="bethrice44"/><br /><sub><b>bethrice44</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Abethrice44" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=bethrice44" title="Code">💻</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3Abethrice44" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sktime/sktime/commits?author=bethrice44" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/big-o"><img src="https://avatars1.githubusercontent.com/u/1134151?v=4?s=100" width="100px;" alt="big-o"/><br /><sub><b>big-o</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=big-o" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=big-o" title="Tests">⚠️</a> <a href="#design-big-o" title="Design">🎨</a> <a href="#ideas-big-o" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3Abig-o" title="Reviewed Pull Requests">👀</a> <a href="#tutorial-big-o" title="Tutorials">✅</a> <a href="#mentoring-big-o" title="Mentoring">🧑‍🏫</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/bobbys-dev"><img src="https://avatars.githubusercontent.com/bobbys-dev?s=100" width="100px;" alt="bobbys"/><br /><sub><b>bobbys</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=bobbys-dev" title="Code">💻</a></td>
@@ -302,9 +303,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/chizzi25"><img src="https://avatars3.githubusercontent.com/u/67911243?v=4?s=100" width="100px;" alt="chizzi25"/><br /><sub><b>chizzi25</b></sub></a><br /><a href="#blog-chizzi25" title="Blogposts">📝</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/chrisholder"><img src="https://avatars.githubusercontent.com/u/4674372?v=4?s=100" width="100px;" alt="chrisholder"/><br /><sub><b>chrisholder</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Tests">⚠️</a> <a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Documentation">📖</a> <a href="#design-chrisholder" title="Design">🎨</a> <a href="#example-chrisholder" title="Examples">💡</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/danbartl"><img src="https://avatars.githubusercontent.com/u/19947407?v=4?s=100" width="100px;" alt="danbartl"/><br /><sub><b>danbartl</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Adanbartl" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=danbartl" title="Code">💻</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3Adanbartl" title="Reviewed Pull Requests">👀</a> <a href="#talk-danbartl" title="Talks">📢</a> <a href="https://github.com/sktime/sktime/commits?author=danbartl" title="Tests">⚠️</a> <a href="#tutorial-danbartl" title="Tutorials">✅</a> <a href="#video-danbartl" title="Videos">📹</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/hamzahiqb"><img src="https://avatars3.githubusercontent.com/u/10302415?v=4?s=100" width="100px;" alt="hamzahiqb"/><br /><sub><b>hamzahiqb</b></sub></a><br /><a href="#infra-hamzahiqb" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/hamzahiqb"><img src="https://avatars3.githubusercontent.com/u/10302415?v=4?s=100" width="100px;" alt="hamzahiqb"/><br /><sub><b>hamzahiqb</b></sub></a><br /><a href="#infra-hamzahiqb" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/hiqbal2"><img src="https://avatars3.githubusercontent.com/u/10302415?v=4?s=100" width="100px;" alt="hiqbal2"/><br /><sub><b>hiqbal2</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=hiqbal2" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/jesellier"><img src="https://avatars0.githubusercontent.com/u/51952076?v=4?s=100" width="100px;" alt="jesellier"/><br /><sub><b>jesellier</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=jesellier" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/jschemm"><img src="https://avatars.githubusercontent.com/u/81151346?v=4?s=100" width="100px;" alt="jschemm"/><br /><sub><b>jschemm</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=jschemm" title="Code">💻</a></td>
@@ -313,9 +314,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/oleskiewicz"><img src="https://avatars1.githubusercontent.com/u/5682158?v=4?s=100" width="100px;" alt="oleskiewicz"/><br /><sub><b>oleskiewicz</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/pabworks"><img src="https://avatars.githubusercontent.com/u/32725127?v=4?s=100" width="100px;" alt="pabworks"/><br /><sub><b>pabworks</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=pabworks" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=pabworks" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/xloem"><img src="?s=100" width="100px;" alt="patiently pending world peace"/><br /><sub><b>patiently pending world peace</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=xloem" title="Code">💻</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/raishubham1"><img src="https://avatars3.githubusercontent.com/u/29356417?v=4?s=100" width="100px;" alt="raishubham1"/><br /><sub><b>raishubham1</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=raishubham1" title="Documentation">📖</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/raishubham1"><img src="https://avatars3.githubusercontent.com/u/29356417?v=4?s=100" width="100px;" alt="raishubham1"/><br /><sub><b>raishubham1</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=raishubham1" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/simone-pignotti"><img src="https://avatars1.githubusercontent.com/u/44410066?v=4?s=100" width="100px;" alt="simone-pignotti"/><br /><sub><b>simone-pignotti</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=simone-pignotti" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Asimone-pignotti" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/sophijka"><img src="https://avatars2.githubusercontent.com/u/47450591?v=4?s=100" width="100px;" alt="sophijka"/><br /><sub><b>sophijka</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=sophijka" title="Documentation">📖</a> <a href="#maintenance-sophijka" title="Maintenance">🚧</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/sri1419"><img src="https://avatars2.githubusercontent.com/u/65078278?v=4?s=100" width="100px;" alt="sri1419"/><br /><sub><b>sri1419</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=sri1419" title="Code">💻</a></td>
diff --git a/docs/source/api_reference/forecasting.rst b/docs/source/api_reference/forecasting.rst
index ec171bed8..ee546ad03 100644
--- a/docs/source/api_reference/forecasting.rst
+++ b/docs/source/api_reference/forecasting.rst
@@ -52,6 +52,7 @@ Pipelines can also be constructed using ``*``, ``+``, and ``|`` dunders.
     HierarchyEnsembleForecaster
     FhPlexForecaster
     IgnoreX
+    FallbackForecaster
 
 Reduction
 ---------
diff --git a/extension_templates/forecasting.py b/extension_templates/forecasting.py
index 06375c9e8..532210ac2 100644
--- a/extension_templates/forecasting.py
+++ b/extension_templates/forecasting.py
@@ -183,7 +183,7 @@ class MyForecaster(BaseForecaster):
         #
         # soft dependency requirement
         "python_dependencies": None,
-        # valid values: str or list of str
+        # valid values: str or list of str, PEP 440 valid package version specifiers
         # raises exception at construction if modules at strings cannot be imported
     }
     #  in case of inheritance, concrete class should typically set tags
diff --git a/extension_templates/param_est.py b/extension_templates/param_est.py
index afccdedbf..975a87e9c 100644
--- a/extension_templates/param_est.py
+++ b/extension_templates/param_est.py
@@ -133,7 +133,7 @@ class MyTimeSeriesParamFitter(BaseParamFitter):
         #
         # soft dependency requirement
         "python_dependencies": None,
-        # valid values: str or list of str
+        # valid values: str or list of str, PEP 440 valid package version specifiers
         # raises exception at construction if modules at strings cannot be imported
     }
     #  in case of inheritance, concrete class should typically set tags
diff --git a/extension_templates/split.py b/extension_templates/split.py
index 42a95e36a..a3679c806 100644
--- a/extension_templates/split.py
+++ b/extension_templates/split.py
@@ -119,7 +119,7 @@ class MySplitter(BaseSplitter):
         #
         # soft dependency requirement
         "python_dependencies": None,
-        # valid values: str or list of str
+        # valid values: str or list of str, PEP 440 valid package version specifiers
         # raises exception at construction if modules at strings cannot be imported
     }
 
diff --git a/extension_templates/transformer.py b/extension_templates/transformer.py
index e2aced2ea..5fccc7288 100644
--- a/extension_templates/transformer.py
+++ b/extension_templates/transformer.py
@@ -270,7 +270,7 @@ class MyTransformer(BaseTransformer):
         #
         # soft dependency requirement
         "python_dependencies": None,
-        # valid values: str or list of str
+        # valid values: str or list of str, PEP 440 valid package version specifiers
         # raises exception at construction if modules at strings cannot be imported
     }
     # in case of inheritance, concrete class should typically set tags
diff --git a/sktime/forecasting/compose/__init__.py b/sktime/forecasting/compose/__init__.py
index f8d522634..30b0c1971 100644
--- a/sktime/forecasting/compose/__init__.py
+++ b/sktime/forecasting/compose/__init__.py
@@ -6,6 +6,7 @@ __all__ = [
     "HierarchyEnsembleForecaster",
     "ColumnEnsembleForecaster",
     "EnsembleForecaster",
+    "FallbackForecaster",
     "AutoEnsembleForecaster",
     "TransformedTargetForecaster",
     "ForecastingPipeline",
@@ -36,6 +37,7 @@ from sktime.forecasting.compose._ensemble import (
     AutoEnsembleForecaster,
     EnsembleForecaster,
 )
+from sktime.forecasting.compose._fallback import FallbackForecaster
 from sktime.forecasting.compose._fhplex import FhPlexForecaster
 from sktime.forecasting.compose._grouped import ForecastByLevel
 from sktime.forecasting.compose._hierarchy_ensemble import HierarchyEnsembleForecaster
diff --git a/sktime/forecasting/compose/_fallback.py b/sktime/forecasting/compose/_fallback.py
new file mode 100644
index 000000000..5e67aef81
--- /dev/null
+++ b/sktime/forecasting/compose/_fallback.py
@@ -0,0 +1,254 @@
+#!/usr/bin/env python3 -u
+# copyright: sktime developers, BSD-3-Clause License (see LICENSE file).
+"""
+Implements the FallbackForecaster.
+
+The FallbackForecaster works with a list of forecasters and tries to fit them in order.
+If the active forecaster fails during prediction, it proceeds to the next. This ensures
+a robust forecasting mechanism by providing fallback options.
+"""
+
+__author__ = ["ninedigits"]
+__all__ = ["FallbackForecaster"]
+
+from sktime.base import _HeterogenousMetaEstimator
+from sktime.datatypes import ALL_TIME_SERIES_MTYPES
+from sktime.forecasting.base import BaseForecaster
+from sktime.utils.warnings import warn
+
+
+class FallbackForecaster(_HeterogenousMetaEstimator, BaseForecaster):
+    """Forecaster that sequentially tries a list of forecasting models.
+
+    Attempts to fit the provided forecasters in the order they are given. If a
+    forecaster fails during fitting or prediction, it proceeds to the next one. This
+    class is useful in scenarios where the reliability of individual forecasting models
+    may be in question, and a fallback mechanism is desired.
+
+    Parameters
+    ----------
+    forecasters : list of forecasters, or
+        list of tuples (str, estimator) of sktime forecasters
+        Forecasters to be tried sequentially.
+        These are "blueprint" transformers resp forecasters,
+        forecaster states do not change when `fit` is called
+
+    warn : bool, default=False
+        If True, raises warnings when a forecaster fails to fit or predict.
+
+    Attributes
+    ----------
+    forecasters_ : list of (str, estimator) tuples
+        The forecasters to be tried sequentially.
+        Forecasters that have been fitted successfully are stored in this list.
+    first_nonfailing_forecaster_index_ : int
+        Index of the first non-failing forecaster in the list of forecasters.
+    current_forecaster_ : sktime forecaster
+        pointer to the first forecaster that was successfully fitted
+        same as ``forecasters_[first_nonfailing_forecaster_index_][1]``
+    current_name_ : str
+        name of the current forecaster
+        same as ``forecasters_[first_nonfailing_forecaster_index_][0]``
+    exceptions_raised_ : dict
+        dictionary of exceptions raised by forecasters during fitting or prediction
+        keys are int indices of forecasters in the list of forecasters
+        values are dicts with keys "failed_at_step", "exception", "forecaster_name"
+        failed_at_step is either "fit" or "predict"
+        exception is the exception raised by the forecaster
+        forecaster_name is the name of the forecaster
+
+    Examples
+    --------
+    >>> from sktime.forecasting.naive import NaiveForecaster
+    >>> from sktime.forecasting.compose import FallbackForecaster
+    >>> from sktime.forecasting.compose import EnsembleForecaster
+    >>> from sktime.forecasting.trend import PolynomialTrendForecaster
+    >>> from sktime.datasets import load_airline
+    >>> y = load_airline()
+    >>> # first fit polyomial trend, if fails make naive forecast
+    >>> forecasters = [
+    ...     ("poly", PolynomialTrendForecaster()),
+    ...     ("naive", NaiveForecaster())
+    ... ]
+    >>> forecaster = FallbackForecaster(forecasters=forecasters)
+    >>> forecaster.fit(y=y, fh=[1, 2, 3])
+    FallbackForecaster(...)
+    >>> y_pred = forecaster.predict()
+    """
+
+    _tags = {
+        "authors": ["ninedigits"],
+        "maintainers": ["ninedigits"],
+        "handles-missing-data": True,
+        "scitype:y": "both",
+        "y_inner_mtype": ALL_TIME_SERIES_MTYPES,
+        "X_inner_mtype": ALL_TIME_SERIES_MTYPES,
+        "fit_is_empty": False,
+    }
+
+    # for default get_params/set_params from _HeterogenousMetaEstimator
+    # _steps_attr points to the attribute of self
+    # which contains the heterogeneous set of estimators
+    # this must be an iterable of (name: str, estimator, ...) tuples for the default
+    _steps_attr = "_forecasters"
+    # if the estimator is fittable, _HeterogenousMetaEstimator also
+    # provides an override for get_fitted_params for params from the fitted estimators
+    # the fitted estimators should be in a different attribute, _steps_fitted_attr
+    # this must be an iterable of (name: str, estimator, ...) tuples for the default
+    _steps_fitted_attr = "forecasters_"
+
+    def __init__(self, forecasters, verbose=False):
+        super().__init__()
+
+        self.forecasters = forecasters
+        self.current_forecaster_ = None
+        self.current_name_ = None
+        self.verbose = verbose
+
+        self._forecasters = self._check_estimators(
+            forecasters, "forecasters", clone_ests=False
+        )
+        self.forecasters_ = self._check_estimators(forecasters, "forecasters")
+
+        self._anytagis_then_set("requires-fh-in-fit", True, False, self._forecasters)
+
+    def _fit(self, y, X=None, fh=None):
+        """Fit the forecasters in the given order until one succeeds.
+
+        Parameters
+        ----------
+        y : array-like
+            Target time series to which to fit the forecasters.
+        X : array-like, optional (default=None)
+            Exogenous variables.
+        fh : array-like, optional (default=None)
+            The forecasting horizon.
+
+        Returns
+        -------
+        self : an instance of self
+
+        Raises
+        ------
+        RuntimeError
+            If all forecasters fail to fit.
+        """
+        self.first_nonfailing_forecaster_index_ = 0
+        self.exceptions_raised_ = dict()
+        return self._try_fit_forecasters(y=y, X=X, fh=fh)
+
+    def _try_fit_forecasters(self, y, X, fh):
+        """
+        Attempt to fit the forecasters in sequence until one succeeds.
+
+        This method iterates over the forecasters starting from the index
+        `first_nonfailing_forecaster_index_`. For each forecaster, it tries to fit it
+        with the current data. If the fit method of a forecaster raises an exception,
+        it records the exception and proceeds to the next forecaster. If a forecaster
+        fits successfully, it updates the current forecaster and its name.
+
+        Returns
+        -------
+        self : an instance of self
+
+        Raises
+        ------
+        RuntimeError
+            If all forecasters fail to fit.
+        """
+        while True:
+            ix = self.first_nonfailing_forecaster_index_
+            if self.first_nonfailing_forecaster_index_ >= len(self.forecasters_):
+                raise RuntimeError("No remaining forecasters to attempt prediction.")
+            name, forecaster = self.forecasters_[ix]
+            try:
+                self.current_name_ = name
+                self.current_forecaster_ = forecaster.clone()
+                self.current_forecaster_.fit(y=y, X=X, fh=fh)
+                return self
+            except Exception as e:
+                self.exceptions_raised_[self.first_nonfailing_forecaster_index_] = {
+                    "failed_at_step": "fit",
+                    "exception": e,
+                    "forecaster_name": name,
+                }
+                self.first_nonfailing_forecaster_index_ += 1
+                if self.verbose:
+                    warn(
+                        f"Forecaster {name} failed to fit with error: {e}",
+                        stacklevel=2,
+                        obj=self,
+                    )
+
+    def _predict(self, fh, X=None):
+        """Predict using the current forecaster.
+
+        If predict fails, fit and predict with the next forecaster.
+
+        Parameters
+        ----------
+        fh : array-like
+            The forecasting horizon.
+        X : array-like, optional (default=None)
+            Exogenous variables.
+
+        Returns
+        -------
+        y_pred : array-like
+            The predicted values.
+
+        Raises
+        ------
+        RuntimeError
+            If no forecaster is fitted or all forecasters fail to predict.
+        """
+        if self.current_forecaster_ is None:
+            raise RuntimeError("No forecaster has been successfully fitted yet.")
+
+        try:
+            return self.current_forecaster_.predict(fh, X)
+        except Exception as e:
+            self.exceptions_raised_[self.first_nonfailing_forecaster_index_] = {
+                "failed_at_step": "fit",
+                "exception": e,
+                "forecaster_name": self.current_name_,
+            }
+            if self.verbose:
+                warn(
+                    f"Current forecaster failed at prediction with error: {e}",
+                    stacklevel=2,
+                    obj=self,
+                )
+            self.first_nonfailing_forecaster_index_ += 1
+
+            # Fit the next forecaster and retry prediction
+            self.current_forecaster_ = None
+            self._try_fit_forecasters(self._y, self._X, self._fh)
+            return self.predict(fh, X)
+
+    @classmethod
+    def get_test_params(cls, parameter_set="default"):
+        """Return testing parameter settings for the estimator.
+
+        Parameters
+        ----------
+        parameter_set : str, default="default"
+            Name of the set of test parameters to return, for use in tests. If no
+            special parameters are defined for a value, will return `"default"` set.
+
+        Returns
+        -------
+        params : dict or list of dict
+        """
+        from sktime.forecasting.compose._reduce import YfromX
+        from sktime.forecasting.naive import NaiveForecaster
+
+        # univariate case
+        FORECASTER = NaiveForecaster()
+        params = [{"forecasters": [("f1", FORECASTER), ("f2", FORECASTER)]}]
+
+        # test multivariate case, i.e., ensembling multiple variables at same time
+        FORECASTER2 = YfromX.create_test_instance()
+        params = params + [{"forecasters": [FORECASTER2, FORECASTER]}]
+
+        return params


Test patch:
diff --git a/sktime/forecasting/compose/tests/test_fallback.py b/sktime/forecasting/compose/tests/test_fallback.py
new file mode 100644
index 000000000..92e69f922
--- /dev/null
+++ b/sktime/forecasting/compose/tests/test_fallback.py
@@ -0,0 +1,496 @@
+#!/usr/bin/env python3 -u
+# copyright: sktime developers, BSD-3-Clause License (see LICENSE file).
+"""Unit tests for the FallbackForecaster functionality.
+
+Tests cover the basic operations of the FallbackForecaster, ensuring proper
+functionality of fitting, predicting, updating, and handling of errors in the
+forecasting process.
+"""
+
+__author__ = ["ninedigits"]
+
+import pandas as pd
+import pytest
+
+from sktime.base import _HeterogenousMetaEstimator
+from sktime.forecasting.base._base import BaseForecaster
+from sktime.forecasting.compose import EnsembleForecaster, FallbackForecaster
+from sktime.forecasting.naive import NaiveForecaster
+from sktime.forecasting.trend import PolynomialTrendForecaster
+from sktime.utils._testing.forecasting import make_forecasting_problem
+
+
+class ForecastingError(Exception):
+    """Exception raised for errors in the forecasting process.
+
+    Attributes
+    ----------
+    message : str
+        Explanation of the error.
+
+    Methods
+    -------
+    __init__(message)
+        Constructs the ForecastingError with the provided message."""
+
+    def __init__(self, message):
+        self.message = message
+        super().__init__(message)
+
+
+class DummyForecaster(_HeterogenousMetaEstimator, BaseForecaster):
+    """Dummy forecaster used for testing the FallbackForecaster.
+
+    This forecaster is intentionally designed to fail at specified stages of the
+    forecasting process (fit, predict, or update) to test the robustness and fallback
+    mechanisms of the FallbackForecaster.
+
+    Parameters
+    ----------
+    raise_at : str
+        Stage at which the forecaster should fail. Options are "fit", "predict",
+        "update".
+
+    Raises
+    ------
+    AttributeError
+        If `raise_at` is not one of the valid options.
+    """
+
+    def __init__(self, raise_at="fit"):
+        super().__init__()
+        __valid__ = ["fit", "predict", "update"]
+        if raise_at not in __valid__:
+            raise AttributeError(f"`raise_at` must choose from {__valid__}")
+        self.forecaster = NaiveForecaster()
+        self.raise_at = raise_at
+        self._is_fitted = False
+
+    def _fit(self, y, X=None, fh=None):
+        """Fit to training data. Optionally fail here."""
+        if self.raise_at == "fit":
+            raise ForecastingError("Intentional failure in fit.")
+        self.forecaster.fit(y, X, fh)
+        self._is_fitted = True
+        return self
+
+    def _predict(self, fh, X=None):
+        """Make predictions. Optionally fail here."""
+        if not self._is_fitted:
+            raise ForecastingError("The forecaster is not fitted yet.")
+        if self.raise_at == "predict":
+            raise ForecastingError("Intentional failure in predict.")
+        return self.forecaster.predict(fh, X)
+
+    def _update(self, y, X=None, update_params=True):
+        """Update the forecaster. Optionally fail here."""
+        if self.raise_at == "update":
+            raise ForecastingError("Intentional failure in update.")
+        self.forecaster.update(y, X, update_params)
+        return self
+
+
+def test_raises_at_fit():
+    """Test dummy forecaster raises at fit"""
+    # Start with negative time series, Theta model will fail here
+    y = make_forecasting_problem(random_state=42)
+    forecaster = DummyForecaster(raise_at="fit")
+    with pytest.raises(ForecastingError):
+        forecaster.fit(y=y, fh=[1, 2, 3])
+
+
+def test_raises_at_predict():
+    """Test dummy forecaster raises at predict"""
+    # Start with negative time series, Theta model will fail here
+    y = make_forecasting_problem(random_state=42)
+    forecaster = DummyForecaster(raise_at="predict")
+    forecaster.fit(y=y, fh=[1, 2, 3])
+    with pytest.raises(ForecastingError):
+        forecaster.predict()
+
+
+def test_raises_at_update():
+    """Test dummy forecaster raises at update"""
+    # Start with negative time series, Theta model will fail here
+    y = make_forecasting_problem(random_state=42)
+    forecaster = DummyForecaster(raise_at="update")
+    forecaster.fit(y=y, fh=[1, 2, 3])
+    forecaster.predict()
+    with pytest.raises(ForecastingError):
+        forecaster.update(y)
+
+
+def test_fallbackforecaster_fails_at_fit():
+    """Test FallbackForecaster fails at first fit, second forecaster succeeds"""
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("fail_fit", DummyForecaster(raise_at="fit")),
+        ]
+    )
+    forecaster2 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("naive", NaiveForecaster()),
+        ]
+    )
+    forecaster = FallbackForecaster(
+        [
+            (
+                "ensemble1_fails_at_fit",
+                forecaster1,
+            ),
+            (
+                "ensemble2",
+                forecaster2,
+            ),
+        ]
+    )
+    forecaster.fit(y=y, fh=[1, 2, 3])
+    y_pred_actual = forecaster.predict()
+
+    forecaster2.fit(y=y, fh=[1, 2, 3])
+    y_pred_expected = forecaster2.predict()
+
+    # Assert that the first valid forecaster is trained
+    name = forecaster.current_name_
+    assert name == "ensemble2"
+
+    # Assert that the first valid forecaster produces the same results as it would
+    # on its own
+    pd.testing.assert_series_equal(y_pred_expected, y_pred_actual)
+
+    # Count the number of exceptions raised
+    exceptions_raised = forecaster.exceptions_raised_
+    assert len(exceptions_raised) == 1
+
+
+def test_fallbackforecaster_fails_at_predict():
+    """Test FallbackForecaster fails at predict, second forecaster succeeds"""
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("raise_at_predict", DummyForecaster(raise_at="predict")),
+        ]
+    )
+    forecaster2 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("naive", NaiveForecaster()),
+        ]
+    )
+
+    forecaster = FallbackForecaster(
+        [
+            ("forecaster1_fails_predict", forecaster1),
+            ("forecaster2_succeeded", forecaster2),
+        ]
+    )
+    forecaster.fit(y=y, fh=[1, 2, 3])
+
+    # Assert predictions line up with the correct forecaster
+    y_pred_actual = forecaster.predict()
+
+    forecaster2.fit(y=y, fh=[1, 2, 3])
+    y_pred_expected = forecaster2.predict()
+
+    # Assert correct forecaster name
+    name = forecaster.current_name_
+    assert name == "forecaster2_succeeded"
+
+    # Assert correct y_pred
+    pd.testing.assert_series_equal(y_pred_expected, y_pred_actual)
+
+
+def test_fallbackforecaster_fails_twice():
+    """First two FallbackForecasters fail, third succeeds"""
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("fails", DummyForecaster(raise_at="fit")),
+        ]
+    )
+    forecaster2 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("fails", DummyForecaster(raise_at="predict")),
+        ]
+    )
+    forecaster3 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("naive", NaiveForecaster()),
+        ]
+    )
+    forecaster4 = PolynomialTrendForecaster()
+
+    forecaster = FallbackForecaster(
+        [
+            ("forecaster1_fails_fit", forecaster1),
+            ("forecaster2_fails_prd", forecaster2),
+            ("forecaster3_succeeded", forecaster3),
+            ("forecaster4_notcalled", forecaster4),
+        ]
+    )
+    forecaster.fit(y=y, fh=[1, 2, 3])
+    y_pred_actual = forecaster.predict()
+
+    forecaster3.fit(y=y, fh=[1, 2, 3])
+    y_pred_expected = forecaster3.predict()
+
+    # Assert correct forecaster name
+    name = forecaster.current_name_
+    assert name == "forecaster3_succeeded"
+
+    # Assert correct y_pred
+    pd.testing.assert_series_equal(y_pred_expected, y_pred_actual)
+
+    # Assert correct number of expected exceptions
+    exceptions_raised = forecaster.exceptions_raised_
+    assert len(exceptions_raised) == 2
+
+    # Assert the correct forecasters failed
+    names_raised_actual = [
+        vals["forecaster_name"] for vals in exceptions_raised.values()
+    ]
+    names_raised_expected = ["forecaster1_fails_fit", "forecaster2_fails_prd"]
+    assert names_raised_actual == names_raised_expected
+
+
+def test_fallbackforecaster_fails_fit_twice():
+    """First two FallbackForecasters fail at fit step, third forecaster succeeds"""
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("fails", DummyForecaster(raise_at="fit")),
+        ]
+    )
+    forecaster2 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("fails", DummyForecaster(raise_at="fit")),
+        ]
+    )
+    forecaster3 = EnsembleForecaster(
+        [
+            ("trend", PolynomialTrendForecaster()),
+            ("naive", NaiveForecaster()),
+        ]
+    )
+    forecaster4 = PolynomialTrendForecaster()
+
+    forecaster = FallbackForecaster(
+        [
+            ("forecaster1_fails_fit", forecaster1),
+            ("forecaster2_fails_fit", forecaster2),
+            ("forecaster3_succeeded", forecaster3),
+            ("forecaster4_notcalled", forecaster4),
+        ]
+    )
+    forecaster.fit(y=y, fh=[1, 2, 3])
+
+    # Assert predictions line up with the correct forecaster
+    y_pred_actual = forecaster.predict()
+
+    forecaster3.fit(y=y, fh=[1, 2, 3])
+    y_pred_expected = forecaster3.predict()
+
+    # Assert correct forecaster name
+    name = forecaster.current_name_
+    assert name == "forecaster3_succeeded"
+
+    # Assert correct y_pred
+    pd.testing.assert_series_equal(y_pred_expected, y_pred_actual)
+
+    # Assert correct number of expected exceptions
+    exceptions_raised = forecaster.exceptions_raised_
+    assert len(exceptions_raised) == 2
+
+    # Assert the correct forecasters failed
+    names_raised_actual = [
+        vals["forecaster_name"] for vals in exceptions_raised.values()
+    ]
+    names_raised_expected = ["forecaster1_fails_fit", "forecaster2_fails_fit"]
+    assert names_raised_actual == names_raised_expected
+
+
+def test_all_forecasters_fail1():
+    """All forecasters fail; predict and fit"""
+    # Start with negative time series, Theta model will fail here
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = ("raise_predict1", DummyForecaster(raise_at="predict"))
+    forecaster2 = ("raise_fit1", DummyForecaster(raise_at="fit"))
+    forecaster3 = ("raise_fit2", DummyForecaster(raise_at="fit"))
+    forecaster = FallbackForecaster([forecaster1, forecaster2, forecaster3])
+    with pytest.raises(RuntimeError):
+        forecaster.fit(y=y, fh=[1, 2, 3])
+        forecaster.predict()
+
+
+def test_all_forecasters_fail2():
+    """All forecasters fail at fit step"""
+    # Start with negative time series, Theta model will fail here
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = ("raise_fit1", DummyForecaster(raise_at="fit"))
+    forecaster2 = ("raise_fit2", DummyForecaster(raise_at="fit"))
+    forecaster3 = ("raise_fit3", DummyForecaster(raise_at="fit"))
+    forecaster = FallbackForecaster([forecaster1, forecaster2, forecaster3])
+    with pytest.raises(RuntimeError):
+        forecaster.fit(y=y, fh=[1, 2, 3])
+
+
+def test_all_forecasters_fail3():
+    """All forecasters fail at predict"""
+    # Start with negative time series, Theta model will fail here
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = ("raise_predict1", DummyForecaster(raise_at="predict"))
+    forecaster2 = ("raise_predict2", DummyForecaster(raise_at="predict"))
+    forecaster3 = ("raise_predict3", DummyForecaster(raise_at="predict"))
+    forecaster = FallbackForecaster([forecaster1, forecaster2, forecaster3])
+    with pytest.raises(RuntimeError):
+        forecaster.fit(y=y, fh=[1, 2, 3])
+        forecaster.predict()
+
+
+def test_many_forecasters_fail1():
+    """All forecasters fail at predict"""
+    # Start with negative time series, Theta model will fail here
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = ("raise_predict1", DummyForecaster(raise_at="predict"))
+    forecaster2 = ("raise_fit2", DummyForecaster(raise_at="fit"))
+    forecaster3 = ("raise_fit3", DummyForecaster(raise_at="fit"))
+    forecaster4 = ("raise_predict4", DummyForecaster(raise_at="predict"))
+    forecaster5 = ("forecaster5", PolynomialTrendForecaster())
+    forecaster = FallbackForecaster(
+        [forecaster1, forecaster2, forecaster3, forecaster4, forecaster5]
+    )
+    forecaster.fit(y, fh=[1, 2, 3])
+    y_pred_actual = forecaster.predict()
+    y_name_actual = forecaster.current_name_
+    expected_forecaster = forecaster5[1]
+    expected_forecaster.fit(y, fh=[1, 2, 3])
+    y_pred_expected = expected_forecaster.predict()
+    y_name_expected = forecaster5[0]
+    pd.testing.assert_series_equal(y_pred_actual, y_pred_expected)
+    assert y_name_actual == y_name_expected
+
+    # Assert correct forecaster name
+    name = forecaster.current_name_
+    assert name == "forecaster5"
+
+    # Assert correct y_pred
+    pd.testing.assert_series_equal(y_pred_expected, y_pred_actual)
+
+    # Assert correct number of expected exceptions
+    exceptions_raised = forecaster.exceptions_raised_
+    assert len(exceptions_raised) == 4
+
+    # Assert the correct forecasters failed
+    names_raised_actual = [
+        vals["forecaster_name"] for vals in exceptions_raised.values()
+    ]
+    names_raised_expected = [
+        "raise_predict1",
+        "raise_fit2",
+        "raise_fit3",
+        "raise_predict4",
+    ]
+    assert names_raised_actual == names_raised_expected
+
+
+def test_fallbackforecaster_fails_twice_simple():
+    """First two FallbackForecasters fail, third succeeds"""
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = DummyForecaster(raise_at="fit")
+    forecaster2 = DummyForecaster(raise_at="predict")
+    forecaster3 = PolynomialTrendForecaster()
+    forecaster4 = NaiveForecaster()
+
+    forecaster = FallbackForecaster(
+        [
+            ("forecaster1_fails_fit", forecaster1),
+            ("forecaster2_fails_prd", forecaster2),
+            ("forecaster3_succeeded", forecaster3),
+            ("forecaster4_notcalled", forecaster4),
+        ]
+    )
+    forecaster.fit(y=y, fh=[1, 2, 3])
+    y_pred_actual = forecaster.predict()
+
+    forecaster3.fit(y=y, fh=[1, 2, 3])
+    y_pred_expected = forecaster3.predict()
+
+    # Assert correct forecaster name
+    name = forecaster.current_name_
+    assert name == "forecaster3_succeeded"
+
+    # Assert correct y_pred
+    pd.testing.assert_series_equal(y_pred_expected, y_pred_actual)
+
+    # Assert correct number of expected exceptions
+    exceptions_raised = forecaster.exceptions_raised_
+    assert len(exceptions_raised) == 2
+
+    # Assert the correct forecasters failed
+    names_raised_actual = [
+        vals["forecaster_name"] for vals in exceptions_raised.values()
+    ]
+    names_raised_expected = ["forecaster1_fails_fit", "forecaster2_fails_prd"]
+    assert names_raised_actual == names_raised_expected
+
+
+def test_fallbackforecaster_fails_many_simple():
+    """First two FallbackForecasters fail, third succeeds"""
+    y = make_forecasting_problem(random_state=42)
+    forecaster1 = DummyForecaster(raise_at="predict")
+    forecaster2 = DummyForecaster(raise_at="predict")
+    forecaster3 = DummyForecaster(raise_at="fit")
+    forecaster4 = DummyForecaster(raise_at="fit")
+    forecaster5 = DummyForecaster(raise_at="fit")
+    forecaster6 = DummyForecaster(raise_at="predict")
+    forecaster7 = DummyForecaster(raise_at="fit")
+    forecaster8 = DummyForecaster(raise_at="predict")
+    forecaster9 = PolynomialTrendForecaster()
+    forecaster10 = NaiveForecaster()
+
+    forecaster = FallbackForecaster(
+        [
+            ("f1", forecaster1),
+            ("f2", forecaster2),
+            ("f3", forecaster3),
+            ("f4", forecaster4),
+            ("f5", forecaster5),
+            ("f6", forecaster6),
+            ("f7", forecaster7),
+            ("f8", forecaster8),
+            ("target", forecaster9),
+            ("notcalled", forecaster10),
+        ]
+    )
+    forecaster.fit(y=y, fh=[1, 2, 3])
+    y_pred_actual = forecaster.predict()
+
+    forecaster9.fit(y=y, fh=[1, 2, 3])
+    y_pred_expected = forecaster9.predict()
+
+    # Assert correct forecaster name
+    name = forecaster.current_name_
+    assert name == "target"
+
+    # Assert correct y_pred
+    pd.testing.assert_series_equal(y_pred_expected, y_pred_actual)
+
+    # Assert correct number of expected exceptions
+    exceptions_raised = forecaster.exceptions_raised_
+    assert len(exceptions_raised) == 8
+
+    # Assert the correct forecasters failed
+    names_raised_actual = [
+        vals["forecaster_name"] for vals in exceptions_raised.values()
+    ]
+    names_raised_expected = ["f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8"]
+    assert names_raised_actual == names_raised_expected