Repository: ourownstory/neural_prophet

Issue:
[enhancement] allow residuals plotting with local normalization
currently disabled in `plot_components`, but should not be hard to allow


Discussion:

Resolving patch:
diff --git a/neuralprophet/forecaster.py b/neuralprophet/forecaster.py
--- a/neuralprophet/forecaster.py
+++ b/neuralprophet/forecaster.py
@@ -1705,7 +1705,7 @@ def plot_last_forecast(
             )
 
     def plot_components(
-        self, fcst, df_name=None, figsize=None, forecast_in_focus=None, residuals=False, plotting_backend="default"
+        self, fcst, df_name="__df__", figsize=None, forecast_in_focus=None, residuals=False, plotting_backend="default"
     ):
         """Plot the NeuralProphet forecast components.
 
@@ -1779,6 +1779,7 @@ def plot_components(
                 figsize=tuple(x * 70 for x in figsize) if figsize else (700, 210),
                 forecast_in_focus=forecast_in_focus if forecast_in_focus else self.highlight_forecast_step_n,
                 residuals=residuals,
+                df_name=df_name,
             )
         else:
             return plot_components(
@@ -1788,6 +1789,7 @@ def plot_components(
                 figsize=figsize,
                 forecast_in_focus=forecast_in_focus if forecast_in_focus else self.highlight_forecast_step_n,
                 residuals=residuals,
+                df_name=df_name,
             )
 
     def plot_parameters(
diff --git a/neuralprophet/plot_forecast.py b/neuralprophet/plot_forecast.py
--- a/neuralprophet/plot_forecast.py
+++ b/neuralprophet/plot_forecast.py
@@ -151,7 +151,14 @@ def plot(
 
 
 def plot_components(
-    m, fcst, quantile=0.5, forecast_in_focus=None, one_period_per_season=True, residuals=False, figsize=None
+    m,
+    fcst,
+    df_name="__df__",
+    quantile=0.5,
+    forecast_in_focus=None,
+    one_period_per_season=True,
+    residuals=False,
+    figsize=None,
 ):
     """Plot the NeuralProphet forecast components.
 
@@ -161,6 +168,8 @@ def plot_components(
             Fitted model
         fcst : pd.DataFrame
             Output of m.predict
+        df_name : str
+            ID from time series that should be plotted
         quantile : float
             Quantile for which the forecast components are to be plotted
         forecast_in_focus : int
@@ -346,13 +355,13 @@ def plot_components(
             if one_period_per_season:
                 comp_name = comp["comp_name"]
                 if comp_name.lower() == "weekly" or m.config_season.periods[comp_name].period == 7:
-                    plot_weekly(m=m, ax=ax, quantile=quantile, comp_name=comp_name)
+                    plot_weekly(m=m, ax=ax, quantile=quantile, comp_name=comp_name, df_name=df_name)
                 elif comp_name.lower() == "yearly" or m.config_season.periods[comp_name].period == 365.25:
-                    plot_yearly(m=m, ax=ax, quantile=quantile, comp_name=comp_name)
+                    plot_yearly(m=m, ax=ax, quantile=quantile, comp_name=comp_name, df_name=df_name)
                 elif comp_name.lower() == "daily" or m.config_season.periods[comp_name].period == 1:
-                    plot_daily(m=m, ax=ax, quantile=quantile, comp_name=comp_name)
+                    plot_daily(m=m, ax=ax, quantile=quantile, comp_name=comp_name, df_name=df_name)
                 else:
-                    plot_custom_season(m=m, ax=ax, quantile=quantile, comp_name=comp_name)
+                    plot_custom_season(m=m, ax=ax, quantile=quantile, comp_name=comp_name, df_name=df_name)
             else:
                 comp_name = f"season_{comp['comp_name']}"
                 plot_forecast_component(fcst=fcst, ax=ax, comp_name=comp_name, plot_name=comp["plot_name"])
diff --git a/neuralprophet/plot_forecast_plotly.py b/neuralprophet/plot_forecast_plotly.py
--- a/neuralprophet/plot_forecast_plotly.py
+++ b/neuralprophet/plot_forecast_plotly.py
@@ -202,7 +202,9 @@ def plot(fcst, quantiles, xlabel="ds", ylabel="y", highlight_forecast=None, line
     return fig
 
 
-def plot_components(m, fcst, forecast_in_focus=None, one_period_per_season=True, residuals=False, figsize=(700, 210)):
+def plot_components(
+    m, fcst, df_name="__df__", forecast_in_focus=None, one_period_per_season=True, residuals=False, figsize=(700, 210)
+):
     """
     Plot the NeuralProphet forecast components.
 
@@ -212,6 +214,8 @@ def plot_components(m, fcst, forecast_in_focus=None, one_period_per_season=True,
             Fitted model
         fcst : pd.DataFrame
             Output of m.predict
+        df_name : str
+            ID from time series that should be plotted
         forecast_in_focus : int
             n-th step ahead forecast AR-coefficients to plot
         one_period_per_season : bool
@@ -398,7 +402,7 @@ def plot_components(m, fcst, forecast_in_focus=None, one_period_per_season=True,
                 comp.update({"multiplicative": True})
             if one_period_per_season:
                 comp_name = comp["comp_name"]
-                trace_object = get_seasonality_props(m, fcst, **comp)
+                trace_object = get_seasonality_props(m, fcst, df_name, **comp)
             else:
                 comp_name = f"season_{comp['comp_name']}"
                 trace_object = get_forecast_component_props(fcst=fcst, comp_name=comp_name, plot_name=comp["plot_name"])
@@ -722,7 +726,7 @@ def get_multiforecast_component_props(
     return {"traces": traces, "xaxis": xaxis, "yaxis": yaxis}
 
 
-def get_seasonality_props(m, fcst, comp_name="weekly", multiplicative=False, quick=False, **kwargs):
+def get_seasonality_props(m, fcst, df_name="__df__", comp_name="weekly", multiplicative=False, quick=False, **kwargs):
     """
     Prepares a dictionary for plotting the selected seasonality with plotly
 
@@ -732,6 +736,8 @@ def get_seasonality_props(m, fcst, comp_name="weekly", multiplicative=False, qui
             Fitted NeuralProphet model
         fcst : pd.DataFrame
             Output of m.predict
+        df_name : str
+            ID from time series that should be plotted
         comp_name : str
             Name of the component to plot
         multiplicative : bool
@@ -757,6 +763,7 @@ def get_seasonality_props(m, fcst, comp_name="weekly", multiplicative=False, qui
         plot_points = np.floor(period * 24 * 60).astype(int)
     days = pd.to_datetime(np.linspace(start.value, end.value, plot_points, endpoint=False))
     df_y = pd.DataFrame({"ds": days})
+    df_y["ID"] = df_name
 
     if quick:
         predicted = m.predict_season_from_dates(m, dates=df_y["ds"], name=comp_name)


Test patch: