Skip to content

feat: add mixscale continuous perturbation scoring#945

Merged
Zethson merged 1 commit into
scverse:mainfrom
stefanm808:feat/mixscale
Jun 10, 2026
Merged

feat: add mixscale continuous perturbation scoring#945
Zethson merged 1 commit into
scverse:mainfrom
stefanm808:feat/mixscale

Conversation

@stefanm808

Copy link
Copy Markdown
Contributor

Implements the Mixscale scoring method (Jiang et al., Nat Cell Biol 2025) as a new method on the Mixscape class. Unlike the binary KO/NP classification in mixscape(), mixscale() computes a continuous perturbation efficiency score per cell likescalar projection onto the estimated perturbation direction vector.

Reuses existing _get_perturbation_markers() pipeline for DE gene detection and follows the same code patterns as mixscape() for split handling, layer access & scaling.

Closes #921 (partial - continuous scoring component)

PR Checklist

  • Referenced issue is linked
  • If you've fixed a bug or added code that should be tested, add tests!

Description of changes
Added Mixscape.mixscale() for continuous perturbation efficiency scoring, implementing the method from Jiang, Dalgarno et al., Nature Cell Biology (2025). Unlike the binary KO/NP classification in mixscape(), mixscale() computes a continuous perturbation efficiency score per cell via scalar projection onto the estimated perturbation direction vector. This is particularly useful for CRISPRi/CRISPRa screens where cells exhibit a gradient of perturbation responses.

Usage:

ms = pt.tl.Mixscape()
ms.perturbation_signature(adata, "perturbation", "NT", split_by="replicate")
ms.mixscale(adata, "gene_target", "NT", layer="X_pert")
# Continuous scores in adata.obs["mixscale_score"]

Technical details

Algorithm:

  1. Reuses existing _get_perturbation_markers() pipeline for DE gene detection
  2. Subsets perturbation signatures to DE genes
  3. Computes perturbation direction vector (mean perturbed − mean control)
  4. Scalar-projects each cell's signature onto this direction
  5. The Z-score standardizes relative to the NT control distribution

Follows the same code patterns as mixscape() for split handling, layer access, and scaling. No new dependencies added. 9 new tests added, all 5 existing Mixscape tests still pass.

Additional context

This addresses item 1 (continuous perturbation scoring) from #921. Items 2-4 (weighted DE, decomposition, program signatures) are planned for follow-up PRs.

@codecov-commenter

codecov-commenter commented Apr 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.37063% with 59 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.81%. Comparing base (12897e1) to head (e57c3b9).
⚠️ Report is 89 commits behind head on main.

Files with missing lines Patch % Lines
pertpy/tools/_perturbation_efficacy/_mixscale.py 77.24% 43 Missing ⚠️
pertpy/tools/_perturbation_efficacy/_base.py 82.60% 16 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #945      +/-   ##
==========================================
+ Coverage   73.54%   77.81%   +4.26%     
==========================================
  Files          48       50       +2     
  Lines        5613     6580     +967     
==========================================
+ Hits         4128     5120     +992     
+ Misses       1485     1460      -25     
Files with missing lines Coverage Δ
pertpy/tools/__init__.py 78.37% <100.00%> (+0.60%) ⬆️
pertpy/tools/_perturbation_efficacy/_mixscape.py 88.12% <100.00%> (ø)
pertpy/tools/_perturbation_efficacy/_base.py 82.60% <82.60%> (ø)
pertpy/tools/_perturbation_efficacy/_mixscale.py 77.24% <77.24%> (ø)

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Zethson Zethson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

thanks for the contribution! I'd very happy to get this feature merged eventually.

This looks a bit AI generated. Could you please disclose that? There's a few things that the contributing guide outlines that was missed. It would be awesome if you could have another look, please.

Some initial feedback:

  1. Should this be in the mixscape code base or should this be its own Tool? Even from a user perspective.
  2. This also needs to show up in the tutorials. Maybe a general version of mixscape & mixscale? We might need a new term then.
  3. We should likely also point this out clearer in our documentation which pretty much just mentions mixscale for now.
  4. The ground truth is the R implementation. Could you please compare your version against the R version? We did the same for mixscape. They should be as close as possible.

Comment thread pertpy/tools/_mixscape.py Outdated
Comment thread pertpy/tools/_mixscape.py Outdated
Comment thread pertpy/tools/_mixscape.py Outdated
Comment thread pertpy/tools/_mixscape.py Outdated
Comment thread pertpy/tools/_mixscape.py Outdated
Comment thread pertpy/tools/_mixscape.py Outdated
Comment thread tests/tools/test_mixscale.py Outdated
Comment thread tests/tools/test_mixscale.py Outdated
Comment thread tests/tools/test_mixscale.py Outdated
@stefanm808

Copy link
Copy Markdown
Contributor Author

Hey thanks for taking a look! I ended up overlooking your styling conventions in the process. A separate tool suggestion is a good shout actually... probably makes more sense architecturally. I will swing back to this when I get a chance! Have a great day.

@Zethson

Zethson commented May 19, 2026

Copy link
Copy Markdown
Member

@stefanm808 very kind check in - is there anything you'd need from me to revive this PR?
I'd love to see you finalize it.

Thanks!

@Zethson

Zethson commented Jun 5, 2026

Copy link
Copy Markdown
Member

@stefanm808 I might take this PR over next week unless you want to finish it?

Zethson added a commit to stefanm808/pertpy that referenced this pull request Jun 9, 2026
Adds pt.tl.Mixscale for continuous per-cell perturbation scoring
(Jiang et al., Nature Cell Biology 2025), matching the satijalab/Mixscale
R implementation to floating-point precision.

Mixscape is refactored into a _mixscape/ package: a shared
PerturbationScreenAnalyzer base (perturbation_signature + DE marker
detection) with Mixscape (binary) and Mixscale (continuous) as siblings.
pt.tl.Mixscape is unchanged.

Builds on scverse#945.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Zethson Zethson force-pushed the feat/mixscale branch 4 times, most recently from d2b3f48 to 601afb9 Compare June 10, 2026 09:52
Adds pt.tl.Mixscale for continuous per-cell perturbation scoring
(Jiang et al., Nature Cell Biology 2025), matching the satijalab/Mixscale
R implementation to floating-point precision.

Mixscape is refactored into a _perturbation_efficacy package: a shared
PerturbationEfficacyAnalyzer base (perturbation_signature + DE marker
detection) with Mixscape (binary) and Mixscale (continuous) as siblings.
pt.tl.Mixscape is unchanged.

Renames the tutorial to perturbation_efficacy and bumps the pertpy-tutorials
submodule to the matching commit (scverse/pertpy-tutorials#64).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Zethson

Zethson commented Jun 10, 2026

Copy link
Copy Markdown
Member

OK I think this PR is now ready. Thank you very much @stefanm808 for your initial work! I hope you're happy with the outcome. Please feel free to have a look and propose any improvements if necessary.

@Zethson Zethson merged commit 652395a into scverse:main Jun 10, 2026
17 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add mixscale for continuous perturbation scoring

3 participants