@@ -751,10 +751,17 @@ def plot_threshold_space_per_scenario(df: pd.DataFrame, output_dir: Path):
751751
752752
753753def plot_margin_vs_best (df : pd .DataFrame , output_dir : Path ):
754- """For each hysteresis margin, plot the best score / savings / overhead.
754+ """For each hysteresis margin, plot score / savings / overhead of the best-scoring run .
755755
756- 2×5 grid (models × regions), with three y-axes per subplot.
756+ 2×5 grid (models × regions). Uses _all_ data (optimized over start dates).
757+ For each hysteresis margin, finds the run with the highest score and plots
758+ that run's score, savings, and overhead.
757759 """
760+ df_all = df [df ["file_type" ] == "all" ]
761+ if df_all .empty :
762+ print (" No _all_ data found, skipping margin vs best overview." )
763+ return
764+
758765 fig , axes = plt .subplots (
759766 len (MODEL_ORDER ),
760767 len (REGION_ORDER ),
@@ -763,69 +770,52 @@ def plot_margin_vs_best(df: pd.DataFrame, output_dir: Path):
763770 sharey = False ,
764771 )
765772
766- line_styles = {"01-01" : "-" , "07-01" : "--" }
767-
768773 for col_idx , region in enumerate (REGION_ORDER ):
769774 for row_idx , model in enumerate (MODEL_ORDER ):
770775 ax = axes [row_idx , col_idx ]
771776 ax2 = ax .twinx ()
772- subset = df [( df ["model" ] == model ) & (df ["region" ] == region )]
777+ subset = df_all [( df_all ["model" ] == model ) & (df_all ["region" ] == region )]
773778
774779 if subset .empty :
775780 ax .set_visible (False )
776781 ax2 .set_visible (False )
777782 continue
778783
779- for start_date , start_group in subset .groupby ("start_date" ):
780- if start_group .empty :
781- continue
782- ls = line_styles .get (start_date , "-" )
783- label_suffix = START_LABELS .get (start_date , start_date )
784-
785- agg = (
786- start_group .groupby ("hysteresis_margin" )
787- .agg (
788- best_score = ("score" , "max" ),
789- best_savings = ("co2_save_pct" , "max" ),
790- best_overhead = ("overhead_pct" , "min" ),
791- )
792- .reset_index ()
793- .sort_values ("hysteresis_margin" )
794- )
784+ # For each hysteresis margin, find the run with highest score
785+ best_per_margin = subset .loc [
786+ subset .groupby ("hysteresis_margin" )["score" ].idxmax ()
787+ ].sort_values ("hysteresis_margin" )
795788
796- if len (agg ) < 2 :
797- continue
789+ if len (best_per_margin ) < 2 :
790+ continue
798791
799- ax .plot (
800- agg ["hysteresis_margin" ],
801- agg ["best_score" ],
802- color = "#2563eb" ,
803- linestyle = ls ,
804- linewidth = 2 ,
805- marker = "o" ,
806- markersize = 4 ,
807- label = f"Score ({ label_suffix } )" ,
808- )
809- ax .plot (
810- agg ["hysteresis_margin" ],
811- agg ["best_savings" ] / 100 ,
812- color = "#059669" ,
813- linestyle = ls ,
814- linewidth = 2 ,
815- marker = "s" ,
816- markersize = 4 ,
817- label = f"Savings/100 ({ label_suffix } )" ,
818- )
819- ax2 .plot (
820- agg ["hysteresis_margin" ],
821- agg ["best_overhead" ],
822- color = "#dc2626" ,
823- linestyle = ls ,
824- linewidth = 2 ,
825- marker = "^" ,
826- markersize = 4 ,
827- label = f"Overhead ({ label_suffix } )" ,
828- )
792+ ax .plot (
793+ best_per_margin ["hysteresis_margin" ],
794+ best_per_margin ["score" ],
795+ color = "#2563eb" ,
796+ linewidth = 2 ,
797+ marker = "o" ,
798+ markersize = 4 ,
799+ label = "Score" ,
800+ )
801+ ax .plot (
802+ best_per_margin ["hysteresis_margin" ],
803+ best_per_margin ["co2_save_pct" ] / 100 ,
804+ color = "#059669" ,
805+ linewidth = 2 ,
806+ marker = "s" ,
807+ markersize = 4 ,
808+ label = "Savings/100" ,
809+ )
810+ ax2 .plot (
811+ best_per_margin ["hysteresis_margin" ],
812+ best_per_margin ["overhead_pct" ],
813+ color = "#dc2626" ,
814+ linewidth = 2 ,
815+ marker = "^" ,
816+ markersize = 4 ,
817+ label = "Overhead" ,
818+ )
829819
830820 if row_idx == 0 :
831821 ax .set_title (region , fontsize = 14 , fontweight = "bold" )
@@ -842,15 +832,16 @@ def plot_margin_vs_best(df: pd.DataFrame, output_dir: Path):
842832
843833 if col_idx == len (REGION_ORDER ) - 1 :
844834 lines1 , labels1 = ax .get_legend_handles_labels ()
835+ lines2 , labels2 = ax2 .get_legend_handles_labels ()
845836 ax .legend (
846- lines1 , labels1 ,
847- fontsize = 6 ,
837+ lines1 + lines2 , labels1 + labels2 ,
838+ fontsize = 7 ,
848839 loc = "upper left" ,
849840 ncol = 1 ,
850841 )
851842
852843 fig .suptitle (
853- "Hysteresis Margin vs Best Score / Savings / Overhead" ,
844+ "Hysteresis Margin vs Best Score / Savings / Overhead (All Start Dates) " ,
854845 fontsize = 16 ,
855846 fontweight = "bold" ,
856847 y = 1.0 ,
@@ -863,13 +854,19 @@ def plot_margin_vs_best(df: pd.DataFrame, output_dir: Path):
863854
864855
865856def plot_margin_vs_best_per_model (df : pd .DataFrame , output_dir : Path ):
866- """Per (model, region): margin on x, best score/savings/overhead on y.
857+ """Per (model, region): margin on x, score/savings/overhead of best-scoring run on y.
867858
868- Two start dates overlaid with different line styles.
859+ Uses _all_ data (optimized over start dates). For each hysteresis margin,
860+ finds the run with the highest score and plots that run's metrics.
869861 """
862+ df_all = df [df ["file_type" ] == "all" ]
863+ if df_all .empty :
864+ print (" No _all_ data found, skipping margin vs best per model." )
865+ return
866+
870867 for model in MODEL_ORDER :
871868 fig , axes = plt .subplots (1 , len (REGION_ORDER ), figsize = (24 , 5 ), sharey = False )
872- model_df = df [ df ["model" ] == model ]
869+ model_df = df_all [ df_all ["model" ] == model ]
873870
874871 for col_idx , region in enumerate (REGION_ORDER ):
875872 ax = axes [col_idx ]
@@ -880,55 +877,41 @@ def plot_margin_vs_best_per_model(df: pd.DataFrame, output_dir: Path):
880877 ax .set_visible (False )
881878 continue
882879
883- for start_date , start_group in region_df .groupby ("start_date" ):
884- if start_group .empty :
885- continue
886- label_suffix = START_LABELS .get (start_date , start_date )
887- ls = "-" if start_date == "01-01" else "--"
888-
889- agg = (
890- start_group .groupby ("hysteresis_margin" )
891- .agg (
892- best_score = ("score" , "max" ),
893- best_savings = ("co2_save_pct" , "max" ),
894- best_overhead = ("overhead_pct" , "min" ),
895- )
896- .reset_index ()
897- .sort_values ("hysteresis_margin" )
898- )
899- if len (agg ) < 2 :
900- continue
880+ # For each hysteresis margin, find the run with highest score
881+ best_per_margin = region_df .loc [
882+ region_df .groupby ("hysteresis_margin" )["score" ].idxmax ()
883+ ].sort_values ("hysteresis_margin" )
901884
902- ax . plot (
903- agg [ "hysteresis_margin" ],
904- agg [ "best_score" ],
905- color = "#2563eb" ,
906- linestyle = ls ,
907- linewidth = 2 ,
908- marker = "o " ,
909- markersize = 4 ,
910- label = f"Score ( { label_suffix } ) " ,
911- )
912- ax . plot (
913- agg [ "hysteresis_margin" ],
914- agg [ "best_savings" ] / 100 ,
915- color = "#059669" ,
916- linestyle = ls ,
917- linewidth = 2 ,
918- marker = "s" ,
919- markersize = 4 ,
920- label = f"Savings/100 ( { label_suffix } )" ,
921- )
922- ax2 . plot (
923- agg [ "hysteresis_margin" ],
924- agg [ "best_overhead " ],
925- color = "#dc2626" ,
926- linestyle = ls ,
927- linewidth = 2 ,
928- marker = "^" ,
929- markersize = 4 ,
930- label = f "Overhead ( { label_suffix } ) " ,
931- )
885+ if len ( best_per_margin ) < 2 :
886+ continue
887+
888+ ax . plot (
889+ best_per_margin [ "hysteresis_margin" ] ,
890+ best_per_margin [ "score" ] ,
891+ color = "#2563eb " ,
892+ linewidth = 2 ,
893+ marker = "o " ,
894+ markersize = 4 ,
895+ label = "Score" ,
896+ )
897+ ax . plot (
898+ best_per_margin [ "hysteresis_margin" ] ,
899+ best_per_margin [ "co2_save_pct" ] / 100 ,
900+ color = "#059669" ,
901+ linewidth = 2 ,
902+ marker = "s" ,
903+ markersize = 4 ,
904+ label = "Savings/100" ,
905+ )
906+ ax2 . plot (
907+ best_per_margin [ "hysteresis_margin " ],
908+ best_per_margin [ "overhead_pct" ] ,
909+ color = "#dc2626" ,
910+ linewidth = 2 ,
911+ marker = "^" ,
912+ markersize = 4 ,
913+ label = "Overhead" ,
914+ )
932915
933916 ax .set_title (region , fontsize = 12 , fontweight = "bold" )
934917 if col_idx == 0 :
@@ -942,10 +925,11 @@ def plot_margin_vs_best_per_model(df: pd.DataFrame, output_dir: Path):
942925 ax .grid (True , alpha = 0.3 )
943926 if col_idx == len (REGION_ORDER ) - 1 :
944927 lines1 , labels1 = ax .get_legend_handles_labels ()
945- ax .legend (lines1 , labels1 , fontsize = 7 , loc = "upper left" )
928+ lines2 , labels2 = ax2 .get_legend_handles_labels ()
929+ ax .legend (lines1 + lines2 , labels1 + labels2 , fontsize = 7 , loc = "upper left" )
946930
947931 fig .suptitle (
948- f"Margin vs Best Metrics: { model } " ,
932+ f"Margin vs Best Metrics: { model } (All Start Dates) " ,
949933 fontsize = 14 ,
950934 fontweight = "bold" ,
951935 y = 1.02 ,
@@ -2182,7 +2166,7 @@ def plot_score_heatmaps(output_dir: Path):
21822166 scores = _compute_score (savings_grid , overhead_grid , alpha )
21832167
21842168 im = ax .imshow (
2185- scores ,
2169+ scores . T ,
21862170 extent = [0 , 200 , 0 , 50 ],
21872171 origin = "lower" ,
21882172 aspect = "auto" ,
@@ -2518,8 +2502,8 @@ def main():
25182502 print ("\n Generating hysteresis comparison plots (#67)..." )
25192503 plot_threshold_space_overview (df_fixed , FIGURES_DIR )
25202504 plot_threshold_space_per_scenario (df_fixed , FIGURES_DIR )
2521- plot_margin_vs_best (df_fixed , FIGURES_DIR )
2522- plot_margin_vs_best_per_model (df_fixed , FIGURES_DIR )
2505+ plot_margin_vs_best (df , FIGURES_DIR )
2506+ plot_margin_vs_best_per_model (df , FIGURES_DIR )
25232507
25242508 print_summary_table (df_fixed )
25252509
0 commit comments