WADE — Wasserstein Area Differential Expression
A two-group differential-expression test for RNA-seq counts that answers two questions instead of one:
- Is there a difference? — the ordinary question, which any DE method answers.
- What kind of difference is it? — a shift affecting every sample, or a pronounced change confined to a small subset of one group.
The second question is the point. A gene altered in 5% of cases and a gene shifted 2× in all of them can produce the same mean difference, and a first-moment test cannot tell them apart. WADE separates them, and never asks you to declare in advance which you are looking for: there is no percentile cutoff, no window width, no tuning parameter describing the shape of the effect.
For each gene it compares the two groups' whole quantile functions. Stage 1
tests the mean shift; stage 2 tests whether a global fold change is an
adequate explanation, against a null built by binomial thinning of the raw
counts; three threshold-free descriptors then say how much of the group
differs, which way, and by how many folds. Inference is by label permutation
with a tail refinement and BH-FDR. docs/method.md is the
full account.
Scope: raw counts. WADE takes raw counts, not TPM or CPM, and is for discrete count data: the tie-breaking jitter and the thinning null both need counts.
Install
pip install wade-rnaseq # NumPy only; compiled kernel included
pip install 'wade-rnaseq[all]' # + polars (results out), plotly and matplotlib (figures), SciPy (saddlepoint)
The distribution is wade-rnaseq (the name wade on PyPI belongs to an
unrelated project); the import name is wade. Wheels cover Linux (x86-64,
ARM64), macOS (Apple silicon, Intel) and Windows (x64) for Python 3.10+. On
any other platform pip builds from source, which needs a Rust toolchain
(rustup); WADE still runs without the compiled kernel,
about 80× slower on the subset test. To build from a clone see
CONTRIBUTING.md.
Not on PyPI yet. Until the first upload:
pip install git+https://github.com/mkiyer/wade(needs Rust).
Use it
import polars as pl, wade
counts = pl.read_csv("counts.tsv", separator="\t") # gene id + one column per sample, RAW counts
samples = pl.read_csv("samples.tsv", separator="\t") # sample_id, condition, ...
cond = wade.condition(samples, key="sample_id", column="condition",
case="tumor", control="normal")
res = wade.wade(counts, normalizer=1.0, cond=cond, nperms=2000, seed=1)
wade.write_results(res, "results.tsv") # + results.manifest.json
counts may be a NumPy array, a polars or pandas DataFrame, a sparse matrix,
or a wade.Counts; WADE reads no files. normalizer is a per-gene vector
(gene length gives TPM-like values), a matrix, a scalar (1.0 gives CPM), or
the name of a column carried alongside the counts. The alignment of samples
to the condition is strict and names the offenders.
| column | question it answers |
|---|---|
p_mean_shift, padj_mean_shift |
stage 1: is average expression different? |
p_subset, padj_subset |
stage 2: is a global shift an inadequate explanation? |
affected_fraction |
what fraction of samples differ? 1.0 = all of them |
direction |
+1 all up, -1 all down, 0 two-sided |
subset_log2_fc |
by how many folds does that fraction differ? |
log2_fc, mean_shift |
the global fold change and the signed quantile area |
z_mean_shift, z_subset |
permutation z-scores, for ranking once p-values hit the floor |
Four patterns, read off the two p-values:
p_mean_shift |
p_subset |
what you are looking at |
|---|---|---|
| significant | — | a global shift; an ordinary DE method finds this too |
| significant | significant | a subset, strong enough to move the mean |
| — | significant | a distributional change with no net mean shift: a balanced subset, or a variance change |
| — | — | not differential |
There is deliberately no categorical label: turning continuous statistics into classes needs thresholds, which is what this design exists to avoid.
from wade import plot_gene, plot_volcano, plot_stages, plot_drivers # needs wade-rnaseq[plot]
plot_gene(res, gene="MYC") # what does this gene's difference look like?
plot_volcano(res, stage="both", label=8) # which genes?
plot_stages(res) # what kind of difference?
plot_drivers(res, "MYC", counts) # should I believe this one?
The first two genes have the same log₂ fold change. The mean-shift test
cannot tell them apart; the curve, p_subset and affected_fraction (0.98
against 0.15) can. A flat curve is a global fold change; a curve that sits at
zero and then climbs is a subset.
Before you run it
Two things are arithmetic on your design, and no amount of data or
permutations moves them (docs/method.md §10).
- Resolution. The grid has
min(n_case, n_ctrl)points.affected_fractionis quantitative above roughly 100 per group and only qualitative below 50. - Detectability. If
ksamples carry a signal, no permutation test can resolve much belowC(n1, k) / C(n1 + n0, k). It is driven by imbalance: balancing the groups helps far more than adding cases.wade.detectability_floor(n_case, n_ctrl, k)computes it.
And one honest caveat: stage 2's p-values are conservative by a measured 4–80×
where they are safe, and can be anti-conservative in another regime. Ranking
survives; calling with an FDR loses real subset genes. Stage 1 has an exact
alternative (stage1="saddlepoint"); stage 2 is the open problem
(docs/pvalue-review.md, ISSUES.md).
Documentation
docs/manual.md— start here: install, run, read the output, the options that matter, what to check before trusting a result.docs/method.md— what WADE computes and why, with every measurement, and what it cannot do.docs/pvalue-review.md— the p-value estimators and the open problem, written for a statistician.notebooks/demo.qmd— the demo on synthetic data with planted ground truth, the source of the figures above;notebooks/benchmark.qmd— head to head against COPA, OS, ORT, MOST, LSOSS, the t-test, Wilcoxon and waddR.CONTRIBUTING.mdandISSUES.md— for developers.
Status
The statistic, both stages, the descriptors with bootstrap intervals, permutation inference with tail refinement and BH, Rust kernels for both permutation loops (bitwise against the NumPy path), the figures, and the data-in / results-out boundary are implemented and tested: 854 tests in about 20 s. At 20,000 genes, 100 v 100 and 2,000 permutations a full run takes about 17 s on a 16-core laptop; a 30,000-gene × 80,000-sample cohort completes in 30 minutes at a 56 GB peak. Every fixture value is re-derived from NumPy, SciPy and the closed forms by a test that is forbidden from importing WADE.
Provenance and license
Extracted from the MCTP cfRNA analysis codebase, where it began as a set of
functions called HITLIB. No patient-derived data is included. GPL-3; see
LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wade_rnaseq-0.1.0.tar.gz.
File metadata
- Download URL: wade_rnaseq-0.1.0.tar.gz
- Upload date:
- Size: 2.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c3015420605d44542755e462a2d9c46f092697cbce5bcdc3a00d74d1a361903
|
|
| MD5 |
1685da5afe162c31d276a51a6a9166a2
|
|
| BLAKE2b-256 |
456e40dd386ad9d29e4965abe26c657cfdf071eccfb5fae0ccd7160f81c2949d
|
Provenance
The following attestation bundles were made for wade_rnaseq-0.1.0.tar.gz:
Publisher:
ci.yml on mkiyer/wade
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wade_rnaseq-0.1.0.tar.gz -
Subject digest:
3c3015420605d44542755e462a2d9c46f092697cbce5bcdc3a00d74d1a361903 - Sigstore transparency entry: 2768154878
- Sigstore integration time:
-
Permalink:
mkiyer/wade@33aa47b54f33fb9c20d33af06d54be25b016852d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mkiyer
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@33aa47b54f33fb9c20d33af06d54be25b016852d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file wade_rnaseq-0.1.0-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: wade_rnaseq-0.1.0-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 344.2 kB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0c2bce2bea55715f38fb51ed17848b52244c2810f5188b0569edbcf622f4438
|
|
| MD5 |
342ec4278517f0ddb005595da9b48bf5
|
|
| BLAKE2b-256 |
6722444660a77c5b7181771b019375ba2b6e8d60fad65ff087a4ba246a60dc1e
|
Provenance
The following attestation bundles were made for wade_rnaseq-0.1.0-cp310-abi3-win_amd64.whl:
Publisher:
ci.yml on mkiyer/wade
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wade_rnaseq-0.1.0-cp310-abi3-win_amd64.whl -
Subject digest:
c0c2bce2bea55715f38fb51ed17848b52244c2810f5188b0569edbcf622f4438 - Sigstore transparency entry: 2768155197
- Sigstore integration time:
-
Permalink:
mkiyer/wade@33aa47b54f33fb9c20d33af06d54be25b016852d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mkiyer
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@33aa47b54f33fb9c20d33af06d54be25b016852d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file wade_rnaseq-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: wade_rnaseq-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 466.6 kB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a58cb4da29b9936b2c68f3116821f9faf2dc502022aad78fb6459e373e759b6c
|
|
| MD5 |
1d289fc75f3e909926b229c8d3cc4e0b
|
|
| BLAKE2b-256 |
278581e833df406317783ecf7d4b1ae4b8309a28b8eb20155af73befee08f051
|
Provenance
The following attestation bundles were made for wade_rnaseq-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
ci.yml on mkiyer/wade
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wade_rnaseq-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
a58cb4da29b9936b2c68f3116821f9faf2dc502022aad78fb6459e373e759b6c - Sigstore transparency entry: 2768154980
- Sigstore integration time:
-
Permalink:
mkiyer/wade@33aa47b54f33fb9c20d33af06d54be25b016852d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mkiyer
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@33aa47b54f33fb9c20d33af06d54be25b016852d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file wade_rnaseq-0.1.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: wade_rnaseq-0.1.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 456.6 kB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e5de21bb21b0dde86f9268a54bdf81486f5f1268364071fc874bd943e85f2d9
|
|
| MD5 |
bd2909dfa1a4dfe20cfd8928daf20d2d
|
|
| BLAKE2b-256 |
340b5dde78289238b56b8b56a37b103853092a013f2c5351b4ddef654f74f425
|
Provenance
The following attestation bundles were made for wade_rnaseq-0.1.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
ci.yml on mkiyer/wade
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wade_rnaseq-0.1.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
6e5de21bb21b0dde86f9268a54bdf81486f5f1268364071fc874bd943e85f2d9 - Sigstore transparency entry: 2768155049
- Sigstore integration time:
-
Permalink:
mkiyer/wade@33aa47b54f33fb9c20d33af06d54be25b016852d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mkiyer
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@33aa47b54f33fb9c20d33af06d54be25b016852d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file wade_rnaseq-0.1.0-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: wade_rnaseq-0.1.0-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 424.5 kB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5eea92a1ecbf6a052f6cfbac990048068d95227e92bf22f5178dec34612fed3
|
|
| MD5 |
6b5d1ed5cc551aee8a2e1847b8563beb
|
|
| BLAKE2b-256 |
209d0e134ada08be2831a3ef10b3b2642eb88b797c2e8657d28bfa1ae32e1a73
|
Provenance
The following attestation bundles were made for wade_rnaseq-0.1.0-cp310-abi3-macosx_11_0_arm64.whl:
Publisher:
ci.yml on mkiyer/wade
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wade_rnaseq-0.1.0-cp310-abi3-macosx_11_0_arm64.whl -
Subject digest:
f5eea92a1ecbf6a052f6cfbac990048068d95227e92bf22f5178dec34612fed3 - Sigstore transparency entry: 2768155277
- Sigstore integration time:
-
Permalink:
mkiyer/wade@33aa47b54f33fb9c20d33af06d54be25b016852d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mkiyer
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@33aa47b54f33fb9c20d33af06d54be25b016852d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file wade_rnaseq-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: wade_rnaseq-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 439.3 kB
- Tags: CPython 3.10+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fac771ac43a1f559de73e372312c41b0260f0b07a31cd49106972c0117304ca8
|
|
| MD5 |
a076911291285ad18c04e56b8580ceb4
|
|
| BLAKE2b-256 |
06219bcf6b96132ff0f7b07ab61019f5d2228d3043cc7790f8577cbd2ec4e6bb
|
Provenance
The following attestation bundles were made for wade_rnaseq-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl:
Publisher:
ci.yml on mkiyer/wade
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wade_rnaseq-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl -
Subject digest:
fac771ac43a1f559de73e372312c41b0260f0b07a31cd49106972c0117304ca8 - Sigstore transparency entry: 2768155142
- Sigstore integration time:
-
Permalink:
mkiyer/wade@33aa47b54f33fb9c20d33af06d54be25b016852d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mkiyer
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@33aa47b54f33fb9c20d33af06d54be25b016852d -
Trigger Event:
workflow_dispatch
-
Statement type: