Files
hypertower-sample/scripts/make_smoke_data.py
T
ryan 4e815df327 Smoke job repo: grouped tabular dataset + config
400 rows / 200 patients, two samples each, so grouped splitting is actually
exercised. Signal is modest and noisy on purpose — a separable toy would score
1.0 with a broken model and prove nothing.
2026-08-13 21:16:22 +02:00

52 lines
2.2 KiB
Python

"""Generate the smoke dataset. Deterministic — rerunning reproduces the file byte
for byte, so the committed CSV can always be regenerated and diffed.
Signal is real but modest: the label depends on age/iop/cdr with noise, so a
working pipeline lands around 0.85-0.95 AUC and a broken one sits near 0.5. A
perfectly separable toy would hide bugs by scoring 1.0 no matter what.
"""
import csv, math, random
from pathlib import Path
rng = random.Random(20260813)
N_PATIENTS, EYES = 200, 2
SITES = ["skovde", "gothenburg", "linkoping"]
out = Path(__file__).resolve().parents[1] / "data" / "smoke" / "labels.csv"
rows = []
for p in range(N_PATIENTS):
# Patient-level traits: both samples from one patient share them, which is
# what makes grouped splitting matter — a per-row split would leak.
base_age = rng.gauss(62, 11)
site = rng.choice(SITES)
frailty = rng.gauss(0, 1)
for e in range(EYES):
age = base_age + rng.gauss(0, 1.5)
iop = rng.gauss(16 + 2.2 * frailty, 2.8) # intraocular pressure
cdr = min(0.95, max(0.05, rng.gauss(0.45 + 0.09 * frailty, 0.11))) # cup/disc
rnfl = rng.gauss(95 - 7 * frailty, 9) # retinal nerve fibre layer
noise = rng.gauss(0, 1)
logit = -6.0 + 0.045 * age + 0.20 * iop + 4.2 * cdr - 0.035 * rnfl + 0.5 * noise
pos = rng.random() < 1 / (1 + math.exp(-logit))
rows.append({
"sample_id": f"S{p:03d}_{e}",
"patient_id": f"P{p:03d}",
"age": round(age, 1),
"iop": round(iop, 1),
"cdr": round(cdr, 3),
"rnfl": round(rnfl, 1),
"noise_feat": round(noise, 3),
"site": site,
"notes": "routine screening" if not pos else "referred",
"diagnosis": "glaucoma" if pos else "healthy",
})
out.parent.mkdir(parents=True, exist_ok=True)
with out.open("w", newline="") as fh:
w = csv.DictWriter(fh, fieldnames=list(rows[0]))
w.writeheader()
w.writerows(rows)
pos = sum(r["diagnosis"] == "glaucoma" for r in rows)
print(f"wrote {out} rows={len(rows)} patients={N_PATIENTS} positives={pos} ({pos/len(rows):.0%})")