corbanyax commited on
Commit
77c7374
·
verified ·
1 Parent(s): 123cdfa

Add toggleable metric charts

Browse files
Files changed (1) hide show
  1. app.py +120 -18
app.py CHANGED
@@ -13,6 +13,7 @@ import numpy as np
13
  import pandas as pd
14
  import plotly.express as px
15
  import plotly.graph_objects as go
 
16
  from sklearn.base import clone
17
  from sklearn.compose import ColumnTransformer
18
  from sklearn.datasets import fetch_california_housing, fetch_openml, load_breast_cancer, load_diabetes, load_digits, load_iris, load_wine, make_classification
@@ -37,6 +38,8 @@ APP_TITLE = "tabBench"
37
  TABFM_MODEL_ID = "google/tabfm-1.0.0-pytorch"
38
  RANDOM_STATE = 42
39
  CANDY_DATA_URL = "https://raw.githubusercontent.com/fivethirtyeight/data/master/candy-power-ranking/candy-data.csv"
 
 
40
 
41
 
42
  @dataclass(frozen=True)
@@ -609,19 +612,83 @@ def benchmark_frame(
609
  return results[["rank", "model", "status", *metric_cols]], preview, summary
610
 
611
 
612
- def metric_chart(results: pd.DataFrame) -> go.Figure:
613
  if results is None or results.empty:
614
  return go.Figure()
615
- long_cols = [c for c in ["accuracy", "f1_weighted", "roc_auc", "r2"] if c in results.columns and results[c].notna().any()]
616
- if long_cols:
617
- long = results.melt(id_vars=["model"], value_vars=long_cols, var_name="metric", value_name="score")
618
- fig = px.bar(long, x="model", y="score", color="metric", barmode="group", color_discrete_sequence=["#f97316", "#8b5cf6", "#14b8a6", "#2563eb"])
619
- fig.update_yaxes(range=[0, 1])
620
- else:
621
- long_cols = [c for c in ["rmse", "mae"] if c in results.columns and results[c].notna().any()]
622
- long = results.melt(id_vars=["model"], value_vars=long_cols, var_name="metric", value_name="score")
623
- fig = px.bar(long, x="model", y="score", color="metric", barmode="group", color_discrete_sequence=["#f97316", "#8b5cf6"])
624
- fig.update_layout(template="plotly_white", height=360, margin=dict(l=20, r=20, t=25, b=20), legend_title_text="")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  return fig
626
 
627
 
@@ -640,20 +707,46 @@ def time_chart(results: pd.DataFrame) -> go.Figure:
640
  return fig
641
 
642
 
643
- def run_catalog(dataset_name: str, sample_size: int, test_percent: int, seed: int, selected_models: list[str], include_tabfm: bool):
 
 
 
 
 
 
 
 
 
644
  spec = get_spec(dataset_name)
645
  df = get_dataset(dataset_name, sample_size, seed)
646
  results, preview, summary = benchmark_frame(df, spec.target, spec.task, sample_size, test_percent / 100, seed, selected_models, include_tabfm)
647
- return summary, results.round(4), metric_chart(results), time_chart(results), preview
648
 
649
 
650
- def run_upload(file, target: str, task: str, sample_size: int, test_percent: int, seed: int, selected_models: list[str], include_tabfm: bool):
 
 
 
 
 
 
 
 
 
 
 
651
  if file is None:
652
  raise gr.Error("Upload a CSV file first.")
653
  df = pd.read_csv(file.name)
654
  selected_task = None if task == "Auto" else task.lower()
655
  results, preview, summary = benchmark_frame(df, target, selected_task, sample_size, test_percent / 100, seed, selected_models, include_tabfm)
656
- return summary, results.round(4), metric_chart(results), time_chart(results), preview
 
 
 
 
 
 
657
 
658
 
659
  def catalog_table() -> pd.DataFrame:
@@ -714,6 +807,8 @@ def build_app() -> gr.Blocks:
714
  seed = gr.Number(value=42, precision=0, label="Random seed")
715
  models = gr.CheckboxGroup(DEFAULT_MODELS, value=["RandomForest", "HistGradientBoosting", "XGBoost", "Dummy"], label="Baselines")
716
  include_tabfm = gr.Checkbox(value=False, label="Run TabFM live")
 
 
717
  run_btn = gr.Button("Run benchmark", variant="primary")
718
  with gr.Column(scale=3):
719
  summary = gr.Markdown()
@@ -722,8 +817,11 @@ def build_app() -> gr.Blocks:
722
  chart = gr.Plot(label="Metric comparison")
723
  speed = gr.Plot(label="Speed")
724
  preview = gr.Dataframe(label="Held-out preview", interactive=False)
725
- run_btn.click(run_catalog, [dataset, sample, test_pct, seed, models, include_tabfm], [summary, leaderboard, chart, speed, preview])
726
- demo.load(run_catalog, [dataset, sample, test_pct, seed, models, include_tabfm], [summary, leaderboard, chart, speed, preview])
 
 
 
727
  with gr.Tab("Upload Dataset"):
728
  with gr.Row():
729
  with gr.Column(scale=1, elem_classes=["panel"]):
@@ -735,6 +833,8 @@ def build_app() -> gr.Blocks:
735
  upload_seed = gr.Number(value=42, precision=0, label="Random seed")
736
  upload_models = gr.CheckboxGroup(DEFAULT_MODELS, value=["RandomForest", "HistGradientBoosting", "XGBoost", "Dummy"], label="Baselines")
737
  upload_tabfm = gr.Checkbox(value=False, label="Run TabFM live")
 
 
738
  upload_btn = gr.Button("Run uploaded dataset", variant="primary")
739
  with gr.Column(scale=3):
740
  upload_summary = gr.Markdown()
@@ -745,9 +845,11 @@ def build_app() -> gr.Blocks:
745
  upload_preview = gr.Dataframe(label="Held-out preview", interactive=False)
746
  upload_btn.click(
747
  run_upload,
748
- [file, target, task, upload_sample, upload_test_pct, upload_seed, upload_models, upload_tabfm],
749
  [upload_summary, upload_leaderboard, upload_chart, upload_speed, upload_preview],
750
  )
 
 
751
  with gr.Tab("Dataset Catalog"):
752
  gr.Dataframe(catalog_table(), interactive=False, label="Included benchmark catalog")
753
  gr.Markdown(
 
13
  import pandas as pd
14
  import plotly.express as px
15
  import plotly.graph_objects as go
16
+ from plotly.subplots import make_subplots
17
  from sklearn.base import clone
18
  from sklearn.compose import ColumnTransformer
19
  from sklearn.datasets import fetch_california_housing, fetch_openml, load_breast_cancer, load_diabetes, load_digits, load_iris, load_wine, make_classification
 
38
  TABFM_MODEL_ID = "google/tabfm-1.0.0-pytorch"
39
  RANDOM_STATE = 42
40
  CANDY_DATA_URL = "https://raw.githubusercontent.com/fivethirtyeight/data/master/candy-power-ranking/candy-data.csv"
41
+ GOOGLE_COLORS = ["#4285F4", "#DB4437", "#F4B400", "#0F9D58", "#A142F4", "#00ACC1"]
42
+ METRIC_CHOICES = ["accuracy", "f1_weighted", "roc_auc", "rmse", "mae", "r2", "seconds"]
43
 
44
 
45
  @dataclass(frozen=True)
 
612
  return results[["rank", "model", "status", *metric_cols]], preview, summary
613
 
614
 
615
+ def metric_chart(results: pd.DataFrame, selected_metrics: list[str] | None = None, chart_style: str = "Line") -> go.Figure:
616
  if results is None or results.empty:
617
  return go.Figure()
618
+ selected_metrics = selected_metrics or METRIC_CHOICES
619
+ metric_cols = [c for c in selected_metrics if c in results.columns and results[c].notna().any()]
620
+ if not metric_cols:
621
+ metric_cols = [c for c in METRIC_CHOICES if c in results.columns and results[c].notna().any()]
622
+ if not metric_cols:
623
+ return go.Figure()
624
+
625
+ clean = results.sort_values("rank") if "rank" in results.columns else results.copy()
626
+ x_labels = clean["model"].astype(str).tolist()
627
+ if chart_style == "Radar":
628
+ normalized = clean[["model", *metric_cols]].copy()
629
+ for metric in metric_cols:
630
+ values = pd.to_numeric(normalized[metric], errors="coerce")
631
+ lo, hi = values.min(), values.max()
632
+ if pd.isna(lo) or pd.isna(hi) or hi == lo:
633
+ normalized[metric] = 0.5
634
+ elif metric in {"rmse", "mae", "seconds"}:
635
+ normalized[metric] = 1 - ((values - lo) / (hi - lo))
636
+ else:
637
+ normalized[metric] = (values - lo) / (hi - lo)
638
+ fig = go.Figure()
639
+ theta = metric_cols + [metric_cols[0]]
640
+ for idx, row in normalized.iterrows():
641
+ values = [row[m] for m in metric_cols] + [row[metric_cols[0]]]
642
+ fig.add_trace(
643
+ go.Scatterpolar(
644
+ r=values,
645
+ theta=theta,
646
+ fill="toself",
647
+ name=str(row["model"]),
648
+ line=dict(color=GOOGLE_COLORS[idx % len(GOOGLE_COLORS)], width=2),
649
+ opacity=0.78,
650
+ )
651
+ )
652
+ fig.update_layout(
653
+ template="plotly_white",
654
+ height=420,
655
+ margin=dict(l=35, r=35, t=35, b=25),
656
+ polar=dict(radialaxis=dict(visible=True, range=[0, 1])),
657
+ legend_title_text="Model",
658
+ title="Normalized metric shape (higher is better)",
659
+ )
660
+ return fig
661
+
662
+ fig = make_subplots(
663
+ rows=len(metric_cols),
664
+ cols=1,
665
+ shared_xaxes=True,
666
+ vertical_spacing=0.08,
667
+ subplot_titles=[metric.replace("_", " ").upper() for metric in metric_cols],
668
+ )
669
+ for idx, metric in enumerate(metric_cols, start=1):
670
+ fig.add_trace(
671
+ go.Scatter(
672
+ x=x_labels,
673
+ y=clean[metric],
674
+ mode="lines+markers",
675
+ name=metric,
676
+ line=dict(color=GOOGLE_COLORS[(idx - 1) % len(GOOGLE_COLORS)], width=3, shape="spline"),
677
+ marker=dict(size=9, line=dict(color="white", width=1.5)),
678
+ hovertemplate=f"<b>%{{x}}</b><br>{metric}: %{{y:.4f}}<extra></extra>",
679
+ ),
680
+ row=idx,
681
+ col=1,
682
+ )
683
+ if metric in {"accuracy", "f1_weighted", "roc_auc", "r2"}:
684
+ fig.update_yaxes(range=[min(0, float(clean[metric].min()) - 0.05), 1.02], row=idx, col=1)
685
+ fig.update_layout(
686
+ template="plotly_white",
687
+ height=max(320, 185 * len(metric_cols)),
688
+ margin=dict(l=30, r=20, t=40, b=35),
689
+ showlegend=False,
690
+ hovermode="x unified",
691
+ )
692
  return fig
693
 
694
 
 
707
  return fig
708
 
709
 
710
+ def run_catalog(
711
+ dataset_name: str,
712
+ sample_size: int,
713
+ test_percent: int,
714
+ seed: int,
715
+ selected_models: list[str],
716
+ include_tabfm: bool,
717
+ selected_metrics: list[str],
718
+ chart_style: str,
719
+ ):
720
  spec = get_spec(dataset_name)
721
  df = get_dataset(dataset_name, sample_size, seed)
722
  results, preview, summary = benchmark_frame(df, spec.target, spec.task, sample_size, test_percent / 100, seed, selected_models, include_tabfm)
723
+ return summary, results.round(4), metric_chart(results, selected_metrics, chart_style), time_chart(results), preview
724
 
725
 
726
+ def run_upload(
727
+ file,
728
+ target: str,
729
+ task: str,
730
+ sample_size: int,
731
+ test_percent: int,
732
+ seed: int,
733
+ selected_models: list[str],
734
+ include_tabfm: bool,
735
+ selected_metrics: list[str],
736
+ chart_style: str,
737
+ ):
738
  if file is None:
739
  raise gr.Error("Upload a CSV file first.")
740
  df = pd.read_csv(file.name)
741
  selected_task = None if task == "Auto" else task.lower()
742
  results, preview, summary = benchmark_frame(df, target, selected_task, sample_size, test_percent / 100, seed, selected_models, include_tabfm)
743
+ return summary, results.round(4), metric_chart(results, selected_metrics, chart_style), time_chart(results), preview
744
+
745
+
746
+ def redraw_metric_chart(results: pd.DataFrame, selected_metrics: list[str], chart_style: str):
747
+ if results is None or len(results) == 0:
748
+ return go.Figure()
749
+ return metric_chart(pd.DataFrame(results), selected_metrics, chart_style)
750
 
751
 
752
  def catalog_table() -> pd.DataFrame:
 
807
  seed = gr.Number(value=42, precision=0, label="Random seed")
808
  models = gr.CheckboxGroup(DEFAULT_MODELS, value=["RandomForest", "HistGradientBoosting", "XGBoost", "Dummy"], label="Baselines")
809
  include_tabfm = gr.Checkbox(value=False, label="Run TabFM live")
810
+ metric_toggles = gr.CheckboxGroup(METRIC_CHOICES, value=["rmse", "mae", "r2", "accuracy", "f1_weighted", "roc_auc"], label="Chart metrics")
811
+ chart_style = gr.Radio(["Line", "Radar"], value="Line", label="Chart style")
812
  run_btn = gr.Button("Run benchmark", variant="primary")
813
  with gr.Column(scale=3):
814
  summary = gr.Markdown()
 
817
  chart = gr.Plot(label="Metric comparison")
818
  speed = gr.Plot(label="Speed")
819
  preview = gr.Dataframe(label="Held-out preview", interactive=False)
820
+ run_inputs = [dataset, sample, test_pct, seed, models, include_tabfm, metric_toggles, chart_style]
821
+ run_btn.click(run_catalog, run_inputs, [summary, leaderboard, chart, speed, preview])
822
+ metric_toggles.change(redraw_metric_chart, [leaderboard, metric_toggles, chart_style], chart)
823
+ chart_style.change(redraw_metric_chart, [leaderboard, metric_toggles, chart_style], chart)
824
+ demo.load(run_catalog, run_inputs, [summary, leaderboard, chart, speed, preview])
825
  with gr.Tab("Upload Dataset"):
826
  with gr.Row():
827
  with gr.Column(scale=1, elem_classes=["panel"]):
 
833
  upload_seed = gr.Number(value=42, precision=0, label="Random seed")
834
  upload_models = gr.CheckboxGroup(DEFAULT_MODELS, value=["RandomForest", "HistGradientBoosting", "XGBoost", "Dummy"], label="Baselines")
835
  upload_tabfm = gr.Checkbox(value=False, label="Run TabFM live")
836
+ upload_metric_toggles = gr.CheckboxGroup(METRIC_CHOICES, value=["rmse", "mae", "r2", "accuracy", "f1_weighted", "roc_auc"], label="Chart metrics")
837
+ upload_chart_style = gr.Radio(["Line", "Radar"], value="Line", label="Chart style")
838
  upload_btn = gr.Button("Run uploaded dataset", variant="primary")
839
  with gr.Column(scale=3):
840
  upload_summary = gr.Markdown()
 
845
  upload_preview = gr.Dataframe(label="Held-out preview", interactive=False)
846
  upload_btn.click(
847
  run_upload,
848
+ [file, target, task, upload_sample, upload_test_pct, upload_seed, upload_models, upload_tabfm, upload_metric_toggles, upload_chart_style],
849
  [upload_summary, upload_leaderboard, upload_chart, upload_speed, upload_preview],
850
  )
851
+ upload_metric_toggles.change(redraw_metric_chart, [upload_leaderboard, upload_metric_toggles, upload_chart_style], upload_chart)
852
+ upload_chart_style.change(redraw_metric_chart, [upload_leaderboard, upload_metric_toggles, upload_chart_style], upload_chart)
853
  with gr.Tab("Dataset Catalog"):
854
  gr.Dataframe(catalog_table(), interactive=False, label="Included benchmark catalog")
855
  gr.Markdown(