Spaces:
Sleeping
Sleeping
File size: 7,582 Bytes
0fff343 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | # 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_CODE` is insufficient; use `TUMOR_TISSUE_SITE` (sample) or `ICD_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` (from `RACE`; optionally also `ethnicity`).
- `tissue_site` β a clean anatomic-site field from `TUMOR_TISSUE_SITE` (preferred) or `ICD_O_3_SITE`, plus a derived boolean `is_oropharynx` (map the oropharynx/tonsil/base-of-tongue values; print the distinct site values + counts so we can confirm the mapping). Keep the existing `site`(ONCOTREE) column too.
- `sex` is 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 with `is_oropharynx == True`), and a `survives_site` boolean = 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 a `survives_purity` boolean (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/` or `engine_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/` or `engine_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.parquet` carries 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.
- `pytest` green, `tsc` clean, airgap tests untouched.
|