ZivKassnerNK's picture
Deploy Streamlit dashboard via Docker Space
5d68221 verified
Raw
History Blame Contribute Delete
5.43 kB
from __future__ import annotations
from datetime import datetime, timezone
import re
from typing import Iterable
import numpy as np
import pandas as pd
_CANONICAL_METRIC_MAP = {
"precision": "Precision",
"recall": "Recall",
"f1": "F1",
"meaniou": "Mean_IoU",
"meantpiou": "Mean_TP_IoU",
}
def normalize_metrics_dataframe(dataframe: pd.DataFrame) -> pd.DataFrame:
if dataframe.empty:
return pd.DataFrame()
rename_map: dict[str, str] = {}
class_column: str | None = None
for column in dataframe.columns:
normalized = _normalize_key(column)
if normalized == "class":
class_column = str(column)
rename_map[str(column)] = "Class"
continue
if normalized.endswith("std"):
metric_key = normalized[: -len("std")]
metric_name = _CANONICAL_METRIC_MAP.get(metric_key)
if metric_name:
rename_map[str(column)] = f"{metric_name}_std"
continue
metric_name = _CANONICAL_METRIC_MAP.get(normalized)
if metric_name:
rename_map[str(column)] = metric_name
if class_column is None:
return pd.DataFrame()
cleaned = dataframe.rename(columns=rename_map).copy()
cleaned["Class"] = cleaned["Class"].astype(str).str.strip()
cleaned = cleaned[cleaned["Class"] != ""]
for column in cleaned.columns:
if column == "Class":
continue
cleaned[column] = cleaned[column].apply(_parse_float)
return cleaned.reset_index(drop=True)
def table_to_long_dataframe(
dataframe: pd.DataFrame,
repo_id: str,
revision: str,
committed_at: datetime | None,
metrics: Iterable[str],
) -> pd.DataFrame:
if dataframe.empty:
return pd.DataFrame()
timestamp = pd.to_datetime(committed_at or datetime.now(timezone.utc), utc=True)
frames: list[pd.DataFrame] = []
for metric in metrics:
if metric not in dataframe.columns:
continue
metric_frame = pd.DataFrame(
{
"repo_id": repo_id,
"revision": revision,
"timestamp": timestamp,
"class": dataframe["Class"].astype(str),
"metric": metric,
"value": dataframe[metric],
"std": dataframe.get(f"{metric}_std", np.nan),
}
)
frames.append(metric_frame)
if not frames:
return pd.DataFrame()
long_df = pd.concat(frames, ignore_index=True)
long_df["value"] = pd.to_numeric(long_df["value"], errors="coerce")
long_df["std"] = pd.to_numeric(long_df["std"], errors="coerce")
long_df["timestamp"] = pd.to_datetime(long_df["timestamp"], utc=True, errors="coerce")
long_df = long_df.dropna(subset=["timestamp"])
return long_df
def add_latest_flags(history_df: pd.DataFrame) -> pd.DataFrame:
if history_df.empty:
result = history_df.copy()
result["is_latest"] = False
return result
unique_runs = history_df[["repo_id", "revision", "timestamp"]].drop_duplicates()
latest_runs = (
unique_runs.sort_values(["repo_id", "timestamp", "revision"])
.groupby("repo_id", as_index=False)
.tail(1)
.assign(is_latest=True)
)
merged = history_df.merge(
latest_runs,
on=["repo_id", "revision", "timestamp"],
how="left",
)
merged["is_latest"] = merged["is_latest"].fillna(False)
return merged
def filter_history(
history_df: pd.DataFrame,
repos: Iterable[str],
classes: Iterable[str],
metrics: Iterable[str],
) -> pd.DataFrame:
if history_df.empty:
return history_df
filtered = history_df.copy()
repos = list(repos)
classes = list(classes)
metrics = list(metrics)
if repos:
filtered = filtered[filtered["repo_id"].isin(repos)]
if classes:
filtered = filtered[filtered["class"].isin(classes)]
if metrics:
filtered = filtered[filtered["metric"].isin(metrics)]
return filtered
def build_latest_snapshot_table(history_df: pd.DataFrame) -> pd.DataFrame:
if history_df.empty:
return pd.DataFrame()
latest_df = history_df[history_df["is_latest"]].copy()
if latest_df.empty:
return pd.DataFrame()
value_pivot = latest_df.pivot_table(
index=["repo_id", "class", "revision", "timestamp"],
columns="metric",
values="value",
aggfunc="first",
)
std_pivot = latest_df.pivot_table(
index=["repo_id", "class", "revision", "timestamp"],
columns="metric",
values="std",
aggfunc="first",
)
if not std_pivot.empty:
std_pivot.columns = [f"{column}_std" for column in std_pivot.columns]
snapshot = value_pivot.join(std_pivot, how="left")
snapshot = snapshot.reset_index().sort_values(["repo_id", "class"])
return snapshot
def _normalize_key(value: str) -> str:
return re.sub(r"[^a-z0-9]", "", str(value).strip().lower())
def _parse_float(value: object) -> float:
if value is None:
return np.nan
text = str(value).strip()
if text == "":
return np.nan
lowered = text.lower()
if lowered in {"nan", "na", "n/a", "none", "null", "-"}:
return np.nan
cleaned = text.replace(",", "")
try:
return float(cleaned)
except ValueError:
return np.nan