Fix consistency columns and latent colors
Browse files- app.py +145 -30
- assets/styles.css +9 -0
- data/latent_samples.csv +0 -0
app.py
CHANGED
|
@@ -263,6 +263,9 @@ def build_dataset_labels() -> dict[str, str]:
|
|
| 263 |
|
| 264 |
DATASET_LABELS = build_dataset_labels()
|
| 265 |
DATASETS = ordered_unique(prediction.get("dataset", pd.Series(dtype=str)))
|
|
|
|
|
|
|
|
|
|
| 266 |
MODEL_SET = set(prediction.get("model", pd.Series(dtype=str)).dropna().astype(str))
|
| 267 |
MODELS = [model for model in PAPER_MODEL_ORDER if model in MODEL_SET]
|
| 268 |
MODELS += sorted(model for model in MODEL_SET if model not in set(MODELS))
|
|
@@ -346,15 +349,54 @@ def condition_label(dataset: str, condition: object) -> str:
|
|
| 346 |
return text
|
| 347 |
|
| 348 |
|
| 349 |
-
def condition_axis_label(dataset: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 350 |
return {
|
| 351 |
"monkey": "Reach direction",
|
| 352 |
"allen_neuropixels": "Orientation",
|
| 353 |
"speech": "Cue",
|
| 354 |
-
"ratinabox": "Position bin",
|
| 355 |
}.get(dataset, "Condition")
|
| 356 |
|
| 357 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
def session_display_label(dataset: str, session: object) -> str:
|
| 359 |
text = "" if pd.isna(session) else str(session)
|
| 360 |
if dataset == "monkey":
|
|
@@ -409,6 +451,14 @@ def sort_table(df: pd.DataFrame, sort_by: list[dict] | None, default: list[tuple
|
|
| 409 |
)
|
| 410 |
|
| 411 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
def records(df: pd.DataFrame) -> list[dict]:
|
| 413 |
clean = df.astype(object).where(pd.notna(df), None)
|
| 414 |
return clean.to_dict("records")
|
|
@@ -810,7 +860,7 @@ def consistency_heatmap(models: list[str] | None) -> go.Figure:
|
|
| 810 |
return fig_layout(fig, height=max(360, 25 * len(pivot.index) + 135))
|
| 811 |
|
| 812 |
|
| 813 |
-
def latent_space_figure(dataset: str, model: str | None) -> go.Figure:
|
| 814 |
if not model:
|
| 815 |
return empty_figure("No latent-space view is available for this selection.")
|
| 816 |
if latent_samples.empty:
|
|
@@ -825,8 +875,10 @@ def latent_space_figure(dataset: str, model: str | None) -> go.Figure:
|
|
| 825 |
|
| 826 |
for col in ["x", "y", "z"]:
|
| 827 |
plot_df[col] = pd.to_numeric(plot_df[col], errors="coerce")
|
| 828 |
-
|
| 829 |
-
|
|
|
|
|
|
|
| 830 |
plot_df["session_display"] = plot_df["session_label"].map(lambda value: session_display_label(dataset, value))
|
| 831 |
plot_df = plot_df.dropna(subset=["x", "y", "z"])
|
| 832 |
if plot_df.empty:
|
|
@@ -840,9 +892,14 @@ def latent_space_figure(dataset: str, model: str | None) -> go.Figure:
|
|
| 840 |
if col in trajectory_df:
|
| 841 |
trajectory_df[col] = pd.to_numeric(trajectory_df[col], errors="coerce")
|
| 842 |
if not trajectory_df.empty:
|
| 843 |
-
|
|
|
|
|
|
|
|
|
|
| 844 |
trajectory_df["session_display"] = trajectory_df["session_label"].map(lambda value: session_display_label(dataset, value))
|
| 845 |
trajectory_df = trajectory_df.dropna(subset=["x", "y", "z"])
|
|
|
|
|
|
|
| 846 |
|
| 847 |
sessions = ordered_unique(plot_df["session_label"])
|
| 848 |
session_titles = [session_display_label(dataset, session) for session in sessions]
|
|
@@ -858,8 +915,8 @@ def latent_space_figure(dataset: str, model: str | None) -> go.Figure:
|
|
| 858 |
vertical_spacing=0.12,
|
| 859 |
)
|
| 860 |
|
| 861 |
-
condition_values = sorted(plot_df["
|
| 862 |
-
use_categorical = len(condition_values) <= 12
|
| 863 |
if dataset == "monkey":
|
| 864 |
condition_colors = {
|
| 865 |
condition: DIRECTION_PALETTE[int(float(condition)) % len(DIRECTION_PALETTE)]
|
|
@@ -875,7 +932,7 @@ def latent_space_figure(dataset: str, model: str | None) -> go.Figure:
|
|
| 875 |
condition: CATEGORICAL_PALETTE[idx % len(CATEGORICAL_PALETTE)]
|
| 876 |
for idx, condition in enumerate(condition_values)
|
| 877 |
}
|
| 878 |
-
condition_name = condition_axis_label(dataset)
|
| 879 |
|
| 880 |
for session_idx, session in enumerate(sessions):
|
| 881 |
session_df = plot_df[plot_df["session_label"].astype(str) == str(session)]
|
|
@@ -887,13 +944,13 @@ def latent_space_figure(dataset: str, model: str | None) -> go.Figure:
|
|
| 887 |
|
| 888 |
if use_categorical:
|
| 889 |
for condition in condition_values:
|
| 890 |
-
cond_df = session_df[session_df["
|
| 891 |
if cond_df.empty:
|
| 892 |
continue
|
| 893 |
-
trace_name =
|
| 894 |
session_traj = trajectory_df[
|
| 895 |
(trajectory_df["session_label"].astype(str) == str(session))
|
| 896 |
-
& (trajectory_df["
|
| 897 |
].sort_values("time_index")
|
| 898 |
fig.add_trace(
|
| 899 |
go.Scatter3d(
|
|
@@ -912,7 +969,7 @@ def latent_space_figure(dataset: str, model: str | None) -> go.Figure:
|
|
| 912 |
customdata=np.stack(
|
| 913 |
[
|
| 914 |
np.repeat(display_session, len(cond_df)),
|
| 915 |
-
cond_df["
|
| 916 |
cond_df["trial_index"].astype(str),
|
| 917 |
cond_df["time_index"].astype(str),
|
| 918 |
],
|
|
@@ -960,15 +1017,17 @@ def latent_space_figure(dataset: str, model: str | None) -> go.Figure:
|
|
| 960 |
marker=dict(
|
| 961 |
size=2.8,
|
| 962 |
opacity=0.72,
|
| 963 |
-
color=session_df["
|
| 964 |
colorscale=RATINABOX_SCALE,
|
|
|
|
|
|
|
| 965 |
showscale=session_idx == 0,
|
| 966 |
colorbar=dict(title=condition_name, thickness=12),
|
| 967 |
),
|
| 968 |
customdata=np.stack(
|
| 969 |
[
|
| 970 |
np.repeat(display_session, len(session_df)),
|
| 971 |
-
session_df["
|
| 972 |
session_df["trial_index"].astype(str),
|
| 973 |
session_df["time_index"].astype(str),
|
| 974 |
],
|
|
@@ -1332,18 +1391,34 @@ app.layout = html.Div(
|
|
| 1332 |
],
|
| 1333 |
className="consistency-table-wrap",
|
| 1334 |
),
|
| 1335 |
-
|
| 1336 |
-
|
| 1337 |
-
|
| 1338 |
-
|
| 1339 |
-
|
| 1340 |
-
|
| 1341 |
-
|
| 1342 |
-
|
| 1343 |
-
|
| 1344 |
-
|
| 1345 |
-
|
| 1346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1347 |
),
|
| 1348 |
],
|
| 1349 |
className="latent-grid",
|
|
@@ -1449,11 +1524,13 @@ def update_leaderboard(dataset: str, sort_by: list[dict] | None):
|
|
| 1449 |
"method",
|
| 1450 |
"task_score",
|
| 1451 |
"robustness_auc",
|
| 1452 |
-
"alignment_score",
|
| 1453 |
"training_time_sec",
|
| 1454 |
"peak_ram_gb",
|
| 1455 |
"peak_vram_gb",
|
| 1456 |
]
|
|
|
|
|
|
|
|
|
|
| 1457 |
sorted_df = sort_table(df, sort_by, [("task_score", False), ("model_order", True)])
|
| 1458 |
table_df = sorted_df[[c for c in visible_cols + ["id", "model"] if c in sorted_df.columns]]
|
| 1459 |
metric = df["metric"].dropna().iloc[0] if df["metric"].notna().any() else "score"
|
|
@@ -1467,6 +1544,26 @@ def update_leaderboard(dataset: str, sort_by: list[dict] | None):
|
|
| 1467 |
)
|
| 1468 |
|
| 1469 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1470 |
@app.callback(
|
| 1471 |
Output("consistency-table", "columns"),
|
| 1472 |
Output("consistency-table", "data"),
|
|
@@ -1477,18 +1574,36 @@ def update_leaderboard(dataset: str, sort_by: list[dict] | None):
|
|
| 1477 |
Input("dataset-filter", "value"),
|
| 1478 |
Input("consistency-table", "active_cell"),
|
| 1479 |
Input("consistency-table", "sort_by"),
|
|
|
|
| 1480 |
)
|
| 1481 |
-
def update_consistency(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1482 |
df = consistency_frame(dataset, None)
|
| 1483 |
model = selected_consistency_model(df, active_cell)
|
| 1484 |
visible_cols = ["method", "alignment_score", "n_sessions", "latent_dim", "n_pairwise"]
|
|
|
|
| 1485 |
sorted_df = sort_table(df, sort_by, [("alignment_score", False), ("model_order", True)])
|
| 1486 |
table_df = sorted_df[[c for c in visible_cols + ["id", "model"] if c in sorted_df.columns]]
|
| 1487 |
return (
|
| 1488 |
column_defs([c for c in visible_cols if c in table_df.columns]),
|
| 1489 |
records(table_df),
|
| 1490 |
sort_status(sort_by, ("alignment_score", False)),
|
| 1491 |
-
latent_space_figure(dataset, model),
|
| 1492 |
consistency_bar_figure(dataset, df),
|
| 1493 |
consistency_heatmap(None),
|
| 1494 |
)
|
|
|
|
| 263 |
|
| 264 |
DATASET_LABELS = build_dataset_labels()
|
| 265 |
DATASETS = ordered_unique(prediction.get("dataset", pd.Series(dtype=str)))
|
| 266 |
+
CONSISTENCY_DATASETS = set(
|
| 267 |
+
active_rows(consistency).get("dataset", pd.Series(dtype=str)).dropna().astype(str)
|
| 268 |
+
)
|
| 269 |
MODEL_SET = set(prediction.get("model", pd.Series(dtype=str)).dropna().astype(str))
|
| 270 |
MODELS = [model for model in PAPER_MODEL_ORDER if model in MODEL_SET]
|
| 271 |
MODELS += sorted(model for model in MODEL_SET if model not in set(MODELS))
|
|
|
|
| 349 |
return text
|
| 350 |
|
| 351 |
|
| 352 |
+
def condition_axis_label(dataset: str, color_mode: str = "condition") -> str:
|
| 353 |
+
if dataset == "ratinabox":
|
| 354 |
+
if color_mode == "x":
|
| 355 |
+
return "X position bin"
|
| 356 |
+
if color_mode == "y":
|
| 357 |
+
return "Y position bin"
|
| 358 |
+
return "Position bin"
|
| 359 |
return {
|
| 360 |
"monkey": "Reach direction",
|
| 361 |
"allen_neuropixels": "Orientation",
|
| 362 |
"speech": "Cue",
|
|
|
|
| 363 |
}.get(dataset, "Condition")
|
| 364 |
|
| 365 |
|
| 366 |
+
def latent_color_label(dataset: str, color_mode: str, value: object) -> str:
|
| 367 |
+
if pd.isna(value):
|
| 368 |
+
return "Unknown"
|
| 369 |
+
if dataset == "ratinabox":
|
| 370 |
+
try:
|
| 371 |
+
idx = int(float(value))
|
| 372 |
+
except ValueError:
|
| 373 |
+
return str(value)
|
| 374 |
+
if color_mode == "x":
|
| 375 |
+
return f"x{idx}"
|
| 376 |
+
if color_mode == "y":
|
| 377 |
+
return f"y{idx}"
|
| 378 |
+
return condition_label(dataset, value)
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
def add_latent_color_columns(df: pd.DataFrame, dataset: str, color_mode: str) -> pd.DataFrame:
|
| 382 |
+
out = df.copy()
|
| 383 |
+
condition_num = pd.to_numeric(out["condition"], errors="coerce")
|
| 384 |
+
if condition_num.isna().any() or (condition_num < 0).any():
|
| 385 |
+
raise ValueError("Latent samples contain missing condition labels.")
|
| 386 |
+
|
| 387 |
+
if dataset == "ratinabox" and color_mode == "x":
|
| 388 |
+
values = (condition_num.astype(int) % 10).astype(str)
|
| 389 |
+
elif dataset == "ratinabox" and color_mode == "y":
|
| 390 |
+
values = (condition_num.astype(int) // 10).astype(str)
|
| 391 |
+
else:
|
| 392 |
+
values = condition_num.astype(int).astype(str)
|
| 393 |
+
|
| 394 |
+
out["color_value"] = values
|
| 395 |
+
out["color_num"] = pd.to_numeric(values, errors="coerce")
|
| 396 |
+
out["color_label"] = out["color_value"].map(lambda value: latent_color_label(dataset, color_mode, value))
|
| 397 |
+
return out
|
| 398 |
+
|
| 399 |
+
|
| 400 |
def session_display_label(dataset: str, session: object) -> str:
|
| 401 |
text = "" if pd.isna(session) else str(session)
|
| 402 |
if dataset == "monkey":
|
|
|
|
| 451 |
)
|
| 452 |
|
| 453 |
|
| 454 |
+
def supported_sort(sort_by: list[dict] | None, columns: Iterable[str]) -> list[dict] | None:
|
| 455 |
+
allowed = set(columns)
|
| 456 |
+
if not sort_by:
|
| 457 |
+
return None
|
| 458 |
+
filtered = [item for item in sort_by if item.get("column_id") in allowed]
|
| 459 |
+
return filtered or None
|
| 460 |
+
|
| 461 |
+
|
| 462 |
def records(df: pd.DataFrame) -> list[dict]:
|
| 463 |
clean = df.astype(object).where(pd.notna(df), None)
|
| 464 |
return clean.to_dict("records")
|
|
|
|
| 860 |
return fig_layout(fig, height=max(360, 25 * len(pivot.index) + 135))
|
| 861 |
|
| 862 |
|
| 863 |
+
def latent_space_figure(dataset: str, model: str | None, color_mode: str = "condition") -> go.Figure:
|
| 864 |
if not model:
|
| 865 |
return empty_figure("No latent-space view is available for this selection.")
|
| 866 |
if latent_samples.empty:
|
|
|
|
| 875 |
|
| 876 |
for col in ["x", "y", "z"]:
|
| 877 |
plot_df[col] = pd.to_numeric(plot_df[col], errors="coerce")
|
| 878 |
+
try:
|
| 879 |
+
plot_df = add_latent_color_columns(plot_df, dataset, color_mode)
|
| 880 |
+
except ValueError as exc:
|
| 881 |
+
return empty_figure(str(exc))
|
| 882 |
plot_df["session_display"] = plot_df["session_label"].map(lambda value: session_display_label(dataset, value))
|
| 883 |
plot_df = plot_df.dropna(subset=["x", "y", "z"])
|
| 884 |
if plot_df.empty:
|
|
|
|
| 892 |
if col in trajectory_df:
|
| 893 |
trajectory_df[col] = pd.to_numeric(trajectory_df[col], errors="coerce")
|
| 894 |
if not trajectory_df.empty:
|
| 895 |
+
try:
|
| 896 |
+
trajectory_df = add_latent_color_columns(trajectory_df, dataset, color_mode)
|
| 897 |
+
except ValueError:
|
| 898 |
+
trajectory_df = trajectory_df.iloc[0:0].copy()
|
| 899 |
trajectory_df["session_display"] = trajectory_df["session_label"].map(lambda value: session_display_label(dataset, value))
|
| 900 |
trajectory_df = trajectory_df.dropna(subset=["x", "y", "z"])
|
| 901 |
+
if "color_value" not in trajectory_df.columns:
|
| 902 |
+
trajectory_df["color_value"] = pd.Series(dtype=str)
|
| 903 |
|
| 904 |
sessions = ordered_unique(plot_df["session_label"])
|
| 905 |
session_titles = [session_display_label(dataset, session) for session in sessions]
|
|
|
|
| 915 |
vertical_spacing=0.12,
|
| 916 |
)
|
| 917 |
|
| 918 |
+
condition_values = sorted(plot_df["color_value"].astype(str).unique(), key=condition_sort_key)
|
| 919 |
+
use_categorical = len(condition_values) <= 12 and dataset != "ratinabox"
|
| 920 |
if dataset == "monkey":
|
| 921 |
condition_colors = {
|
| 922 |
condition: DIRECTION_PALETTE[int(float(condition)) % len(DIRECTION_PALETTE)]
|
|
|
|
| 932 |
condition: CATEGORICAL_PALETTE[idx % len(CATEGORICAL_PALETTE)]
|
| 933 |
for idx, condition in enumerate(condition_values)
|
| 934 |
}
|
| 935 |
+
condition_name = condition_axis_label(dataset, color_mode)
|
| 936 |
|
| 937 |
for session_idx, session in enumerate(sessions):
|
| 938 |
session_df = plot_df[plot_df["session_label"].astype(str) == str(session)]
|
|
|
|
| 944 |
|
| 945 |
if use_categorical:
|
| 946 |
for condition in condition_values:
|
| 947 |
+
cond_df = session_df[session_df["color_value"].astype(str) == condition]
|
| 948 |
if cond_df.empty:
|
| 949 |
continue
|
| 950 |
+
trace_name = latent_color_label(dataset, color_mode, condition)
|
| 951 |
session_traj = trajectory_df[
|
| 952 |
(trajectory_df["session_label"].astype(str) == str(session))
|
| 953 |
+
& (trajectory_df["color_value"].astype(str) == condition)
|
| 954 |
].sort_values("time_index")
|
| 955 |
fig.add_trace(
|
| 956 |
go.Scatter3d(
|
|
|
|
| 969 |
customdata=np.stack(
|
| 970 |
[
|
| 971 |
np.repeat(display_session, len(cond_df)),
|
| 972 |
+
cond_df["color_label"].astype(str),
|
| 973 |
cond_df["trial_index"].astype(str),
|
| 974 |
cond_df["time_index"].astype(str),
|
| 975 |
],
|
|
|
|
| 1017 |
marker=dict(
|
| 1018 |
size=2.8,
|
| 1019 |
opacity=0.72,
|
| 1020 |
+
color=session_df["color_num"],
|
| 1021 |
colorscale=RATINABOX_SCALE,
|
| 1022 |
+
cmin=0,
|
| 1023 |
+
cmax=9 if color_mode in {"x", "y"} else 99,
|
| 1024 |
showscale=session_idx == 0,
|
| 1025 |
colorbar=dict(title=condition_name, thickness=12),
|
| 1026 |
),
|
| 1027 |
customdata=np.stack(
|
| 1028 |
[
|
| 1029 |
np.repeat(display_session, len(session_df)),
|
| 1030 |
+
session_df["color_label"].astype(str),
|
| 1031 |
session_df["trial_index"].astype(str),
|
| 1032 |
session_df["time_index"].astype(str),
|
| 1033 |
],
|
|
|
|
| 1391 |
],
|
| 1392 |
className="consistency-table-wrap",
|
| 1393 |
),
|
| 1394 |
+
html.Div(
|
| 1395 |
+
[
|
| 1396 |
+
html.Div(
|
| 1397 |
+
[
|
| 1398 |
+
html.Label("Color by"),
|
| 1399 |
+
dcc.Dropdown(
|
| 1400 |
+
id="latent-color-mode",
|
| 1401 |
+
clearable=False,
|
| 1402 |
+
),
|
| 1403 |
+
],
|
| 1404 |
+
id="latent-color-control",
|
| 1405 |
+
className="control latent-color-control",
|
| 1406 |
+
),
|
| 1407 |
+
dcc.Graph(
|
| 1408 |
+
id="latent-space",
|
| 1409 |
+
config={
|
| 1410 |
+
"displayModeBar": "hover",
|
| 1411 |
+
"toImageButtonOptions": {
|
| 1412 |
+
"format": "png",
|
| 1413 |
+
"filename": "benchdash_latent_space",
|
| 1414 |
+
"height": 900,
|
| 1415 |
+
"width": 1200,
|
| 1416 |
+
"scale": 2,
|
| 1417 |
+
},
|
| 1418 |
+
},
|
| 1419 |
+
),
|
| 1420 |
+
],
|
| 1421 |
+
className="latent-panel",
|
| 1422 |
),
|
| 1423 |
],
|
| 1424 |
className="latent-grid",
|
|
|
|
| 1524 |
"method",
|
| 1525 |
"task_score",
|
| 1526 |
"robustness_auc",
|
|
|
|
| 1527 |
"training_time_sec",
|
| 1528 |
"peak_ram_gb",
|
| 1529 |
"peak_vram_gb",
|
| 1530 |
]
|
| 1531 |
+
if str(dataset) in CONSISTENCY_DATASETS:
|
| 1532 |
+
visible_cols.insert(3, "alignment_score")
|
| 1533 |
+
sort_by = supported_sort(sort_by, visible_cols)
|
| 1534 |
sorted_df = sort_table(df, sort_by, [("task_score", False), ("model_order", True)])
|
| 1535 |
table_df = sorted_df[[c for c in visible_cols + ["id", "model"] if c in sorted_df.columns]]
|
| 1536 |
metric = df["metric"].dropna().iloc[0] if df["metric"].notna().any() else "score"
|
|
|
|
| 1544 |
)
|
| 1545 |
|
| 1546 |
|
| 1547 |
+
@app.callback(
|
| 1548 |
+
Output("latent-color-mode", "options"),
|
| 1549 |
+
Output("latent-color-mode", "value"),
|
| 1550 |
+
Output("latent-color-control", "style"),
|
| 1551 |
+
Input("dataset-filter", "value"),
|
| 1552 |
+
)
|
| 1553 |
+
def update_latent_color_control(dataset: str):
|
| 1554 |
+
if str(dataset) == "ratinabox":
|
| 1555 |
+
return (
|
| 1556 |
+
[
|
| 1557 |
+
{"label": "Position bin", "value": "condition"},
|
| 1558 |
+
{"label": "X position", "value": "x"},
|
| 1559 |
+
{"label": "Y position", "value": "y"},
|
| 1560 |
+
],
|
| 1561 |
+
"condition",
|
| 1562 |
+
{},
|
| 1563 |
+
)
|
| 1564 |
+
return ([{"label": condition_axis_label(str(dataset)), "value": "condition"}], "condition", {"display": "none"})
|
| 1565 |
+
|
| 1566 |
+
|
| 1567 |
@app.callback(
|
| 1568 |
Output("consistency-table", "columns"),
|
| 1569 |
Output("consistency-table", "data"),
|
|
|
|
| 1574 |
Input("dataset-filter", "value"),
|
| 1575 |
Input("consistency-table", "active_cell"),
|
| 1576 |
Input("consistency-table", "sort_by"),
|
| 1577 |
+
Input("latent-color-mode", "value"),
|
| 1578 |
)
|
| 1579 |
+
def update_consistency(
|
| 1580 |
+
dataset: str,
|
| 1581 |
+
active_cell: dict | None,
|
| 1582 |
+
sort_by: list[dict] | None,
|
| 1583 |
+
color_mode: str | None,
|
| 1584 |
+
):
|
| 1585 |
+
if str(dataset) not in CONSISTENCY_DATASETS:
|
| 1586 |
+
label = DATASET_LABELS.get(dataset, dataset)
|
| 1587 |
+
message = f"Cross-session alignment is not defined for {label}."
|
| 1588 |
+
return (
|
| 1589 |
+
[],
|
| 1590 |
+
[],
|
| 1591 |
+
message,
|
| 1592 |
+
empty_figure(message),
|
| 1593 |
+
empty_figure(message),
|
| 1594 |
+
consistency_heatmap(None),
|
| 1595 |
+
)
|
| 1596 |
df = consistency_frame(dataset, None)
|
| 1597 |
model = selected_consistency_model(df, active_cell)
|
| 1598 |
visible_cols = ["method", "alignment_score", "n_sessions", "latent_dim", "n_pairwise"]
|
| 1599 |
+
sort_by = supported_sort(sort_by, visible_cols)
|
| 1600 |
sorted_df = sort_table(df, sort_by, [("alignment_score", False), ("model_order", True)])
|
| 1601 |
table_df = sorted_df[[c for c in visible_cols + ["id", "model"] if c in sorted_df.columns]]
|
| 1602 |
return (
|
| 1603 |
column_defs([c for c in visible_cols if c in table_df.columns]),
|
| 1604 |
records(table_df),
|
| 1605 |
sort_status(sort_by, ("alignment_score", False)),
|
| 1606 |
+
latent_space_figure(dataset, model, color_mode or "condition"),
|
| 1607 |
consistency_bar_figure(dataset, df),
|
| 1608 |
consistency_heatmap(None),
|
| 1609 |
)
|
assets/styles.css
CHANGED
|
@@ -228,6 +228,15 @@ h2 {
|
|
| 228 |
align-items: start;
|
| 229 |
}
|
| 230 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
.chart-grid {
|
| 232 |
display: grid;
|
| 233 |
gap: 16px;
|
|
|
|
| 228 |
align-items: start;
|
| 229 |
}
|
| 230 |
|
| 231 |
+
.latent-panel {
|
| 232 |
+
min-width: 0;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
.latent-color-control {
|
| 236 |
+
width: min(260px, 100%);
|
| 237 |
+
margin-bottom: 8px;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
.chart-grid {
|
| 241 |
display: grid;
|
| 242 |
gap: 16px;
|
data/latent_samples.csv
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|