richiam commited on
Commit
63fd4c9
·
verified ·
1 Parent(s): b236ae9

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +49 -21
app.py CHANGED
@@ -768,18 +768,30 @@ _METRIC_LABELS = {
768
  }
769
 
770
 
771
- def _build_grid_summary_figure(field=None, min_size=None):
772
- """Heatmap of mean silhouette per (model, threshold).
 
 
 
 
 
 
 
773
  field=None/'__global__' → average over all fields.
774
- min_size=None/'__all__' → average over all min community sizes.
 
775
  """
776
  if METRICS_DF.empty:
777
  return go.Figure()
 
 
 
 
778
 
779
  def _short(m):
780
  return m.split("__")[-1] if "__" in m else m
781
 
782
- src = METRICS_DF.dropna(subset=["silhouette_cosine"])
783
  if field and field != "__global__":
784
  src = src[src["field"] == field]
785
  if src.empty:
@@ -794,7 +806,7 @@ def _build_grid_summary_figure(field=None, min_size=None):
794
 
795
  agg = (
796
  src
797
- .groupby(["model", "threshold"])["silhouette_cosine"]
798
  .mean()
799
  .reset_index()
800
  )
@@ -802,50 +814,54 @@ def _build_grid_summary_figure(field=None, min_size=None):
802
 
803
  pivot = agg.pivot_table(
804
  index="short_model", columns="threshold",
805
- values="silhouette_cosine", aggfunc="mean",
806
  )
807
- # Sort rows: worst best (best appears at top visually in Plotly heatmap)
808
- row_order = pivot.max(axis=1).sort_values(ascending=True).index.tolist()
 
809
  pivot = pivot.loc[row_order]
810
 
811
- # Embed star in the best cell's text (avoids categorical axis coordinate issues)
812
- best_idx = agg["silhouette_cosine"].idxmax()
813
  best = agg.loc[best_idx]
814
  best_short = _short(best["model"])
815
  best_thresh = best["threshold"]
816
 
817
  rows_list = list(pivot.index)
818
- cols_list = list(pivot.columns)
 
819
  text = []
820
  for ri, row_name in enumerate(rows_list):
821
  row_text = []
822
  for ci, col_val in enumerate(cols_list):
823
  v = pivot.iloc[ri, ci]
824
  if pd.notna(v):
825
- cell = f"★ {v:.3f}" if (row_name == best_short and col_val == best_thresh) else f"{v:.3f}"
826
  else:
827
  cell = ""
828
  row_text.append(cell)
829
  text.append(row_text)
830
 
 
 
 
 
831
  fig = go.Figure(go.Heatmap(
832
  z=pivot.values,
833
  x=[str(c) for c in pivot.columns],
834
  y=list(pivot.index),
835
- colorscale="RdYlGn",
836
  text=text,
837
  texttemplate="%{text}",
838
  textfont=dict(size=11),
839
- colorbar=dict(title=dict(text="Mean<br>Silhouette", font=dict(size=11)), thickness=14),
840
- hovertemplate="Model: %{y}<br>Threshold: %{x}<br>Mean silhouette: %{z:.3f}<extra></extra>",
841
  ))
842
 
843
- field_label = "all fields" if (not field or field == "__global__") else field.replace("_", " ").title()
844
- min_label = "all min-sizes" if (not min_size or min_size == "__all__") else f"min={min_size}"
845
- n_models = len(pivot.index)
846
  fig.update_layout(
847
  title=dict(
848
- text=f"Grid Search Overview — Mean Silhouette · {field_label} · {min_label} ★ = best",
849
  font=dict(size=13),
850
  ),
851
  xaxis=dict(title="Threshold", type="category", tickfont=dict(size=11)),
@@ -880,6 +896,17 @@ def grid_metrics_tab():
880
  dbc.Card([
881
  dbc.CardBody([
882
  dbc.Row([
 
 
 
 
 
 
 
 
 
 
 
883
  dbc.Col([
884
  html.Label("Field", className="fw-semibold small mb-1"),
885
  dcc.Dropdown(
@@ -2646,11 +2673,12 @@ def download_cluster_table(n_clicks, rows):
2646
 
2647
  @app.callback(
2648
  Output("metrics-summary-graph", "figure"),
 
2649
  Input("dd-summary-field", "value"),
2650
  Input("dd-summary-minsize", "value"),
2651
  )
2652
- def update_summary_graph(field, min_size):
2653
- return _build_grid_summary_figure(field, min_size)
2654
 
2655
 
2656
  @app.callback(
 
768
  }
769
 
770
 
771
+ _OVERVIEW_METRICS = {
772
+ "silhouette_cosine": ("Silhouette Score", True, "RdYlGn", "Mean<br>Silhouette"),
773
+ "davies_bouldin": ("Davies-Bouldin Index", False, "RdYlGn_r", "Mean<br>Davies-Bouldin"),
774
+ "n_clusters": ("N Clusters", True, "Blues", "Mean<br>N Clusters"),
775
+ }
776
+
777
+
778
+ def _build_grid_summary_figure(field=None, min_size=None, metric="silhouette_cosine"):
779
+ """Heatmap of a clustering metric per (model, threshold).
780
  field=None/'__global__' → average over all fields.
781
+ min_size=None/'__all__' → average over all min community sizes.
782
+ metric: one of silhouette_cosine | davies_bouldin | n_clusters.
783
  """
784
  if METRICS_DF.empty:
785
  return go.Figure()
786
+ if metric not in _OVERVIEW_METRICS:
787
+ metric = "silhouette_cosine"
788
+
789
+ metric_label, higher_better, colorscale, cb_title = _OVERVIEW_METRICS[metric]
790
 
791
  def _short(m):
792
  return m.split("__")[-1] if "__" in m else m
793
 
794
+ src = METRICS_DF.dropna(subset=[metric])
795
  if field and field != "__global__":
796
  src = src[src["field"] == field]
797
  if src.empty:
 
806
 
807
  agg = (
808
  src
809
+ .groupby(["model", "threshold"])[metric]
810
  .mean()
811
  .reset_index()
812
  )
 
814
 
815
  pivot = agg.pivot_table(
816
  index="short_model", columns="threshold",
817
+ values=metric, aggfunc="mean",
818
  )
819
+ # Sort rows so the best model appears at the top
820
+ best_per_row = pivot.max(axis=1) if higher_better else pivot.min(axis=1)
821
+ row_order = best_per_row.sort_values(ascending=higher_better).index.tolist()
822
  pivot = pivot.loc[row_order]
823
 
824
+ # Identify best cell and embed star in cell text
825
+ best_idx = agg[metric].idxmax() if higher_better else agg[metric].idxmin()
826
  best = agg.loc[best_idx]
827
  best_short = _short(best["model"])
828
  best_thresh = best["threshold"]
829
 
830
  rows_list = list(pivot.index)
831
+ cols_list = list(pivot.columns)
832
+ fmt = ".0f" if metric == "n_clusters" else ".3f"
833
  text = []
834
  for ri, row_name in enumerate(rows_list):
835
  row_text = []
836
  for ci, col_val in enumerate(cols_list):
837
  v = pivot.iloc[ri, ci]
838
  if pd.notna(v):
839
+ cell = f"★ {v:{fmt}}" if (row_name == best_short and col_val == best_thresh) else f"{v:{fmt}}"
840
  else:
841
  cell = ""
842
  row_text.append(cell)
843
  text.append(row_text)
844
 
845
+ field_label = "all fields" if (not field or field == "__global__") else field.replace("_", " ").title()
846
+ min_label = "all min-sizes" if (not min_size or min_size == "__all__") else f"min={min_size}"
847
+ n_models = len(pivot.index)
848
+
849
  fig = go.Figure(go.Heatmap(
850
  z=pivot.values,
851
  x=[str(c) for c in pivot.columns],
852
  y=list(pivot.index),
853
+ colorscale=colorscale,
854
  text=text,
855
  texttemplate="%{text}",
856
  textfont=dict(size=11),
857
+ colorbar=dict(title=dict(text=cb_title, font=dict(size=11)), thickness=14),
858
+ hovertemplate=f"Model: %{{y}}<br>Threshold: %{{x}}<br>{metric_label}: %{{z:{fmt}}}<extra></extra>",
859
  ))
860
 
861
+ better_str = "higher = better" if higher_better else "lower = better"
 
 
862
  fig.update_layout(
863
  title=dict(
864
+ text=f"Grid Search Overview — {metric_label} ({better_str}) · {field_label} · {min_label} ★ = best",
865
  font=dict(size=13),
866
  ),
867
  xaxis=dict(title="Threshold", type="category", tickfont=dict(size=11)),
 
896
  dbc.Card([
897
  dbc.CardBody([
898
  dbc.Row([
899
+ dbc.Col([
900
+ html.Label("Metric", className="fw-semibold small mb-1"),
901
+ dcc.Dropdown(
902
+ id="dd-summary-metric",
903
+ options=[{"label": v[0], "value": k}
904
+ for k, v in _OVERVIEW_METRICS.items()],
905
+ value="silhouette_cosine",
906
+ clearable=False,
907
+ style={"fontSize": "13px"},
908
+ ),
909
+ ], width=3),
910
  dbc.Col([
911
  html.Label("Field", className="fw-semibold small mb-1"),
912
  dcc.Dropdown(
 
2673
 
2674
  @app.callback(
2675
  Output("metrics-summary-graph", "figure"),
2676
+ Input("dd-summary-metric", "value"),
2677
  Input("dd-summary-field", "value"),
2678
  Input("dd-summary-minsize", "value"),
2679
  )
2680
+ def update_summary_graph(metric, field, min_size):
2681
+ return _build_grid_summary_figure(field, min_size, metric)
2682
 
2683
 
2684
  @app.callback(