Spaces:
Sleeping
Prompt β confound pass: extend Effect's confounders + add site-stratified / purity-survival flags to the module ranking
Goal: let us tell whether a gene/module separates HPV for a mechanistic reason or via a confounder (sex, ancestry, anatomic site, immune composition). Read the named code first; don't assume. Airgap stays absolute (clinical/technical covariates are named non-gene variables β allowed, same category as the existing stage/age; purity is derived in the validation layer, never in the engine). pytest + tsc after.
VERIFIED data availability (don't re-assume β checked against the repo)
Raw HNSC clinical files (data/raw_hnsc/data_clinical_patient.txt / _sample.txt) contain: SEX, RACE, ETHNICITY, AJCC_PATHOLOGIC_TUMOR_STAGE, AGE, ICD_O_3_SITE (patient) and TUMOR_TISSUE_SITE / ONCOTREE_CODE (sample). The processed clinical.parquet (build_hnsc.py ~lines 110-117) currently keeps only site(=ONCOTREE_CODE), age, sex, stage, hpv_status. So:
- sex β already in the parquet. β
- race / ethnicity β in the raw files, NOT yet extracted. Need a build change.
- anatomic site for oropharynx β
ONCOTREE_CODEis insufficient; useTUMOR_TISSUE_SITE(sample) orICD_O_3_SITE(patient). Need a build change. - smoking β NOT in this download at all. Do not attempt to adjust for it.
- tumor purity β NOT in clinical. Must be derived from expression (see Part E).
Part A β extend the HNSC build to carry the confounders (data)
Note (verified): read_clinical_tsv returns the FULL dataframe, so RACE / TUMOR_TISSUE_SITE / ICD_O_3_SITE are reachable via merged.get(...) β no need to add them to HNSC_REQUIRED_*_COLS. In data_pipeline/build_hnsc.py (the clinical dict ~lines 110-117): read and write into clinical.parquet:
race(fromRACE; optionally alsoethnicity).tissue_siteβ a clean anatomic-site field fromTUMOR_TISSUE_SITE(preferred) orICD_O_3_SITE, plus a derived booleanis_oropharynx(map the oropharynx/tonsil/base-of-tongue values; print the distinct site values + counts so we can confirm the mapping). Keep the existingsite(ONCOTREE) column too.sexis already present β leave it. Print the non-null counts for race / tissue_site / is_oropharynx so missingness is visible. (Colorectal build unchanged.)
Part B β carry the new clinical fields through the API (no airgap change)
_prepare_lab_data (api/app.py) currently subsets cohort.clinical[["stage","age"]]. For HNSC, widen the kept set to include sex, race, tissue_site/is_oropharynx (only the columns that exist; guard each). These are named clinical variables, not gene symbols β airgap-clean. Keep colorectal as-is. The returned clinical DataFrame is what both Effect (via the run's ctx) and the module endpoint (Part D/E) will read, so the new columns must be in the RETURN, not just kept somewhere upstream.
Part C β generalize Effect to a configurable confounder set (engine)
Effect.execute (engine_v2/nodes.py ~436-471) hardcodes stage + age. Generalize it to adjust on a configurable list of confounders pulled from ctx.clinical (one-hot encode categoricals like sex/race/site; keep age continuous; dropna per the design matrix; guard rank/collinearity). Default must stay stage + age so existing MSI/TMB/HPV runs are byte-for-byte unchanged. When the extra clinical columns are present, allow Effect to include sex + race (NOT smoking β unavailable). The confounder set is fixed by config, not GP-selectable (the program shouldn't roam over arbitrary confounders). Update/extend the relevant engine_v2 tests.
Part D β site-stratified survival flag on the module ranking (validation layer)
IMPORTANT (verified): _compute_module_ranking currently discards clinical β line ~1282 is M_all, y_all, _clin, _extra = _prepare_lab_data(...) with clinical thrown away as _clin. Stop discarding it: capture the clinical frame, and align is_oropharynx to the held-out patients (the same test_ids it already reconstructs) so you can subset. Then, for the HNSC/HPV case add per-module:
combined_holdout_oropharynx: the module's mean-aggregate AUROC recomputed within the oropharynx subgroup only (held-out patients withis_oropharynx == True), and asurvives_siteboolean = it stays close to the full-cohort combined AUROC (within a tolerance, e.g. drop < ~0.05) rather than collapsing toward 0.5. This tells us whether the module separates HPV beyond just marking oropharynx tissue.- Guard small subgroups (skip / mark "n too small" if the oropharynx held-out has too few per class).
- Gate to HNSC; colorectal modules omit this gracefully.
Part E β purity-survival flag (validation layer; heaviest part β may be phased)
Purity isn't in the data, so derive an immune-infiltration proxy in the validation layer (names allowed here, exactly like the rank diagnostics): per patient, the mean expression of a small standard immune-marker set (e.g. CD8A, GZMB, PRF1, CD3D, CD2 β a fixed curated list in one place). Since the matrix M is opaque-columned, resolve those marker SYMBOLS to their opaque IDs via the bounded reveal path (same as reference sets), then mean those columns of M per patient. Higher proxy = more infiltrate = lower purity. Then per module add:
combined_holdout_highpurity: the module's mean-aggregate AUROC recomputed within the low-infiltration (high-purity) subset of held-out patients (e.g. bottom tertile of the proxy), and asurvives_purityboolean (same tolerance idea). This catches modules whose HPV signal is really an immune-composition artifact (they'll collapse in the high-purity subset).- This derivation lives in the validation/API layer β never in
engine/orengine_v2/. If scope is tight, ship Parts A-D first and land Part E second; mark it clearly.
Part F β UI: surface the flags + the winner's-curse caveat (presentation)
- In the Coordinated-modules table, show the survival flags per module (e.g. small "site β/β" and "purity β/β" chips, or the stratified AUROCs on expand). A module that survives both is a real candidate; one that collapses is a likely confound artifact.
- Add to the modules "?" the winner's-curse caveat we agreed on, verbatim intent: "These groups are scored by re-evaluating ~2,900 explored sets on the same small held-out set and showing the best β so the very top values are optimistically biased (the luckiest of thousands). The winning program was chosen by cross-validation, which guards against that, so trust it as the engine's pick even when a table row scores higher."
CONSTRAINTS
- Airgap: clinical confounders (sex/race/site) and the derived purity proxy are named non-gene variables / validation-layer computations β never enter
engine/orengine_v2/as gene identities. Module payloads stay opaque-ID + scores + flags. - Defaults preserve current behaviour (Effect default = stage+age; non-HNSC unaffected). Colorectal + existing tests stay green.
Checkpoint
- HNSC
clinical.parquetcarries race + tissue_site + is_oropharynx (with printed counts); sex already there. - Effect adjusts on a configurable set, default stage+age unchanged; with HNSC clinical present it can include sex+race.
- Module ranking shows site-stratified (and, when Part E lands, purity) survival flags for HNSC/HPV; modules that are site/immune artifacts visibly collapse.
- Modules "?" carries the winner's-curse caveat.
pytestgreen,tscclean, airgap tests untouched.