SPB Git

spb/wpai0001_uqo

Public
2.0 KB · 55 lines python
1"""Cluster bootstrap for accuracies and paired differences.23Items nested in generating templates (clusters) are not independent; the bootstrap resamples4clusters with replacement. Paired differences use identical items for both conditions.5"""6from __future__ import annotations78from dataclasses import dataclass910import numpy as np111213@dataclass14class CI:15    estimate: float16    low: float17    high: float18    level: float19    n_items: int20    n_clusters: int21    n_boot: int2223    def to_dict(self) -> dict:24        return self.__dict__.copy()252627def _cluster_index(clusters: list[str]) -> tuple[np.ndarray, int]:28    uniq = {c: i for i, c in enumerate(dict.fromkeys(clusters))}29    return np.array([uniq[c] for c in clusters]), len(uniq)303132def cluster_bootstrap_mean(values: np.ndarray, clusters: list[str], n_boot: int = 2000, level: float = 0.95, seed: int = 0) -> CI:33    values = np.asarray(values, dtype=float)34    cid, k = _cluster_index(clusters)35    rng = np.random.default_rng(seed)36    sums = np.bincount(cid, weights=values, minlength=k)37    counts = np.bincount(cid, minlength=k).astype(float)38    stats = np.empty(n_boot)39    for b in range(n_boot):40        draw = rng.integers(0, k, k)41        stats[b] = sums[draw].sum() / counts[draw].sum()42    a = (1 - level) / 243    return CI(float(values.mean()), float(np.quantile(stats, a)), float(np.quantile(stats, 1 - a)), level, len(values), k, n_boot)444546def cluster_bootstrap_paired_diff(a: np.ndarray, b: np.ndarray, clusters: list[str], n_boot: int = 2000, level: float = 0.95, seed: int = 0) -> CI:47    """CI for mean(a) - mean(b) on identical items (paired), resampling clusters."""48    return cluster_bootstrap_mean(np.asarray(a, float) - np.asarray(b, float), clusters, n_boot, level, seed)495051def seeds_summary(values: list[float]) -> dict:52    v = np.asarray(values, float)53    return {"n_seeds": int(len(v)), "mean": float(v.mean()), "sd": float(v.std(ddof=1)) if len(v) > 1 else None,54            "min": float(v.min()), "max": float(v.max())}55
Files · main
Menu

Theme