Repository: sktime/sktime

Issue:
[BUG] `ColumnEnsembleForecaster` adds extra level to probabilistic predictions output
**Describe the bug**
When using the `predict_quantile` or `predict_interval` methods on a `ColumnEnsembleForecaster` the resulting output contains an extra irrelevant level of just `"quantile"` or `"interval"` respectively. 

**To Reproduce**
<!--
Add a Minimal, Complete, and Verifiable example (for more details, see e.g. https://stackoverflow.com/help/mcve

If the code is too long, feel free to put it in a public gist and link it in the issue: https://gist.github.com
-->

```python
from sktime.forecasting.theta import ThetaForecaster
from sktime.forecasting.compose import ColumnEnsembleForecaster

from sktime.utils._testing.series import _make_series
import numpy as np

y = _make_series()
f = ColumnEnsembleForecaster(ThetaForecaster())
fh = np.arange(5)
f.fit(y)
f.predict_quantiles(fh)
```

**Expected behavior**
A dataframe of two levels, top level names variables and second level names quantiles, instead a redundant middle level is included just saying "quantile".







Discussion:
yes, confirmed.

I think the expected behaviour is subtly different:
* in the univariate case, the first level should be removed from current result (containing "0" or similar)
* in the strictly multivariate case, the second level should be removed (containign "interval" or "coverage")

(both compared to current behaviour)

Resolving patch:
diff --git a/sktime/forecasting/compose/_column_ensemble.py b/sktime/forecasting/compose/_column_ensemble.py
--- a/sktime/forecasting/compose/_column_ensemble.py
+++ b/sktime/forecasting/compose/_column_ensemble.py
@@ -230,7 +230,12 @@ def _predict_quantiles(self, fh=None, X=None, alpha=None):
             Row index is fh. Entries are quantile forecasts, for var in col index,
                 at quantile probability in second-level col index, for each row index.
         """
-        return self._by_column("predict_quantiles", fh=fh, X=X, alpha=alpha)
+        out = self._by_column("predict_quantiles", fh=fh, X=X, alpha=alpha)
+        if len(out.columns.get_level_values(0).unique()) == 1:
+            out.columns = out.columns.droplevel(level=0)
+        else:
+            out.columns = out.columns.droplevel(level=1)
+        return out
 
     def _predict_interval(self, fh=None, X=None, coverage=None):
         """Compute/return prediction quantiles for a forecast.
@@ -268,7 +273,12 @@ def _predict_interval(self, fh=None, X=None, coverage=None):
                 Upper/lower interval end forecasts are equivalent to
                 quantile forecasts at alpha = 0.5 - c/2, 0.5 + c/2 for c in coverage.
         """
-        return self._by_column("predict_interval", fh=fh, X=X, coverage=coverage)
+        out = self._by_column("predict_interval", fh=fh, X=X, coverage=coverage)
+        if len(out.columns.get_level_values(0).unique()) == 1:
+            out.columns = out.columns.droplevel(level=0)
+        else:
+            out.columns = out.columns.droplevel(level=1)
+        return out
 
     def _predict_var(self, fh, X=None, cov=False):
         """Forecast variance at future horizon.
@@ -391,7 +401,9 @@ def get_test_params(cls, parameter_set="default"):
         """
         # imports
         from sktime.forecasting.naive import NaiveForecaster
+        from sktime.forecasting.theta import ThetaForecaster
+
+        params1 = {"forecasters": NaiveForecaster()}
+        params2 = {"forecasters": ThetaForecaster()}
 
-        FORECASTER = NaiveForecaster()
-        params = {"forecasters": FORECASTER}
-        return params
+        return [params1, params2]


Test patch: