@@ -968,6 +968,7 @@ def plot_binned_residuals(cv_predictions, by="hour"):
968968
969969 all_iqr_bands = []
970970 all_mean_lines = []
971+ time_range = None # Will store the min/max time values for the perfect line
971972
972973 for i , cv_prediction in enumerate (cv_predictions ):
973974 # Get date range for this CV fold
@@ -997,6 +998,17 @@ def plot_binned_residuals(cv_predictions, by="hour"):
997998 .with_columns (pl .lit (fold_label ).alias ("fold" ))
998999 )
9991000
1001+ # Store time range for perfect line (use the first CV fold)
1002+ if time_range is None :
1003+ time_range = (
1004+ residuals_stats [time_column ].min (),
1005+ residuals_stats [time_column ].max (),
1006+ )
1007+ else :
1008+ time_range = (
1009+ min (time_range [0 ], residuals_stats [time_column ].min ()),
1010+ max (time_range [1 ], residuals_stats [time_column ].max ()),
1011+ )
10001012 # Create IQR band for this CV fold
10011013 iqr_band = (
10021014 altair .Chart (residuals_stats )
@@ -1023,6 +1035,29 @@ def plot_binned_residuals(cv_predictions, by="hour"):
10231035 all_iqr_bands .append (iqr_band )
10241036 all_mean_lines .append (mean_line )
10251037
1038+ # Create perfect residuals line at y=0
1039+ perfect_line = (
1040+ altair .Chart (
1041+ pl .DataFrame (
1042+ {
1043+ time_column : [time_range [0 ], time_range [1 ]],
1044+ "perfect_residual" : [0 , 0 ],
1045+ "label" : ["Perfect" ] * 2 ,
1046+ }
1047+ )
1048+ )
1049+ .mark_line (strokeDash = [5 , 5 ], opacity = 0.8 , color = "black" )
1050+ .encode (
1051+ x = altair .X (f"{ time_column } :O" , title = x_title ),
1052+ y = altair .Y ("perfect_residual:Q" , title = "Mean residual (MW)" ),
1053+ color = altair .Color (
1054+ "label:N" ,
1055+ scale = altair .Scale (range = ["black" ]),
1056+ legend = None ,
1057+ ),
1058+ )
1059+ )
1060+
10261061 # Combine all IQR bands
10271062 combined_iqr = all_iqr_bands [0 ]
10281063 for band in all_iqr_bands [1 :]:
@@ -1033,8 +1068,10 @@ def plot_binned_residuals(cv_predictions, by="hour"):
10331068 for line in all_mean_lines [1 :]:
10341069 combined_lines += line
10351070
1036- # Layer the IQR bands behind the mean lines
1037- return (combined_iqr + combined_lines ).resolve_scale (color = "independent" )
1071+ # Layer the IQR bands behind the mean lines, with perfect line on top
1072+ return (combined_iqr + combined_lines + perfect_line ).resolve_scale (
1073+ color = "independent"
1074+ )
10381075
10391076
10401077plot_binned_residuals (cv_predictions , by = "hour" ).interactive ().properties (
0 commit comments