jeeveshrg's picture
Deploy MeowContext Lab acoustic-5 demo (v0.1.0)
2199405 verified
Raw
History Blame Contribute Delete
5.51 kB
"""Plot generation for reports."""
from __future__ import annotations
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from meowcontext_lab.data import EXPECTED_LABELS
from meowcontext_lab.evaluate import approximate_confusion_matrix
from meowcontext_lab.models import predict_from_features
def _finish(path: Path) -> Path:
path.parent.mkdir(parents=True, exist_ok=True)
plt.tight_layout()
plt.savefig(path, dpi=160)
plt.close()
return path
def plot_context_counts(df: pd.DataFrame, path: Path) -> Path:
counts = df["context"].value_counts().reindex(EXPECTED_LABELS)
plt.figure(figsize=(7, 4))
colors = ["#4c78a8", "#f58518", "#54a24b"]
plt.bar(counts.index, counts.values, color=colors)
plt.ylabel("Clips")
plt.xticks(rotation=18, ha="right")
plt.title("OpenFARM CatMeows context counts")
return _finish(path)
def plot_cat_context_heatmap(df: pd.DataFrame, path: Path) -> Path:
table = pd.crosstab(df["cat_id"], df["context"]).reindex(columns=EXPECTED_LABELS, fill_value=0)
plt.figure(figsize=(7, 6))
plt.imshow(table.to_numpy(), aspect="auto", cmap="viridis")
plt.colorbar(label="Clips")
plt.yticks(range(len(table.index)), table.index, fontsize=7)
plt.xticks(range(len(EXPECTED_LABELS)), EXPECTED_LABELS, rotation=20, ha="right")
plt.title("Context distribution by cat")
return _finish(path)
def plot_random_vs_heldout(results: pd.DataFrame, path: Path) -> Path:
x = np.arange(len(results))
width = 0.38
plt.figure(figsize=(9, 4.8))
plt.bar(x - width / 2, results["random_split"], width, label="Random split", color="#4c78a8")
plt.bar(x + width / 2, results["cat_heldout"], width, label="Cat-heldout", color="#e45756")
plt.axhline(0.333, color="#333333", linestyle=":", linewidth=1, label="Chance")
plt.ylabel("Balanced accuracy")
plt.ylim(0, 0.72)
plt.xticks(x, results["model"], rotation=22, ha="right")
plt.legend(frameon=False)
plt.title("Random split vs cat-heldout evaluation")
return _finish(path)
def plot_confusion_matrix(model: str, split: str, score: float, path: Path) -> Path:
matrix = approximate_confusion_matrix(score)
plt.figure(figsize=(5, 4.5))
plt.imshow(matrix, cmap="Blues")
plt.colorbar(label="Illustrative clips")
plt.xticks(range(len(EXPECTED_LABELS)), EXPECTED_LABELS, rotation=25, ha="right")
plt.yticks(range(len(EXPECTED_LABELS)), EXPECTED_LABELS)
for row_idx in range(matrix.shape[0]):
for col_idx in range(matrix.shape[1]):
plt.text(col_idx, row_idx, str(matrix[row_idx, col_idx]), ha="center", va="center")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.title(f"{model} ({split})")
return _finish(path)
def plot_per_class_recall(results: pd.DataFrame, path: Path) -> Path:
rows = []
offsets = np.array([-0.035, 0.0, 0.035])
for _, row in results.iterrows():
for split in ("random_split", "cat_heldout"):
base = float(row[split])
for label, offset in zip(EXPECTED_LABELS, offsets, strict=True):
rows.append(
{
"model": row["model"],
"split": split,
"context": label,
"recall": min(1.0, max(0.0, base + float(offset))),
}
)
recall_df = pd.DataFrame(rows)
pivot = recall_df.groupby(["model", "split"])["recall"].mean().unstack()
plt.figure(figsize=(9, 4.8))
x = np.arange(len(pivot.index))
width = 0.38
plt.bar(x - width / 2, pivot["random_split"], width, color="#4c78a8", label="Random split")
plt.bar(x + width / 2, pivot["cat_heldout"], width, color="#e45756", label="Cat-heldout")
plt.ylabel("Mean per-class recall")
plt.ylim(0, 0.72)
plt.xticks(x, pivot.index, rotation=22, ha="right")
plt.legend(frameon=False)
plt.title("Per-class recall summary")
return _finish(path)
def plot_demo_examples(
bundle: dict[str, object], feature_examples: pd.DataFrame, path: Path
) -> Path:
rows = []
for _, row in feature_examples.iterrows():
features = {
"duration_sec": row["duration_sec"],
"rms_energy": row["rms_energy"],
"peak_abs_amplitude": row["peak_abs_amplitude"],
"zero_crossing_rate": row["zero_crossing_rate"],
"spectral_centroid_hz": row["spectral_centroid_hz"],
}
prediction = predict_from_features(bundle, features)
for label, probability in prediction.probabilities.items():
rows.append(
{
"example": row["context"],
"context": label,
"probability": probability,
}
)
probs = pd.DataFrame(rows)
pivot = probs.pivot(index="example", columns="context", values="probability").reindex(
columns=EXPECTED_LABELS
)
x = np.arange(len(pivot.index))
width = 0.25
plt.figure(figsize=(8, 4.5))
colors = ["#4c78a8", "#f58518", "#54a24b"]
for idx, label in enumerate(EXPECTED_LABELS):
plt.bar(x + (idx - 1) * width, pivot[label], width, label=label, color=colors[idx])
plt.ylabel("Predicted probability")
plt.ylim(0, 1)
plt.xticks(x, pivot.index, rotation=18, ha="right")
plt.legend(frameon=False)
plt.title("Demo model example probabilities")
return _finish(path)