plots.preprocessing_plots
Figures for the spotforecast2-safe data-preparation artifacts.
Companion plots for the preprocessing stages of spotforecast2_safe: the target-corruption heal step (plot_corruption_spans visualizes a TargetCorruptionReport next to the frames before and after healing) and the gap-imputation step (plot_imputation_weights visualizes the filled frame and weight series returned by get_missing_weights).
All functions follow the stateless conventions of spotforecast2.plots.evaluation: they return a matplotlib.figure.Figure, never call plt.show(), and do not mutate matplotlib.rcParams — styling and figure lifecycle stay with the caller (set matplotlib.use("Agg") before importing pyplot in headless environments).
Functions
plot_corruption_spans
plots.preprocessing_plots.plot_corruption_spans(
data_before,
data_after,
target,
spans,
* ,
reference= None ,
zoom= None ,
span_end_extension= '1h' ,
target_color= None ,
reference_color= None ,
span_color= '0.5' ,
span_alpha= 0.12 ,
before_label= 'corrupted series' ,
after_label= 'healed series' ,
reference_label= 'reference series' ,
linewidth= 0.9 ,
ylabel= '' ,
legend_loc= 'lower left' ,
figsize= (6.3 , 3.6 ),
)
Two-panel before/after view of a target-corruption heal.
The top panel shows the corrupted series (and, when reference is given, the reference series the deviation rule compares against), the bottom panel the same column after the policy ran. The flagged spans of the TargetCorruptionReport are shaded in both panels.
TargetCorruptionReport.spans records each span as a pair of hour-floored ISO strings in which the end names the START of the last flagged hour. The default span_end_extension of one hour therefore closes the interval so the shading covers the full flagged stretch. Spans are clipped to the zoom window and dropped when they fall entirely outside it, so a distant span never widens the x-axis.
Parameters
data_before
pd .DataFrame
Frame before healing. Must contain target (and reference when given).
required
data_after
pd .DataFrame
Frame after healing. Must contain target.
required
target
str
Name of the healed column.
required
spans
Sequence [tuple [str , str ]]
Flagged spans as (start, end) pairs of timestamps or ISO strings, typically report.spans.
required
reference
str | None
Optional reference column of data_before, drawn in the top panel.
None
zoom
Optional (start, end) pair restricting both panels to a window around the episode.
None
span_end_extension
str | pd .Timedelta
Amount added to each span end before shading. The default “1h” matches the report contract above.
'1h'
target_color
str | None
Line color of the target series in both panels. None leaves the choice to matplotlib’s color cycle.
None
reference_color
str | None
Line color of the reference series.
None
span_color
str
Fill color of the shaded spans.
'0.5'
span_alpha
float
Alpha of the shaded spans.
0.12
before_label
str
Legend label of the top-panel target line.
'corrupted series'
after_label
str
Legend label of the bottom-panel target line.
'healed series'
reference_label
str
Legend label of the reference line.
'reference series'
linewidth
float
Width of all series lines.
0.9
ylabel
str
Y-axis label, applied to both panels.
''
legend_loc
str
loc argument forwarded to both legends.
'lower left'
figsize
tuple [float , float ]
Figure size of the two-panel figure.
(6.3, 3.6)
Returns
Figure
A matplotlib.figure.Figure with two axes sharing the x-axis.
Raises
ValueError
If a frame is empty, if target (or reference) is missing from its frame, or if a span is not a pair.
Examples
import matplotlib
matplotlib.use("Agg" ) # non-interactive backend for doc rendering
import numpy as np
import pandas as pd
from spotforecast2.plots.preprocessing_plots import plot_corruption_spans
idx = pd.date_range("2025-01-01" , periods= 192 , freq= "15min" , tz= "UTC" )
rng = np.random.default_rng(3 )
before = pd.DataFrame({"Actual Load" : 10.0 + rng.normal(0.0 , 0.05 , 192 )}, index= idx)
after = before.copy()
flagged = idx[(idx >= "2025-01-01 12:00" ) & (idx < "2025-01-01 15:00" )]
after.loc[flagged, "Actual Load" ] = float ("nan" )
spans = [("2025-01-01T12:00" , "2025-01-01T14:00" )]
fig = plot_corruption_spans(before, after, "Actual Load" , spans)
assert len (fig.axes) == 2
assert len (fig.axes[0 ].patches) == 1 and len (fig.axes[1 ].patches) == 1
print ("plot_corruption_spans: 1 span shaded in both panels" )
plot_corruption_spans: 1 span shaded in both panels
plot_imputation_weights
plots.preprocessing_plots.plot_imputation_weights(
data_gapped,
data_filled,
weights,
target,
* ,
zoom= None ,
bridge_slots= 1 ,
series_color= None ,
fill_color= None ,
weight_color= None ,
series_label= 'series with gap' ,
fill_label= 'filled values' ,
weight_label= 'sample weight' ,
linewidth= 0.9 ,
fill_linewidth= 1.4 ,
ylabel= '' ,
weight_ylabel= 'weight' ,
weight_ylim= (- 0.05 , 1.05 ),
legend_loc= 'lower left' ,
figsize= (6.3 , 3.6 ),
)
Two-panel view of a gap fill and its companion sample weights.
Visualizes the pair returned by spotforecast2_safe.preprocessing.imputation.get_missing_weights: the top panel shows the gapped series with the filled values drawn over every gap, the bottom panel the weight series as a step plot, which is zero inside a gap and for the trailing window after it.
Each filled segment is extended by bridge_slots positions on both sides so the fill connects visually to the surrounding series.
Parameters
data_gapped
pd .DataFrame
Frame before imputation; gap slots hold NaN.
required
data_filled
pd .DataFrame
Frame after imputation, same index as data_gapped.
required
weights
pd .Series
Weight series aligned with the frames, e.g. the second return value of get_missing_weights.
required
target
str
Name of the filled column.
required
zoom
Optional (start, end) pair restricting both panels to a window around the gap.
None
bridge_slots
int
Positions added on each side of a filled segment.
1
series_color
str | None
Line color of the gapped series. None leaves the choice to matplotlib’s color cycle.
None
fill_color
str | None
Line color of the filled segments.
None
weight_color
str | None
Line color of the weight step plot.
None
series_label
str
Legend label of the gapped series.
'series with gap'
fill_label
str
Legend label of the filled segments.
'filled values'
weight_label
str
Legend label of the weight series.
'sample weight'
linewidth
float
Width of the series and weight lines.
0.9
fill_linewidth
float
Width of the filled segments.
1.4
ylabel
str
Y-axis label of the top panel.
''
weight_ylabel
str
Y-axis label of the bottom panel.
'weight'
weight_ylim
tuple [float , float ]
Y-limits of the bottom panel.
(-0.05, 1.05)
legend_loc
str
loc argument forwarded to both legends.
'lower left'
figsize
tuple [float , float ]
Figure size of the two-panel figure.
(6.3, 3.6)
Returns
Figure
A matplotlib.figure.Figure with two axes sharing the x-axis.
Raises
ValueError
If a frame is empty, if target is missing from either frame, or if the frames and weights do not share the same index.
Examples
import matplotlib
matplotlib.use("Agg" ) # non-interactive backend for doc rendering
import numpy as np
import pandas as pd
from spotforecast2.plots.preprocessing_plots import plot_imputation_weights
idx = pd.date_range("2025-01-01" , periods= 192 , freq= "15min" , tz= "UTC" )
rng = np.random.default_rng(5 )
gapped = pd.DataFrame({"Actual Load" : 10.0 + rng.normal(0.0 , 0.05 , 192 )}, index= idx)
gapped.iloc[60 :84 , 0 ] = float ("nan" )
filled = gapped.ffill()
weights = pd.Series(1.0 , index= idx)
weights.iloc[60 :120 ] = 0.0
fig = plot_imputation_weights(gapped, filled, weights, "Actual Load" )
assert len (fig.axes) == 2
assert len (fig.axes[0 ].lines) == 2 # series + one filled segment
print ("plot_imputation_weights: 1 gap bridged" )
plot_imputation_weights: 1 gap bridged