Repository: sktime/sktime

Issue:
[BUG] inconsistent date/time index in `plot_windows`
reported originally by @jpss95 in #519.

Confirmed by @fkiraly on 0.21.0, current `main`, windows, python 3.11

---
Hi all, the same thing is happening to me with the plot_windows method. (sktime==0.18.0)

Here a code to reproduce the bug:

```python
import pandas as pd
import numpy as np
from sktime.utils.plotting import plot_windows
from sktime.forecasting.base import ForecastingHorizon
from sktime.forecasting.model_selection import ExpandingWindowSplitter

forecast_steps= 5
initial_window_size= 40
step_length=1
fh = ForecastingHorizon(np.arange(1,forecast_steps+1))

#create dummy data
dates = pd.date_range(start='2016-01', end='2022-12', freq='M')
df = pd.DataFrame(index=dates)
df['y'] = np.random.rand(len(df))

cv = ExpandingWindowSplitter(fh=fh, initial_window=initial_window_size, step_length=step_length)

print(df)
plot_windows(cv, df)
```

![image](https://github.com/sktime/sktime/assets/64998671/b4bc55db-e44f-4d1c-b1b4-f5251455e7bb)


The image clearly has wrong indexes

cc: @fkiraly

_Originally posted by @jpss95 in https://github.com/sktime/sktime/issues/519#issuecomment-1638671715_
            


Discussion:
@jpss95, I opened a new issue here, since this concerns a different plotting utility.

Thanks for reporting!

Resolving patch:
diff --git a/sktime/utils/plotting.py b/sktime/utils/plotting.py
--- a/sktime/utils/plotting.py
+++ b/sktime/utils/plotting.py
@@ -129,7 +129,7 @@ def plot_series(
     # generate integer x-values
     xs = [np.argwhere(index.isin(y.index)).ravel() for y in series]
 
-    # create figure if no Axe provided for plotting
+    # create figure if no ax provided for plotting
     if _ax_kwarg_is_none:
         fig, ax = plt.subplots(1, figsize=plt.figaspect(0.25))
 
@@ -405,17 +405,35 @@ def _get_windows(cv, y):
     return train_windows, test_windows
 
 
-def plot_windows(cv, y, title=""):
+def plot_windows(cv, y, title="", ax=None):
     """Plot training and test windows.
 
+    Plots the training and test windows for each split of a time series,
+    subject to an sktime time series splitter.
+
+    x-axis: time, ranging from start to end of `y`
+    y-axis: window number, starting at 0
+    plot elements: training split (orange) and test split (blue)
+        dots indicate index in the training or test split
+        will be plotted on top of each other if train/test split is not disjoint
+
     Parameters
     ----------
     y : pd.Series
         Time series to split
-    cv : temporal cross-validation iterator object
-        Temporal cross-validation iterator
+    cv : sktime splitter object, descendant of BaseSplitter
+        Time series splitter, e.g., temporal cross-validation iterator
     title : str
         Plot title
+    ax : matplotlib.axes.Axes, optional (default=None)
+        Axes on which to plot. If None, axes will be created and returned.
+
+    Returns
+    -------
+    fig : matplotlib.figure.Figure, returned only if ax is None
+        matplotlib figure object
+    ax : matplotlib.axes.Axes
+        matplotlib axes object with the figure
     """
     _check_soft_dependencies("matplotlib", "seaborn")
     import matplotlib.pyplot as plt
@@ -424,6 +442,12 @@ def plot_windows(cv, y, title=""):
 
     simplefilter("ignore", category=UserWarning)
 
+    _ax_kwarg_is_none = True if ax is None else False
+
+    # create figure if no ax provided for plotting
+    if _ax_kwarg_is_none:
+        fig, ax = plt.subplots(1, figsize=plt.figaspect(0.25))
+
     train_windows, test_windows = _get_windows(cv, y)
 
     def get_y(length, split):
@@ -461,12 +485,19 @@ def get_y(length, split):
         )
     ax.invert_yaxis()
     ax.yaxis.set_major_locator(MaxNLocator(integer=True))
+    xtickslocs = [tick for tick in ax.get_xticks() if tick in np.arange(n_timepoints)]
     ax.set(
         title=title,
         ylabel="Window number",
         xlabel="Time",
-        xticklabels=y.index,
+        xticks=xtickslocs,
+        xticklabels=y.iloc[xtickslocs].index,
     )
     # remove duplicate labels/handles
     handles, labels = ((leg[:2]) for leg in ax.get_legend_handles_labels())
     ax.legend(handles, labels)
+
+    if _ax_kwarg_is_none:
+        return fig, ax
+    else:
+        return ax


Test patch: