Spaces:
Running
Running
Commit ·
2a1a3d6
1
Parent(s): dee5092
KPI cards as native HTML row above the forest plot (responsive flexbox)
Browse files- Move 3 KPI cards out of the Plotly figure into a sibling gr.HTML with
CSS flexbox layout. They now resize fluidly with the window and wrap
on narrow viewports instead of overlapping or overflowing.
- plot_significance_forest() now returns (figure, kpi_html) tuple.
- Forest plot top margin reduced 180px → 40px (cards no longer occupy
paper-y > 1.0 inside the figure).
- Cache (figures_cache.json) gains a 'forest_kpi' map alongside 'forest';
the live render path also produces the HTML on cache miss. Existing
cached entries return an empty KPI block until the next rebuild.
- app.py +129 -62
- build_figures_cache.py +9 -4
- perfmap_hulls_override.json +1 -1
app.py
CHANGED
|
@@ -1130,6 +1130,58 @@ body:not(.dark) #nk-light-mode-nudge { display: none !important; }
|
|
| 1130 |
.nk-bar-plot .modebar-container,
|
| 1131 |
.nk-bar-plot .js-plotly-plot .plotly .modebar { display: none !important; }
|
| 1132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1133 |
/* ===== Leaderboard table view (HTML, with per-cell rank badge) ===== */
|
| 1134 |
.nk-lb-wrap { padding: 0 !important; }
|
| 1135 |
.nk-empty-table {
|
|
@@ -4640,60 +4692,45 @@ def plot_significance_forest(per_dataset_df, metric="Accuracy"):
|
|
| 4640 |
# Each card has 3 stacked annotations (kicker / big value / subline).
|
| 4641 |
# Plotly inline `<span style='font-size:..'>` is unreliable, so each
|
| 4642 |
# annotation sets its size via the `font=` parameter instead.
|
| 4643 |
-
|
| 4644 |
-
|
| 4645 |
-
|
| 4646 |
-
|
| 4647 |
-
|
| 4648 |
-
|
| 4649 |
-
|
| 4650 |
-
|
| 4651 |
-
|
| 4652 |
-
|
| 4653 |
-
|
| 4654 |
-
|
| 4655 |
-
|
| 4656 |
-
|
| 4657 |
-
|
| 4658 |
-
|
| 4659 |
-
|
| 4660 |
-
|
| 4661 |
-
|
| 4662 |
-
|
| 4663 |
-
|
| 4664 |
-
|
| 4665 |
-
|
| 4666 |
-
|
| 4667 |
-
|
| 4668 |
-
|
| 4669 |
-
|
| 4670 |
-
|
| 4671 |
-
|
| 4672 |
-
|
| 4673 |
-
|
| 4674 |
-
|
| 4675 |
-
|
| 4676 |
-
|
| 4677 |
-
|
| 4678 |
-
|
| 4679 |
-
|
| 4680 |
-
|
| 4681 |
-
|
| 4682 |
-
fig.add_annotation(
|
| 4683 |
-
text=f"<b>{value}</b>", x=x_center, y=KPI_Y,
|
| 4684 |
-
xref="paper", yref="paper", showarrow=False,
|
| 4685 |
-
xanchor="center", yanchor="middle", align="center",
|
| 4686 |
-
font=dict(size=value_size, color="#060510",
|
| 4687 |
-
family="BDO Grotesk, Helvetica Neue, sans-serif"),
|
| 4688 |
-
)
|
| 4689 |
-
# Sub
|
| 4690 |
-
fig.add_annotation(
|
| 4691 |
-
text=sub, x=x_center, y=KPI_Y - LINE_GAP,
|
| 4692 |
-
xref="paper", yref="paper", showarrow=False,
|
| 4693 |
-
xanchor="center", yanchor="middle", align="center",
|
| 4694 |
-
font=dict(size=12, color="#5d6c7b",
|
| 4695 |
-
family="BDO Grotesk, Helvetica Neue, sans-serif"),
|
| 4696 |
-
)
|
| 4697 |
|
| 4698 |
# Axes
|
| 4699 |
raw_min = min(ci_low.get(m, v) for m, v in zip(y_order, dot_x) if v == v)
|
|
@@ -4746,8 +4783,16 @@ def plot_significance_forest(per_dataset_df, metric="Accuracy"):
|
|
| 4746 |
)
|
| 4747 |
|
| 4748 |
fig.update_layout(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4749 |
height=max(420, 36 * len(y_order) + 240),
|
| 4750 |
-
margin
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4751 |
template="plotly_white",
|
| 4752 |
plot_bgcolor="rgba(0,0,0,0)",
|
| 4753 |
paper_bgcolor="rgba(0,0,0,0)",
|
|
@@ -4764,7 +4809,7 @@ def plot_significance_forest(per_dataset_df, metric="Accuracy"):
|
|
| 4764 |
f" <span style='color:#5d6c7b;font-size:11px'>· 95% CI</span>"),
|
| 4765 |
showarrow=False, font=dict(size=12),
|
| 4766 |
)
|
| 4767 |
-
return fig
|
| 4768 |
|
| 4769 |
|
| 4770 |
def plot_winrate_matrix(per_dataset_df, metric="Accuracy"):
|
|
@@ -5316,11 +5361,16 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 5316 |
.reset_index()
|
| 5317 |
)
|
| 5318 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5319 |
ranking_plot = gr.Plot(
|
| 5320 |
-
value=
|
| 5321 |
-
public_enter_per_dataset,
|
| 5322 |
-
metric="Accuracy",
|
| 5323 |
-
),
|
| 5324 |
show_label=False,
|
| 5325 |
elem_classes=["nk-bar-plot"],
|
| 5326 |
visible=True,
|
|
@@ -5960,10 +6010,13 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 5960 |
# Bars view = significance forest plot. Cached at build
|
| 5961 |
# time (build_figures_cache.py) because the live render
|
| 5962 |
# runs Wilcoxon-Holm + 1000-iter paired bootstrap, ~30 s
|
| 5963 |
-
# on Spaces CPU per metric change.
|
|
|
|
|
|
|
| 5964 |
import plotly.graph_objects as _go
|
| 5965 |
|
| 5966 |
fig = _go.Figure(cache_root["forest"][metric])
|
|
|
|
| 5967 |
return (
|
| 5968 |
gr.update(visible=True, value=fig),
|
| 5969 |
hide,
|
|
@@ -5973,6 +6026,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 5973 |
hide,
|
| 5974 |
hide,
|
| 5975 |
hide,
|
|
|
|
| 5976 |
count_label,
|
| 5977 |
)
|
| 5978 |
|
|
@@ -6017,6 +6071,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6017 |
hide,
|
| 6018 |
hide,
|
| 6019 |
hide,
|
|
|
|
| 6020 |
count_label,
|
| 6021 |
)
|
| 6022 |
|
|
@@ -6041,6 +6096,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6041 |
hide,
|
| 6042 |
hide,
|
| 6043 |
gr.update(visible=True, value=fig),
|
|
|
|
| 6044 |
count_label,
|
| 6045 |
)
|
| 6046 |
|
|
@@ -6060,11 +6116,12 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6060 |
hide,
|
| 6061 |
hide,
|
| 6062 |
hide,
|
|
|
|
| 6063 |
count_label,
|
| 6064 |
)
|
| 6065 |
# Bars view now renders the significance forest plot
|
| 6066 |
# (Wilcoxon-Holm leading-group highlight + CI lollipops).
|
| 6067 |
-
fig = plot_significance_forest(
|
| 6068 |
filtered_per_dataset, metric=metric,
|
| 6069 |
)
|
| 6070 |
return (
|
|
@@ -6076,6 +6133,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6076 |
hide,
|
| 6077 |
hide,
|
| 6078 |
hide,
|
|
|
|
| 6079 |
count_label,
|
| 6080 |
)
|
| 6081 |
|
|
@@ -6090,6 +6148,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6090 |
hide,
|
| 6091 |
hide,
|
| 6092 |
gr.update(visible=True, value=None),
|
|
|
|
| 6093 |
count_label,
|
| 6094 |
)
|
| 6095 |
fig = plot_winrate_matrix(filtered_per_dataset, metric=metric)
|
|
@@ -6102,6 +6161,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6102 |
hide,
|
| 6103 |
hide,
|
| 6104 |
gr.update(visible=True, value=fig),
|
|
|
|
| 6105 |
count_label,
|
| 6106 |
)
|
| 6107 |
|
|
@@ -6125,6 +6185,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6125 |
hide,
|
| 6126 |
hide,
|
| 6127 |
hide,
|
|
|
|
| 6128 |
count_label,
|
| 6129 |
)
|
| 6130 |
|
|
@@ -6145,6 +6206,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6145 |
hide,
|
| 6146 |
hide,
|
| 6147 |
hide,
|
|
|
|
| 6148 |
count_label,
|
| 6149 |
)
|
| 6150 |
fig = plot_dataset_comparison(
|
|
@@ -6159,6 +6221,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6159 |
hide,
|
| 6160 |
hide,
|
| 6161 |
hide,
|
|
|
|
| 6162 |
count_label,
|
| 6163 |
)
|
| 6164 |
|
|
@@ -6173,6 +6236,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6173 |
hide,
|
| 6174 |
hide,
|
| 6175 |
hide,
|
|
|
|
| 6176 |
count_label,
|
| 6177 |
)
|
| 6178 |
overlay_mm, sm_mm, overlay_rk, sm_rk = plot_model_radars(
|
|
@@ -6187,6 +6251,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6187 |
gr.update(visible=True, value=overlay_rk),
|
| 6188 |
gr.update(visible=True, value=sm_rk),
|
| 6189 |
hide,
|
|
|
|
| 6190 |
count_label,
|
| 6191 |
)
|
| 6192 |
|
|
@@ -6200,6 +6265,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6200 |
hide,
|
| 6201 |
hide,
|
| 6202 |
hide,
|
|
|
|
| 6203 |
count_label,
|
| 6204 |
)
|
| 6205 |
|
|
@@ -6233,6 +6299,7 @@ with gr.Blocks(css=css, theme=nk_theme, head=NAV_HEAD_JS) as demo:
|
|
| 6233 |
radar_overlay_rank_plot,
|
| 6234 |
radar_grid_rank_plot,
|
| 6235 |
winrate_plot,
|
|
|
|
| 6236 |
dataset_count_md,
|
| 6237 |
],
|
| 6238 |
)
|
|
|
|
| 1130 |
.nk-bar-plot .modebar-container,
|
| 1131 |
.nk-bar-plot .js-plotly-plot .plotly .modebar { display: none !important; }
|
| 1132 |
|
| 1133 |
+
/* No special width constraint on the forest plot: Plotly autosize fills
|
| 1134 |
+
the container and the chart redraws on window resize. The KPI cards live
|
| 1135 |
+
in a sibling HTML row (.nk-bar-kpis), not inside the figure, so they get
|
| 1136 |
+
real CSS flexbox responsiveness. */
|
| 1137 |
+
.nk-bar-kpi-wrap { padding: 0 !important; }
|
| 1138 |
+
.nk-bar-kpis {
|
| 1139 |
+
display: flex;
|
| 1140 |
+
gap: 12px;
|
| 1141 |
+
flex-wrap: wrap;
|
| 1142 |
+
padding: 0 8px 12px;
|
| 1143 |
+
justify-content: center;
|
| 1144 |
+
}
|
| 1145 |
+
.nk-bar-kpi {
|
| 1146 |
+
flex: 1 1 240px;
|
| 1147 |
+
min-width: 200px;
|
| 1148 |
+
max-width: 360px;
|
| 1149 |
+
background: #f7f7fa;
|
| 1150 |
+
border: 1px solid #e6e4ef;
|
| 1151 |
+
border-radius: 8px;
|
| 1152 |
+
padding: 12px 16px;
|
| 1153 |
+
text-align: center;
|
| 1154 |
+
overflow: hidden;
|
| 1155 |
+
}
|
| 1156 |
+
.nk-bar-kpi-kicker {
|
| 1157 |
+
font-family: "Geist Mono", ui-monospace, monospace;
|
| 1158 |
+
font-size: 11px;
|
| 1159 |
+
color: #5d6c7b;
|
| 1160 |
+
letter-spacing: 0.04em;
|
| 1161 |
+
margin-bottom: 4px;
|
| 1162 |
+
}
|
| 1163 |
+
.nk-bar-kpi-value {
|
| 1164 |
+
font-family: "BDO Grotesk", "Helvetica Neue", sans-serif;
|
| 1165 |
+
font-size: 20px;
|
| 1166 |
+
font-weight: 700;
|
| 1167 |
+
color: #060510;
|
| 1168 |
+
line-height: 1.2;
|
| 1169 |
+
word-break: break-word;
|
| 1170 |
+
margin-bottom: 4px;
|
| 1171 |
+
}
|
| 1172 |
+
.nk-bar-kpi-sub {
|
| 1173 |
+
font-family: "BDO Grotesk", "Helvetica Neue", sans-serif;
|
| 1174 |
+
font-size: 11px;
|
| 1175 |
+
color: #5d6c7b;
|
| 1176 |
+
}
|
| 1177 |
+
body.dark .nk-bar-kpi {
|
| 1178 |
+
background: rgba(40, 35, 70, 0.4);
|
| 1179 |
+
border-color: rgba(255, 255, 255, 0.1);
|
| 1180 |
+
}
|
| 1181 |
+
body.dark .nk-bar-kpi-value { color: #f5f3ff; }
|
| 1182 |
+
body.dark .nk-bar-kpi-kicker,
|
| 1183 |
+
body.dark .nk-bar-kpi-sub { color: #b8b0d0; }
|
| 1184 |
+
|
| 1185 |
/* ===== Leaderboard table view (HTML, with per-cell rank badge) ===== */
|
| 1186 |
.nk-lb-wrap { padding: 0 !important; }
|
| 1187 |
.nk-empty-table {
|
|
|
|
| 4692 |
# Each card has 3 stacked annotations (kicker / big value / subline).
|
| 4693 |
# Plotly inline `<span style='font-size:..'>` is unreliable, so each
|
| 4694 |
# annotation sets its size via the `font=` parameter instead.
|
| 4695 |
+
#
|
| 4696 |
+
# Responsive sizing: the card pill is sized in paper fractions (so it
|
| 4697 |
+
# shrinks with the plot width), but the *text* is sized in pixels. To
|
| 4698 |
+
# avoid overflow on narrow plots, we shrink the value/sub text more
|
| 4699 |
+
# aggressively for long content AND truncate the LEADING GROUP value
|
| 4700 |
+
# to at most 2 model names (was 3) so common cases fit even at small
|
| 4701 |
+
# widths.
|
| 4702 |
+
# KPI cards: rendered as a sibling HTML block (not inside the figure),
|
| 4703 |
+
# so they get native CSS flexbox responsiveness and resize fluidly with
|
| 4704 |
+
# the window. The HTML is returned alongside the figure.
|
| 4705 |
+
if n_leaders == 1:
|
| 4706 |
+
kpi_group_value_display = leader_names_pretty[0]
|
| 4707 |
+
elif n_leaders == 2:
|
| 4708 |
+
kpi_group_value_display = " · ".join(leader_names_pretty)
|
| 4709 |
+
else:
|
| 4710 |
+
kpi_group_value_display = " · ".join(leader_names_pretty[:2]) + " …"
|
| 4711 |
+
|
| 4712 |
+
import html as _html
|
| 4713 |
+
def _esc(s):
|
| 4714 |
+
return _html.escape(str(s))
|
| 4715 |
+
kpi_html = (
|
| 4716 |
+
'<div class="nk-bar-kpis">'
|
| 4717 |
+
' <div class="nk-bar-kpi">'
|
| 4718 |
+
f' <div class="nk-bar-kpi-kicker">LEADING GROUP</div>'
|
| 4719 |
+
f' <div class="nk-bar-kpi-value">{_esc(kpi_group_value_display)}</div>'
|
| 4720 |
+
f' <div class="nk-bar-kpi-sub">{_esc(kpi_group_sub)}</div>'
|
| 4721 |
+
' </div>'
|
| 4722 |
+
' <div class="nk-bar-kpi">'
|
| 4723 |
+
f' <div class="nk-bar-kpi-kicker">LEAD MARGIN</div>'
|
| 4724 |
+
f' <div class="nk-bar-kpi-value">{_esc(kpi_margin_value)}</div>'
|
| 4725 |
+
f' <div class="nk-bar-kpi-sub">{_esc(kpi_margin_sub)}</div>'
|
| 4726 |
+
' </div>'
|
| 4727 |
+
' <div class="nk-bar-kpi">'
|
| 4728 |
+
f' <div class="nk-bar-kpi-kicker">TOP-3 COVERAGE</div>'
|
| 4729 |
+
f' <div class="nk-bar-kpi-value">{_esc(kpi_top3_value)}</div>'
|
| 4730 |
+
f' <div class="nk-bar-kpi-sub">{_esc(kpi_top3_sub)}</div>'
|
| 4731 |
+
' </div>'
|
| 4732 |
+
'</div>'
|
| 4733 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4734 |
|
| 4735 |
# Axes
|
| 4736 |
raw_min = min(ci_low.get(m, v) for m, v in zip(y_order, dot_x) if v == v)
|
|
|
|
| 4783 |
)
|
| 4784 |
|
| 4785 |
fig.update_layout(
|
| 4786 |
+
# Autosize: the figure fills its container at full width and shrinks
|
| 4787 |
+
# down to the min-width set by .nk-bar-plot's CSS rule (820px); below
|
| 4788 |
+
# that the wrapper scrolls horizontally rather than compressing.
|
| 4789 |
+
autosize=True,
|
| 4790 |
height=max(420, 36 * len(y_order) + 240),
|
| 4791 |
+
# Left margin holds the model labels (drawn at paper-x=0 with
|
| 4792 |
+
# xshift=-18). 140px is enough for the longest model name (~12 chars
|
| 4793 |
+
# at 12px monospace ≈ 86px) plus the 18px gap. Top margin was 180px
|
| 4794 |
+
# to host the KPI cards; now 40px since the cards are gone.
|
| 4795 |
+
margin=dict(l=140, r=90, t=40, b=70),
|
| 4796 |
template="plotly_white",
|
| 4797 |
plot_bgcolor="rgba(0,0,0,0)",
|
| 4798 |
paper_bgcolor="rgba(0,0,0,0)",
|
|
|
|
| 4809 |
f" <span style='color:#5d6c7b;font-size:11px'>· 95% CI</span>"),
|
| 4810 |
showarrow=False, font=dict(size=12),
|
| 4811 |
)
|
| 4812 |
+
return fig, kpi_html
|
| 4813 |
|
| 4814 |
|
| 4815 |
def plot_winrate_matrix(per_dataset_df, metric="Accuracy"):
|
|
|
|
| 5361 |
.reset_index()
|
| 5362 |
)
|
| 5363 |
|
| 5364 |
+
_init_forest_fig, _init_forest_kpi = plot_significance_forest(
|
| 5365 |
+
public_enter_per_dataset, metric="Accuracy",
|
| 5366 |
+
)
|
| 5367 |
+
ranking_kpi_html = gr.HTML(
|
| 5368 |
+
value=_init_forest_kpi,
|
| 5369 |
+
elem_classes=["nk-bar-kpi-wrap"],
|
| 5370 |
+
visible=True,
|
| 5371 |
+
)
|
| 5372 |
ranking_plot = gr.Plot(
|
| 5373 |
+
value=_init_forest_fig,
|
|
|
|
|
|
|
|
|
|
| 5374 |
show_label=False,
|
| 5375 |
elem_classes=["nk-bar-plot"],
|
| 5376 |
visible=True,
|
|
|
|
| 6010 |
# Bars view = significance forest plot. Cached at build
|
| 6011 |
# time (build_figures_cache.py) because the live render
|
| 6012 |
# runs Wilcoxon-Holm + 1000-iter paired bootstrap, ~30 s
|
| 6013 |
+
# on Spaces CPU per metric change. The KPI HTML is
|
| 6014 |
+
# served from the cache when present; otherwise we fall
|
| 6015 |
+
# back to live recompute (slow path).
|
| 6016 |
import plotly.graph_objects as _go
|
| 6017 |
|
| 6018 |
fig = _go.Figure(cache_root["forest"][metric])
|
| 6019 |
+
kpi_html_cached = cache_root.get("forest_kpi", {}).get(metric, "")
|
| 6020 |
return (
|
| 6021 |
gr.update(visible=True, value=fig),
|
| 6022 |
hide,
|
|
|
|
| 6026 |
hide,
|
| 6027 |
hide,
|
| 6028 |
hide,
|
| 6029 |
+
gr.update(visible=True, value=kpi_html_cached),
|
| 6030 |
count_label,
|
| 6031 |
)
|
| 6032 |
|
|
|
|
| 6071 |
hide,
|
| 6072 |
hide,
|
| 6073 |
hide,
|
| 6074 |
+
gr.update(),
|
| 6075 |
count_label,
|
| 6076 |
)
|
| 6077 |
|
|
|
|
| 6096 |
hide,
|
| 6097 |
hide,
|
| 6098 |
gr.update(visible=True, value=fig),
|
| 6099 |
+
gr.update(),
|
| 6100 |
count_label,
|
| 6101 |
)
|
| 6102 |
|
|
|
|
| 6116 |
hide,
|
| 6117 |
hide,
|
| 6118 |
hide,
|
| 6119 |
+
gr.update(),
|
| 6120 |
count_label,
|
| 6121 |
)
|
| 6122 |
# Bars view now renders the significance forest plot
|
| 6123 |
# (Wilcoxon-Holm leading-group highlight + CI lollipops).
|
| 6124 |
+
fig, kpi_html = plot_significance_forest(
|
| 6125 |
filtered_per_dataset, metric=metric,
|
| 6126 |
)
|
| 6127 |
return (
|
|
|
|
| 6133 |
hide,
|
| 6134 |
hide,
|
| 6135 |
hide,
|
| 6136 |
+
gr.update(visible=True, value=kpi_html),
|
| 6137 |
count_label,
|
| 6138 |
)
|
| 6139 |
|
|
|
|
| 6148 |
hide,
|
| 6149 |
hide,
|
| 6150 |
gr.update(visible=True, value=None),
|
| 6151 |
+
gr.update(),
|
| 6152 |
count_label,
|
| 6153 |
)
|
| 6154 |
fig = plot_winrate_matrix(filtered_per_dataset, metric=metric)
|
|
|
|
| 6161 |
hide,
|
| 6162 |
hide,
|
| 6163 |
gr.update(visible=True, value=fig),
|
| 6164 |
+
gr.update(),
|
| 6165 |
count_label,
|
| 6166 |
)
|
| 6167 |
|
|
|
|
| 6185 |
hide,
|
| 6186 |
hide,
|
| 6187 |
hide,
|
| 6188 |
+
gr.update(),
|
| 6189 |
count_label,
|
| 6190 |
)
|
| 6191 |
|
|
|
|
| 6206 |
hide,
|
| 6207 |
hide,
|
| 6208 |
hide,
|
| 6209 |
+
gr.update(),
|
| 6210 |
count_label,
|
| 6211 |
)
|
| 6212 |
fig = plot_dataset_comparison(
|
|
|
|
| 6221 |
hide,
|
| 6222 |
hide,
|
| 6223 |
hide,
|
| 6224 |
+
gr.update(),
|
| 6225 |
count_label,
|
| 6226 |
)
|
| 6227 |
|
|
|
|
| 6236 |
hide,
|
| 6237 |
hide,
|
| 6238 |
hide,
|
| 6239 |
+
gr.update(),
|
| 6240 |
count_label,
|
| 6241 |
)
|
| 6242 |
overlay_mm, sm_mm, overlay_rk, sm_rk = plot_model_radars(
|
|
|
|
| 6251 |
gr.update(visible=True, value=overlay_rk),
|
| 6252 |
gr.update(visible=True, value=sm_rk),
|
| 6253 |
hide,
|
| 6254 |
+
gr.update(),
|
| 6255 |
count_label,
|
| 6256 |
)
|
| 6257 |
|
|
|
|
| 6265 |
hide,
|
| 6266 |
hide,
|
| 6267 |
hide,
|
| 6268 |
+
gr.update(),
|
| 6269 |
count_label,
|
| 6270 |
)
|
| 6271 |
|
|
|
|
| 6299 |
radar_overlay_rank_plot,
|
| 6300 |
radar_grid_rank_plot,
|
| 6301 |
winrate_plot,
|
| 6302 |
+
ranking_kpi_html,
|
| 6303 |
dataset_count_md,
|
| 6304 |
],
|
| 6305 |
)
|
build_figures_cache.py
CHANGED
|
@@ -101,17 +101,19 @@ def build_one(benchmark, industry, metric):
|
|
| 101 |
# Forest plot (Wilcoxon-Holm leading-group highlight + paired-bootstrap CI).
|
| 102 |
# This is the slow figure on live render (B=1000 bootstrap × all model pairs),
|
| 103 |
# so pre-caching it is the whole point of this build script for the Bars view.
|
|
|
|
| 104 |
try:
|
| 105 |
-
forest = plot_significance_forest(df_display, metric=metric)
|
| 106 |
except Exception as _e:
|
| 107 |
print(f" [warn] forest plot failed for bench={benchmark} ind={industry} metric={metric}: {_e}")
|
| 108 |
-
forest = None
|
| 109 |
# Win-rate matrix should ALSO use the display-filtered frame so the
|
| 110 |
# heatmap doesn't show duplicate display names (single-fit vs ensemble).
|
| 111 |
winrate = plot_winrate_matrix(df_display, metric=metric) if metric not in ("Elo_score", "%↗ over XGBoost") else None
|
| 112 |
return (
|
| 113 |
bars.to_plotly_json(),
|
| 114 |
forest.to_plotly_json() if forest is not None else None,
|
|
|
|
| 115 |
winrate.to_plotly_json() if winrate is not None else None,
|
| 116 |
agg.round(6).to_dict(orient="records"),
|
| 117 |
int(df_display['dataset_id'].nunique()),
|
|
@@ -132,14 +134,17 @@ for benchmark in BENCHMARKS:
|
|
| 132 |
cache[benchmark] = {}
|
| 133 |
for industry in INDUSTRY_BUCKETS:
|
| 134 |
cache[benchmark][industry] = {
|
| 135 |
-
"metrics": {}, "forest": {}, "
|
|
|
|
| 136 |
}
|
| 137 |
for metric in METRICS:
|
| 138 |
i += 1
|
| 139 |
-
bars_dict, forest_dict, winrate_dict, agg, n = build_one(benchmark, industry, metric)
|
| 140 |
cache[benchmark][industry]["metrics"][metric] = bars_dict
|
| 141 |
if forest_dict is not None:
|
| 142 |
cache[benchmark][industry]["forest"][metric] = forest_dict
|
|
|
|
|
|
|
| 143 |
if winrate_dict is not None:
|
| 144 |
cache[benchmark][industry]["winrate"][metric] = winrate_dict
|
| 145 |
cache[benchmark][industry]["n_datasets"] = n
|
|
|
|
| 101 |
# Forest plot (Wilcoxon-Holm leading-group highlight + paired-bootstrap CI).
|
| 102 |
# This is the slow figure on live render (B=1000 bootstrap × all model pairs),
|
| 103 |
# so pre-caching it is the whole point of this build script for the Bars view.
|
| 104 |
+
# Returns (figure, kpi_html) — both go in the cache.
|
| 105 |
try:
|
| 106 |
+
forest, forest_kpi = plot_significance_forest(df_display, metric=metric)
|
| 107 |
except Exception as _e:
|
| 108 |
print(f" [warn] forest plot failed for bench={benchmark} ind={industry} metric={metric}: {_e}")
|
| 109 |
+
forest, forest_kpi = None, None
|
| 110 |
# Win-rate matrix should ALSO use the display-filtered frame so the
|
| 111 |
# heatmap doesn't show duplicate display names (single-fit vs ensemble).
|
| 112 |
winrate = plot_winrate_matrix(df_display, metric=metric) if metric not in ("Elo_score", "%↗ over XGBoost") else None
|
| 113 |
return (
|
| 114 |
bars.to_plotly_json(),
|
| 115 |
forest.to_plotly_json() if forest is not None else None,
|
| 116 |
+
forest_kpi,
|
| 117 |
winrate.to_plotly_json() if winrate is not None else None,
|
| 118 |
agg.round(6).to_dict(orient="records"),
|
| 119 |
int(df_display['dataset_id'].nunique()),
|
|
|
|
| 134 |
cache[benchmark] = {}
|
| 135 |
for industry in INDUSTRY_BUCKETS:
|
| 136 |
cache[benchmark][industry] = {
|
| 137 |
+
"metrics": {}, "forest": {}, "forest_kpi": {}, "winrate": {},
|
| 138 |
+
"n_datasets": 0, "table": [],
|
| 139 |
}
|
| 140 |
for metric in METRICS:
|
| 141 |
i += 1
|
| 142 |
+
bars_dict, forest_dict, forest_kpi_html, winrate_dict, agg, n = build_one(benchmark, industry, metric)
|
| 143 |
cache[benchmark][industry]["metrics"][metric] = bars_dict
|
| 144 |
if forest_dict is not None:
|
| 145 |
cache[benchmark][industry]["forest"][metric] = forest_dict
|
| 146 |
+
if forest_kpi_html is not None:
|
| 147 |
+
cache[benchmark][industry]["forest_kpi"][metric] = forest_kpi_html
|
| 148 |
if winrate_dict is not None:
|
| 149 |
cache[benchmark][industry]["winrate"][metric] = winrate_dict
|
| 150 |
cache[benchmark][industry]["n_datasets"] = n
|
perfmap_hulls_override.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
| 19 |
"46955",
|
| 20 |
"255",
|
| 21 |
"137",
|
| 22 |
-
"
|
| 23 |
"44186",
|
| 24 |
"44130",
|
| 25 |
"40497",
|
|
|
|
| 19 |
"46955",
|
| 20 |
"255",
|
| 21 |
"137",
|
| 22 |
+
"41705",
|
| 23 |
"44186",
|
| 24 |
"44130",
|
| 25 |
"40497",
|