from __future__ import annotations import argparse import json import random from collections import defaultdict from pathlib import Path def classify_transformation(pair: dict) -> str: age_changed = pair.get("source_age") != pair.get("target_age") sex_changed = pair.get("source_sex") != pair.get("target_sex") if age_changed and sex_changed: return "intersectional" if age_changed: return "age_only" if sex_changed: return "sex_only" return "none" def stratified_sample( pairs: list[dict], n_per_stratum: int, seed: int, ) -> list[int]: rng = random.Random(seed) by_stratum: dict[str, list[int]] = defaultdict(list) for i, pair in enumerate(pairs): stratum = classify_transformation(pair) if stratum != "none": by_stratum[stratum].append(i) selected: list[int] = [] for stratum in ["age_only", "sex_only", "intersectional"]: pool = by_stratum[stratum] rng.shuffle(pool) n = min(n_per_stratum, len(pool)) selected.extend(pool[:n]) if n < n_per_stratum: print( f"Warning: only {n} pairs available for {stratum} " f"(requested {n_per_stratum})" ) return selected def main() -> None: parser = argparse.ArgumentParser( description="Stratified sampling of pairs for radiologist validation" ) parser.add_argument("--pairs", type=str, required=True) parser.add_argument("--n-per-stratum", type=int, default=150) parser.add_argument("--seed", type=int, default=42) parser.add_argument("--output", type=str, default="data/pairs.json") args = parser.parse_args() with open(args.pairs) as f: all_pairs = json.load(f) indices = stratified_sample(all_pairs, args.n_per_stratum, args.seed) sampled = [all_pairs[i] for i in indices] for pair in sampled: pair["_transform_type"] = classify_transformation(pair) counts: dict[str, int] = defaultdict(int) for pair in sampled: counts[pair["_transform_type"]] += 1 output_path = Path(args.output) output_path.parent.mkdir(parents=True, exist_ok=True) with open(output_path, "w") as f: json.dump(sampled, f, indent=2) print(f"Sampled {len(sampled)} pairs from {len(all_pairs)} total") for stratum, count in sorted(counts.items()): print(f" {stratum}: {count}") print(f"Saved to: {output_path}") if __name__ == "__main__": main()