feat: add drug_mean and drug_tissue_mean response transformations (conditional-mean residualization) - #465
Open
jfrog64 wants to merge 4 commits into
Conversation
The shipped response transformations ("standard", "minmax", "robust")
are global monotone rescalings of the response. Since the reported
metrics are rank-based (Spearman) and partly mean-centered per drug and
per cell line, such rescalings cannot change any score.
GroupMeanCenterer subtracts a conditional mean instead: the per-drug
mean, estimated on the training fold only, so the model spends its
capacity on the residual drug x cell line structure rather than on the
drug main effect. inverse_transform adds the mean back, so predictions
stay on the original response scale. Drugs unseen during fit fall back
to the global training mean, which keeps the transformation safe for LDO
and cross-study prediction.
DrugResponseDataset supplies the drug ids as groups when the
transformation advertises requires_groups, so plain sklearn scalers keep
being called exactly as before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add the new drevalpy.response_transformation module to the API page and list drug_mean in the --response_transformation option and in the "Available Response Transformations" section of the usage docs. Also make the DrugResponseDataset cross-reference in the module docstring fully qualified so sphinx can resolve it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## development #465 +/- ##
===============================================
+ Coverage 80.34% 82.45% +2.10%
===============================================
Files 101 122 +21
Lines 8171 10024 +1853
===============================================
+ Hits 6565 8265 +1700
- Misses 1606 1759 +153 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
GroupMeanCenterer takes the fields the group key is built from as group_fields, and estimates a mean for every prefix of it. The lookup walks the levels from specific to coarse, so an unseen (drug, tissue) combination falls back to the mean of its drug and only an unknown drug to the global training mean. Without that nesting the transformation would be useless in LTO, where every test tissue is unseen by construction. DrugResponseDataset supplies the requested columns instead of always the drug ids, and raises if it does not have one of them, so that a dataset without tissues does not silently behave like drug_mean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Checklist for all PRs
docsis updated — new module added todocs/API.rst, new options documented indocs/usage.rstChanges
New features
--response_transformationcurrently offersstandard,minmaxandrobust. All three are global monotonerescalings of the response. That means they cannot change any of the metrics DrEval reports: Spearman and Kendall
are rank-based and therefore invariant under a monotone map, and the
Normalized *metrics subtract theNaiveMeanEffectsPredictorbaseline, which rescales along with everything else. Pearson/R² are invariant under anaffine map. So today the flag exists, but for the reported scores it is close to a no-op — the only effect is on the
optimizer's conditioning.
This adds two transformations that actually change what the model has to learn:
drug_meananddrug_tissue_mean.drevalpy/response_transformation.py(new) containsGroupMeanCenterer, a transformer that subtracts aconditional mean — estimated on the training fold only — instead of a global constant:
The model is then trained on the residuals rather than on the main effect, which is where the
Normalized *metricslook anyway.
inverse_transformadds the group mean back, so predictions are written out on the original responsescale and every downstream evaluation is unaffected.
group_fieldsnames the columns ofDrugResponseDatasetthe group key is built from:drug_mean→("drug_ids",): removes the drug main effect, i.e. how sensitive cell lines are to this drug onaverage.
drug_tissue_mean→("drug_ids", "tissue"): also removes the tissue-specific sensitivity of a drug, so themodel is left with the within-tissue drug × cell line structure.
The means are nested, not flat.
fitestimates a mean for every prefix ofgroup_fields, and the lookup walksthem from specific to coarse: a (drug, tissue) combination that did not occur in the training fold falls back to the
mean of its drug, an unknown drug to the global training mean. Without that,
drug_tissue_meanwould be useless inLTO, where every test tissue is unseen by construction and a flat fallback would push every test row onto the global
mean; with it, LTO simply degenerates to
drug_mean.Because the transformer needs to know which row belongs to which group,
DrugResponseDatasetsupplies the columns —and only for transformations that ask for them:
transform,fit_transformandinverse_transformforward these kwargs. ForStandardScaler,MinMaxScalerandRobustScalerthe dict is empty, so those calls are byte-for-byte the ones that ran before — no existingbehaviour changes, and
--response_transformationkeeps its current default.Details that matter for the eval modes DrEval cares about:
fitonly ever sees the training fold, so no test-fold information leaks into the offsets.with the ASCII unit separator and uses
searchsortedper level, so this stays one vectorized pass per levelinstead of a per-row dict lookup.
tissueis optional onDrugResponseDataset. If a dataset has none,drug_tissue_meanraises with a messagenaming the missing field rather than silently behaving like
drug_mean.Wiring:
get_response_transformationreturns the transformer for both names,check_argumentsaccepts them, and thetyper help text plus
docs/usage.rstlist them.Tests:
tests/test_response_transformation.py(22 tests) coversrequires_groupsis set onGroupMeanCentererand absent on plain sklearn scalersfitrecords the global mean and the per-group means, and both nesting levels for two group fieldstransformcenters every group on zero, andinverse_transformround-trips to the original values(n,)and(n, 1)input, since the dataset calls it with column vectorssearchsortedclipping)
("A", "B_C")vs("A_B", "C"))groupsit degenerates to plain mean-centering, and an empty training fold does not raiseDrugResponseDataset.fit_transform/inverse_transformhand over drug ids and tissues, and transform thepredictions with the same groups
drug_tissue_meangroupsargumentget_response_transformationandcheck_argumentsaccept both names and still reject nonsenseBug fixes
Maintenance