Repository: sktime/sktime

Issue:
[ENH] TruncationTransformer should preserve index and column names of initial pandas dataframe 
**Is your feature request related to a problem? Please describe.**

TruncationTransformer is a very useful function, but it become hard to use in conjunction with an sklearn ColumnTransformer if you want the Pipeline to be interpretable. After the transform, the new truncated dataframe loses all feature and index name conventions. The image below illustrates what occurs after the transform: 
![image](https://user-images.githubusercontent.com/83707037/178787184-5ef0be61-894d-4022-af92-ad432df29521.png)

**Describe the solution you'd like**

It is highly desirable that the truncated dataframe preserve the labelling convention of the initial dataframe. In this way, the user is less prone to error and avoid unnecessary bugs. For example, if I'm attempting to create an interpretable pipeline that utilizes a ColumnTransformer and the names given of the initial dataframe, the pipeline will break:
```
    ct = ColumnTransformer(
        [
            (sensor_col, univariate_pipeline, [sensor_col]) for sensor_col in sensor_cols
        ]
    )

    multivariate_pipeline = Pipeline(
        [
            ('truncator', TruncationTransformer()), #apply truncation across all periods in the panel_df
            ('univariate_model', ct),
        ]
    )
```

```
KeyError: 's1' 
....

File ~/projects/PdM/PdM/lib/python3.8/site-packages/sklearn/compose/_column_transformer.py:357, in ColumnTransformer._validate_column_callables(self, X)
    355         columns = columns(X)
    356     all_columns.append(columns)
--> 357     transformer_to_input_indices[name] = _get_column_indices(X, columns)
    359 self._columns = all_columns
    360 self._transformer_to_input_indices = transformer_to_input_indices

File ~/projects/PdM/PdM/lib/python3.8/site-packages/sklearn/utils/__init__.py:424, in _get_column_indices(X, key)
    421             column_indices.append(col_idx)
    423     except KeyError as e:
--> 424         raise ValueError("A given column is not a column of the dataframe") from e
    426     return column_indices
    427 else:

ValueError: A given column is not a column of the dataframe

```

**Describe alternatives you've considered**
Solutions include iterating over range(0, num_sensor_columns), but this shouldn't be considered a clean and interpretable option. Is there anyway to have the Transformer preserve the labels? 



Discussion:

Resolving patch:
diff --git a/sktime/datatypes/_panel/_convert.py b/sktime/datatypes/_panel/_convert.py
--- a/sktime/datatypes/_panel/_convert.py
+++ b/sktime/datatypes/_panel/_convert.py
@@ -589,6 +589,7 @@ def from_multi_index_to_3d_numpy_adp(obj, store=None):
     res = from_multi_index_to_3d_numpy(X=obj)
     if isinstance(store, dict):
         store["columns"] = obj.columns
+        store["index_names"] = obj.index.names
 
     return res
 
@@ -667,6 +668,10 @@ def from_3d_numpy_to_multi_index_adp(obj, store=None):
         and len(store["columns"]) == obj.shape[1]
     ):
         res.columns = store["columns"]
+
+    if isinstance(store, dict) and "index_names" in store.keys():
+        res.index.names = store["index_names"]
+
     return res
 
 
@@ -733,6 +738,9 @@ def from_multi_index_to_nested(
 
 def from_multi_index_to_nested_adp(obj, store=None):
 
+    if isinstance(store, dict):
+        store["index_names"] = obj.index.names
+
     return from_multi_index_to_nested(multi_ind_dataframe=obj, instance_index=None)
 
 
@@ -818,10 +826,15 @@ def from_nested_to_multi_index(X, instance_index=None, time_index=None):
 
 def from_nested_to_multi_index_adp(obj, store=None):
 
-    return from_nested_to_multi_index(
+    res = from_nested_to_multi_index(
         X=obj, instance_index="instances", time_index="timepoints"
     )
 
+    if isinstance(store, dict) and "index_names" in store.keys():
+        res.index.names = store["index_names"]
+
+    return res
+
 
 convert_dict[("nested_univ", "pd-multiindex", "Panel")] = from_nested_to_multi_index_adp
 
@@ -943,6 +956,9 @@ def from_dflist_to_multiindex(obj, store=None):
 
     mi = pd.concat(obj, axis=0, keys=range(n), names=["instances", "timepoints"])
 
+    if isinstance(store, dict) and "index_names" in store.keys():
+        mi.index.names = store["index_names"]
+
     return mi
 
 
@@ -955,6 +971,9 @@ def from_multiindex_to_dflist(obj, store=None):
 
     Xlist = [obj.loc[i].rename_axis(None) for i in instance_index]
 
+    if isinstance(store, dict):
+        store["index_names"] = obj.index.names
+
     return Xlist
 
 
diff --git a/sktime/transformations/panel/truncation.py b/sktime/transformations/panel/truncation.py
--- a/sktime/transformations/panel/truncation.py
+++ b/sktime/transformations/panel/truncation.py
@@ -117,6 +117,7 @@ def _transform(self, X, y=None):
         truncate = [pd.Series([series.iloc[idxs] for series in out]) for out in arr]
 
         Xt = pd.DataFrame(truncate)
+        Xt.columns = X.columns
 
         return Xt
 


Test patch: