| from __future__ import annotations |
|
|
| import importlib |
| from pathlib import Path |
| import warnings |
| import pandas as pd |
| import streamlit as st |
|
|
| try: |
| from common.method_registry import ( |
| dump_method_registry_snapshot, |
| canonicalize_method_name, |
| get_method_meta, |
| normalize_method_name, |
| ) |
| except ModuleNotFoundError: |
| from src.common.method_registry import ( |
| dump_method_registry_snapshot, |
| canonicalize_method_name, |
| get_method_meta, |
| normalize_method_name, |
| ) |
|
|
|
|
| DATASET_FILES = { |
| "L63": [ |
| "l63_ensemble_results.nc", |
| "UKI_results/uki_l63_ensemble_results.nc", |
| "bayesian/l63_abc.nc", |
| "bayesian/l63_hm.nc" |
| ], |
| "L96": [ |
| "l96_ensemble_results.nc", |
| "UKI_results/uki_l96_ensemble_results.nc", |
| "bayesian/l96_abc.nc" |
| ], |
| "L96_NN_FORCING": [ |
| "l96_nn_forcing_ensemble_results.nc", |
| "UKI_results/uki_l96_nn_forcing_ensemble_results.nc" |
| ], |
| "L96_SPATIAL_FORCING": [ |
| "l96_spatial_forcing_ensemble_results.nc", |
| "UKI_results/uki_l96_spatial_forcing_ensemble_results.nc", |
| "bayesian/l96_varying_abc.nc" |
| ], |
| } |
|
|
|
|
|
|
| EXPECTED_DIMS = ("algorithm_type", "rmse_target", "ensemble_size", "random_seed") |
|
|
|
|
| @st.cache_data(show_spinner=False) |
| def load_metric_store() -> pd.DataFrame: |
| xr = importlib.import_module("xarray") |
|
|
| project_root = Path(__file__).resolve().parent.parent |
| data_dir = project_root / "data" |
|
|
| records: list[pd.DataFrame] = [] |
|
|
| for benchmark_name, filenames in DATASET_FILES.items(): |
| for filename in filenames: |
| file_path = data_dir / filename |
| if not file_path.exists(): |
| warnings.warn(f"Dataset {file_path} does not exist") |
| continue |
|
|
| with xr.open_dataset(file_path) as dataset: |
| if "metric" not in dataset: |
| warnings.warn(f"Dataset {file_path} does not contain 'metric' variable") |
| continue |
|
|
| metric = dataset["metric"] |
| |
| |
| failures_count = (metric == -1).sum(dim="random_seed") |
|
|
| failure_rate = (failures_count / dataset.sizes["random_seed"]) * 100 |
| |
| |
| metric = metric.where(metric != -1) |
| |
| metric_mean = metric.mean(dim="random_seed", skipna=True) |
| df = metric_mean.to_dataframe(name="metric").reset_index() |
| |
| df_failures = failure_rate.to_dataframe(name="failure_rate").reset_index() |
| df["failure_rate"] = df_failures["failure_rate"] |
|
|
| df["benchmark"] = benchmark_name |
| |
| if "algorithm_type" not in df.columns: |
| if "abc" in file_path.name: |
| df["algorithm_type"] = "abc" |
| elif "hm" in file_path.name: |
| df["algorithm_type"] = "hm" |
| |
| if "ensemble_size" not in df.columns: |
| if "abc" in file_path.name: |
| df["ensemble_size"] = 1 |
| |
| missing_cols = [c for c in ["benchmark", "algorithm_type", "rmse_target", "ensemble_size", "metric", "failure_rate"] if c not in df.columns] |
| if missing_cols: |
| st.warning( |
| f"Skipping {file_path.name}: metric dataframe is missing expected columns {missing_cols}" |
| ) |
| continue |
|
|
| records.append(df[["benchmark", "algorithm_type", "rmse_target", "ensemble_size", "metric", "failure_rate"]]) |
|
|
| if not records: |
| return pd.DataFrame(columns=["benchmark", "algorithm_type", "rmse_target", "ensemble_size", "metric", "failure_rate"]) |
|
|
| merged = pd.concat(records, ignore_index=True) |
| merged["algorithm_alias"] = merged["algorithm_type"].map(normalize_method_name) |
| merged["algorithm_type"] = merged["algorithm_alias"].map(canonicalize_method_name) |
| merged["ensemble_size"] = pd.to_numeric(merged["ensemble_size"], errors="coerce") |
| merged["metric"] = pd.to_numeric(merged["metric"], errors="coerce") |
| merged["failure_rate"] = pd.to_numeric(merged["failure_rate"], errors="coerce").fillna(0.0) |
| merged = merged.dropna(subset=["ensemble_size", "metric"]) |
| merged["ensemble_size"] = merged["ensemble_size"].astype(int) |
| merged["forward_model_runs"] = merged["metric"] |
|
|
| merged["abbreviation"] = merged["algorithm_type"].map(lambda method: get_method_meta(method).get("abbreviation")) |
| merged["Method"] = merged["algorithm_type"].map(lambda method: get_method_meta(method).get("Method")) |
| merged["family"] = merged["algorithm_type"].map(lambda method: get_method_meta(method).get("family")) |
| merged["abbreviation"] = merged["abbreviation"].fillna(merged["algorithm_type"].str.upper()) |
| merged["Method"] = merged["Method"].fillna(merged["abbreviation"]) |
| merged["family"] = merged["family"].fillna("Kalman") |
|
|
| try: |
| dump_method_registry_snapshot(project_root=project_root, observed_methods=set(merged["algorithm_type"].unique())) |
| except OSError: |
| st.warning("Unable to write method registry snapshot to .cache/known_methods_snapshot.json") |
|
|
| return merged |
|
|
|
|
| def build_leaderboard(metric_store: pd.DataFrame) -> pd.DataFrame: |
| if metric_store.empty: |
| return pd.DataFrame( |
| columns=[ |
| "family", |
| "Method", |
| "abbreviation", |
| "benchmark", |
| "Forward Model Runs", |
| "Optimal Ensemble Size", |
| "Target Level", |
| ] |
| ) |
|
|
| best_idx = metric_store.groupby(["benchmark", "algorithm_type"])["metric"].idxmin() |
| best = metric_store.loc[best_idx].copy() |
| best = best.rename(columns={"metric": "Forward Model Runs", "rmse_target": "Target Level", "ensemble_size": "Optimal Ensemble Size"}) |
| return best[["family", "Method", "abbreviation", "benchmark", "Forward Model Runs", "Optimal Ensemble Size", "Target Level"]].sort_values( |
| ["benchmark", "Forward Model Runs"], ascending=[True, True] |
| ) |
|
|