Repository: sktime/sktime

Issue:
[BUG] issues with WindowSummarizer in hierarchical case
I think there are two bugs (or problems) with `WindowSummarizer`:

* it assumes that levels are named "instances" etc. This assumption is no longer made in the `pd-multiindex` mtype
* despite claims made in various places, it is not actually ready for hierarchical data - `X_inner_mtype` should include `pd_multiindex_hier`, but in that case the transformer will break.

FYI @danbartl 


Discussion:

Resolving patch:
diff --git a/sktime/transformations/series/summarize.py b/sktime/transformations/series/summarize.py
--- a/sktime/transformations/series/summarize.py
+++ b/sktime/transformations/series/summarize.py
@@ -203,6 +203,7 @@ class WindowSummarizer(BaseTransformer):
         "X_inner_mtype": [
             "pd-multiindex",
             "pd.DataFrame",
+            "pd_multiindex_hier",
         ],  # which mtypes do _fit/_predict support for X?
         "skip-inverse-transform": True,  # is inverse-transform skipped when called?
         "univariate-only": False,  # can the transformer handle multivariate X?
@@ -343,7 +344,8 @@ def _transform(self, X, y=None):
             bfill = False
         for cols in target_cols:
             if isinstance(X.index, pd.MultiIndex):
-                X_grouped = X.groupby("instances")[cols]
+                hier_levels = list(range(X.index.nlevels - 1))
+                X_grouped = X.groupby(level=hier_levels)[cols]
                 df = Parallel(n_jobs=self.n_jobs)(
                     delayed(_window_feature)(X_grouped, **kwargs, bfill=bfill)
                     for index, kwargs in func_dict.iterrows()


Test patch:
diff --git a/sktime/transformations/series/tests/test_window_summarizer.py b/sktime/transformations/series/tests/test_window_summarizer.py
--- a/sktime/transformations/series/tests/test_window_summarizer.py
+++ b/sktime/transformations/series/tests/test_window_summarizer.py
@@ -42,6 +42,20 @@ def check_eval(test_input, expected):
 
 y_grouped = pd.concat([y_group1, y_group2])
 
+mi = pd.MultiIndex.from_product([[0], [0], y.index], names=["h1", "h2", "time"])
+y_hier1 = pd.DataFrame(y.values, index=mi, columns=["y"])
+
+mi = pd.MultiIndex.from_product([[0], [1], y.index], names=["h1", "h2", "time"])
+y_hier2 = pd.DataFrame(y.values, index=mi, columns=["y"])
+
+mi = pd.MultiIndex.from_product([[1], [0], y.index], names=["h1", "h2", "time"])
+y_hier3 = pd.DataFrame(y.values, index=mi, columns=["y"])
+
+mi = pd.MultiIndex.from_product([[1], [1], y.index], names=["h1", "h2", "time"])
+y_hier4 = pd.DataFrame(y.values, index=mi, columns=["y"])
+
+y_hierarchical = pd.concat([y_hier1, y_hier2, y_hier3, y_hier4])
+
 y_ll, X_ll = load_longley()
 y_ll_train, _, X_ll_train, X_ll_test = temporal_train_test_split(y_ll, X_ll)
 
@@ -99,6 +113,13 @@ def count_gt100(x):
             None,
             None,
         ),
+        (
+            kwargs,
+            ["y_lag_1", "y_mean_1_3", "y_mean_1_12", "y_std_1_4"],
+            y_hierarchical,
+            None,
+            None,
+        ),
         (
             None,
             ["var_0_lag_1", "var_1"],
@@ -136,6 +157,9 @@ def test_windowsummarizer(kwargs, column_names, y, target_cols, truncate):
     else:
         Xt_columns = None
 
+    # check that the index names are preserved
+    assert y.index.names == Xt.index.names
+
     check_eval(Xt_columns, column_names)