| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 |
- # viz/plots.py — Python 3.8 compatible plotting utilities
- from __future__ import annotations
- import math
- import numpy as np
- import matplotlib.pyplot as plt
- from cycler import cycler
- # ----- Unified style (kept lightweight) -----
- default_cycler = (
- cycler(color=["C0", "C1", "C2", "C3", "C4", "C5"])
- + cycler(marker=["s", "D", "^", "v", "o", "x"])
- + cycler(linestyle=[":", "--", "-", "-.", "--", ":"])
- )
- plt.rc("axes", prop_cycle=default_cycler)
- # ----- 95% CI (t critical) -----
- def tcrit_95(n: int) -> float:
- """Return ~95% t critical value for sample size n (simple, conservative)."""
- if n <= 1:
- return float("inf")
- if n < 30:
- # Conservative constant (close to df=9..29 range)
- return 2.262
- return 1.96
- def mean_ci95(vals):
- """Return (mean, half_width) for 95% CI."""
- arr = np.array(list(vals), dtype=float)
- n = len(arr)
- if n == 0:
- return 0.0, 0.0
- if n == 1:
- return float(arr[0]), 0.0
- m = float(arr.mean())
- s = float(arr.std(ddof=1))
- half = tcrit_95(n) * (s / math.sqrt(n))
- return m, half
- def plot_with_ci_band(ax, xs, mean, half, *, label, line_kwargs=None, band_kwargs=None):
- """Plot mean line and shaded CI band (Python 3.8 safe)."""
- line_kwargs = {} if line_kwargs is None else dict(line_kwargs)
- band = {"alpha": 0.25}
- if band_kwargs is not None:
- band.update(dict(band_kwargs))
- line, = ax.plot(xs, mean, label=label, **line_kwargs)
- ax.fill_between(xs, mean - half, mean + half, **band)
- return line
|