@@ -22,8 +22,8 @@ def render_performance_table_great_tables(
2222) -> GT :
2323 """Render prepared performance data as a Great Tables table.
2424
25- This renderer is intended for Marimo and other HTML notebook/document
26- contexts. Reactable remains available when expandable row details are wanted.
25+ This renderer is intended for Marimo and HTML/Quarto contexts. Reactable
26+ remains available when expandable confusion-matrix row details are wanted.
2727 """
2828 if performance_data .is_empty ():
2929 raise ValueError ("performance_data must contain at least one row" )
@@ -33,11 +33,11 @@ def render_performance_table_great_tables(
3333 raise ValueError ("performance_data must contain exactly one stratification" )
3434 stratified_by = stratifications [0 ]
3535
36- columns = [
36+ source_columns = [
3737 "reference_group" , "chosen_cutoff" , "sensitivity" , "specificity" , "ppv" ,
3838 "npv" , "lift" , "predicted_positives" , "net_benefit" , "ppcr" ,
3939 ]
40- data = performance_data .select ([c for c in columns if c in performance_data .columns ])
40+ data = performance_data .select ([c for c in source_columns if c in performance_data .columns ])
4141 if "reference_group" in data .columns :
4242 data = data .rename ({"reference_group" : "Model" })
4343
@@ -50,40 +50,50 @@ def render_performance_table_great_tables(
5050 if sort_columns :
5151 data = data .sort (sort_columns )
5252
53- # Match the R table's combined predicted-positive display.
53+ # Match the R table's count + percentage display for predicted positives.
54+ ppcr_percent = (pl .col ("ppcr" ) * 100 ).round (2 ).cast (pl .String )
5455 data = data .with_columns (
55- pl .format (
56- "{} ({:.2f}%)" ,
57- pl .col ("predicted_positives" ),
58- pl .col ("ppcr" ) * 100 ,
56+ pl .concat_str (
57+ [
58+ pl .col ("predicted_positives" ).cast (pl .String ),
59+ pl .lit (" (" ),
60+ ppcr_percent ,
61+ pl .lit ("%)" ),
62+ ]
5963 ).alias ("Predicted Positives" )
6064 ).drop (["predicted_positives" , "ppcr" ])
6165
6266 if stratified_by != "probability_threshold" and "net_benefit" in data .columns :
6367 data = data .drop ("net_benefit" )
6468
65- metric_columns = [c for c in ["sensitivity" , "specificity" , "ppv" , "npv" , "lift" , "net_benefit" ] if c in data .columns ]
66-
67- table = (
68- GT (data )
69- .cols_label (
70- Model = "Model" ,
71- Threshold = "Probability Threshold" ,
72- sensitivity = "Sens" ,
73- specificity = "Spec" ,
74- ppv = "PPV" ,
75- npv = "NPV" ,
76- lift = "Lift" ,
77- net_benefit = "Net Benefit" ,
78- )
79- .tab_spanner (label = "Performance Metrics" , columns = metric_columns )
80- .fmt_number (columns = [c for c in metric_columns if c != "lift" ], decimals = 2 )
81- .fmt_number (columns = [c for c in ["lift" ] if c in data .columns ], decimals = 2 )
69+ metric_columns = [
70+ c for c in ["sensitivity" , "specificity" , "ppv" , "npv" , "lift" , "net_benefit" ]
71+ if c in data .columns
72+ ]
73+ labels = {
74+ "Model" : "Model" ,
75+ "Threshold" : "Probability Threshold" ,
76+ "sensitivity" : "Sens" ,
77+ "specificity" : "Spec" ,
78+ "ppv" : "PPV" ,
79+ "npv" : "NPV" ,
80+ "lift" : "Lift" ,
81+ "net_benefit" : "Net Benefit" ,
82+ "Predicted Positives" : "Predicted Positives" ,
83+ }
84+ labels = {column : label for column , label in labels .items () if column in data .columns }
85+
86+ table = GT (data ).cols_label (cases = labels )
87+ if metric_columns :
88+ table = table .tab_spanner (label = "Performance Metrics" , columns = metric_columns )
89+ table = table .fmt_number (columns = metric_columns , decimals = 2 )
90+
91+ # Keep model identification visually consistent with rtichoke's palette.
92+ models = (
93+ data .get_column ("Model" ).unique (maintain_order = True ).to_list ()
94+ if "Model" in data .columns
95+ else []
8296 )
83-
84- # Great Tables is presentation-first, so use gentle cell fills rather than
85- # recreating Reactable's JS data bars. Keep model colors consistent with R.
86- models = data .get_column ("Model" ).unique (maintain_order = True ).to_list () if "Model" in data .columns else []
8797 for index , model in enumerate (models ):
8898 table = table .tab_style (
8999 style = [
0 commit comments