Skip to main content

hpdex

High-performance differential expression analysis for single-cell data

PyPI version Python 3.10+ MIT License Status: stable C++ backend

Overview · Installation · Quick Start · API · Kernels · Benchmarks · FAQ · License


🔎 Overview

hpdex now ships a C++ backend with careful memory layout and true multithreading, delivering large speedups for Mann–Whitney U–based DE on single-cell matrices:

  • ⚡ Sparse CSC: in DE scenarios (gene × group pairs), the pure Mann–Whitney U kernel is often hundreds of times faster than SciPy when multi-threaded.
  • 🧩 End-to-end DE (parallel_differential_expression): with scheduling and reuse, typical pipelines are 2–3 orders of magnitude faster than pdex-style Python loops.
  • 📦 Dense: even single-thread is commonly ~3× faster than SciPy on dense inputs; multi-thread scales further.
  • 📈 Statistical parity: tie-aware U, continuity correction, and asymptotic p-values match scipy.stats.mannwhitneyu under equivalent settings (within float tolerance).

Key capabilities:

  • 🧵 C++ multithreading, per-thread workspaces, low allocator pressure
  • 🧮 Float & histogram kernels (auto-selected by data type)
  • 🧠 Tie & continuity corrections; exact or asymptotic p-values in backend
  • 🧱 Robust sparse support (CSC highly recommended)
  • 🧰 Drop-in friendly design & clean Python API

⚙️ Installation

From PyPI

pip install hpdex

From source

git clone --recurse-submodules https://github.com/AI4Cell/hpdex.git # for highway submodule
cd hpdex
pip install -e .
Requirements
  • Python ≥ 3.10
  • numpy, scipy, pandas, anndata, tqdm
  • Building from source requires a C++17 compiler; OpenMP recommended for threading

🚀 Quick Start

import anndata as ad
from hpdex import parallel_differential_expression

adata = ad.read_h5ad("data.h5ad")

df = parallel_differential_expression(
    adata,
    groupby_key="perturbation",
    reference="control",
    threads=8,                 # C++ multithreading
    tie_correction=True,
    use_continuity=True,       # continuity correction for U -> Z
    show_progress=True,
)

df.to_csv("de_results.csv", index=False)

Output schema (DataFrame):

column description
target target group name
feature gene / feature id
p_value two-sided p-value from Mann–Whitney U
u_statistic U₁ statistic (reference vs target)
fold_change mean(target) / mean(reference)
log2_fold_change log2(fold_change)
fdr BH-adjusted p-value

📚 API Reference

parallel_differential_expression

parallel_differential_expression(
    adata: ad.AnnData,
    groupby_key: str,
    reference: str | None,          # None -> treated as "non-targeting" baseline
    groups: list[str] | None = None,
    metric: str = "wilcoxon",       # currently only "wilcoxon" (Mann–Whitney U)
    tie_correction: bool = True,
    use_continuity: bool = True,
    min_samples: int = 2,
    threads: int = -1,              # -1 -> use all logical cores
    clip_value: float = 20.0,
    show_progress: bool = True,
) -> pd.DataFrame

Notes

  • threads controls the C++ backend concurrency.
  • reference=None creates a “non-targeting” baseline for missing labels.
  • Internally the matrix is converted to CSC (scipy.sparse.csc_matrix) for best performance.
  • The backend currently defaults to zero-handling = "min" (zeros at head), suitable for UMI counts.

Low-level backend (advanced)

from hpdex.backend import mannwhitneyu, group_mean

U1, P = mannwhitneyu(
    matrix_csc,           # scipy.sparse.csc_matrix or ndarray (dense handled internally)
    group_id_int32,       # shape: (n_cells,), 0=reference, 1..G-1=targets, -1=ignored
    n_targets: int,
    ref_sorted=False,     # set True if your ref slice is already sorted
    tar_sorted=False,     # set True if target slices are sorted
    use_continuity=True,
    tie_correction=True,
    zero_handling="min",  # "none" | "min" | "max" | "mix"
    threads=-1,
    show_progress=False,
)

means = group_mean(
    matrix_csc,
    group_id_int32,
    G,                    # total groups (reference + targets)
    include_zeros=True,
    threads=-1,
)  # returns array shape: (G, n_genes)

🧪 Statistical Kernels

Float kernel

  • Merge-rank U with tie tracking; contiguous slices; vector-friendly
  • Memory: O(n) per slice; scales well with threads

Histogram kernel

  • For integer/UMI counts with limited range
  • Bucketized ranks reduce sorting; memory O(bins) (≪ n)

Common

  • Exact/asymptotic p-values in backend
  • Tie & continuity corrections
  • Batch-wise scheduling across (gene × group)

📈 Benchmarks

Typical observations (indicative; varies by CPU, threads, sparsity):

Scenario Matrix Speedup vs SciPy
Dense single-thread U per gene dense ~3×
Sparse multi-thread U (DE setting) CSC, 64T 100×–300×
End-to-end DE vs pdex-style baseline CSC, 64T 10²–10³×

Why fast? Thread-local workspaces (no per-column allocations), cache-friendly slices, zero-aware rank merge, and reduced Python overhead.


🧷 Testing

cd test && cat bench_mwu.txt | xargs python bench_mwu.py

❓ FAQ

Do you correct for multiple testing? Yes. The output fdr applies Benjamini–Hochberg (BH).
Why are p-values sometimes ~0? Very large samples and strong effects can underflow in float. This is expected; prefer fdr for decisions.
How are zeros handled? Backend supports "none", "min", "max", "mix". The high-level API currently uses "min" by default, which is suitable for UMI counts.

📄 License

MIT — see LICENSE.


Built for large-scale single-cell perturbation analysis, now powered by a C++ backend.

Metadata

Release files for hpdex 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for hpdex 1.0.1
File
hpdex-1.0.1-cp313-cp313-win_amd64.whl CPython 3.13 CPython 3.13 Windows x86-64 Details
hpdex-1.0.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64, Linux glibc 2.26+ x86-64 Details
hpdex-1.0.1-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details
hpdex-1.0.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.28+ x86-64, Linux glibc 2.26+ x86-64 Details
hpdex-1.0.1-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details
hpdex-1.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.28+ x86-64, Linux glibc 2.26+ x86-64 Details
hpdex-1.0.1-cp310-cp310-win_amd64.whl CPython 3.10 CPython 3.10 Windows x86-64 Details
hpdex-1.0.1-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.28+ x86-64, Linux glibc 2.26+ x86-64 Details

Total release size: 1.8 MB

Release files / hpdex-1.0.1-cp313-cp313-win_amd64.whl

Download URL hpdex-1.0.1-cp313-cp313-win_amd64.whl
Size 150.7 kB
Tags CPython 3.13 Windows x86-64
SHA-256 checksum
How to use checksums
d3c5ae0d3246f3e3d9ca4c8949ec015625778810ed03b088c67f67152bd2d696
BLAKE2b-256 checksum
How to use checksums
11a158928122ca563d24544ff81841dbbf5704091232964d82f4999d4ff0b912
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / hpdex-1.0.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl

Download URL hpdex-1.0.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
Size 307.3 kB
Tags CPython 3.13 Linux glibc 2.26+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
6968eac368a1f0364d9fd1349c1044a4c0e65ff79948e890503ca976696ffe6a
BLAKE2b-256 checksum
How to use checksums
305ac16de3f6157ad54082a6dfe9234aaf5aade7b954f9aa7bb7c8d51cb168e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / hpdex-1.0.1-cp312-cp312-win_amd64.whl

Download URL hpdex-1.0.1-cp312-cp312-win_amd64.whl
Size 150.7 kB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
16fff3a00b5ce398be7f3ecde9a1f69ee65f053b6a08ca56d4f15581e72ab080
BLAKE2b-256 checksum
How to use checksums
98c06d7d07f62d60b9fcd199b87dca0cdff43eaf40364ae47cc775621bbed8c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / hpdex-1.0.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl

Download URL hpdex-1.0.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
Size 312.9 kB
Tags CPython 3.12 Linux glibc 2.26+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
cdaa83ba0f9a1d320eb73195312006eb51a031ca0e0e695205fe5e8597e3ef80
BLAKE2b-256 checksum
How to use checksums
acb7e624003b3acdae623ec0a95f8dec3b1eeb3bae0c2f4b881909ae63e87802
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / hpdex-1.0.1-cp311-cp311-win_amd64.whl

Download URL hpdex-1.0.1-cp311-cp311-win_amd64.whl
Size 149.4 kB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
3cd1ae5808198dbd1c73c6534090837a1de6ab2dcce2b6e302a8338fbb6039c0
BLAKE2b-256 checksum
How to use checksums
e4dc901ef5af9a0b39b8e7f4386bc445d46595b44901cf9f34021329f2b80f23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / hpdex-1.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl

Download URL hpdex-1.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
Size 307.8 kB
Tags CPython 3.11 Linux glibc 2.26+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
27f7ffe5674554ea459f1787606fcaf8d04abe024c5f6d667ebaa64373048566
BLAKE2b-256 checksum
How to use checksums
99dbfebb8674d65e3a2a803fb469dcdc0dde327e9ee0bf1e792cfe47d06f5d2b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / hpdex-1.0.1-cp310-cp310-win_amd64.whl

Download URL hpdex-1.0.1-cp310-cp310-win_amd64.whl
Size 149.4 kB
Tags CPython 3.10 Windows x86-64
SHA-256 checksum
How to use checksums
f75fd0fc578a22297af4d48dc4da196fa719819d3a190d8277856b472787f081
BLAKE2b-256 checksum
How to use checksums
776823bf9a8cb16afd30438d7d5f9c3bd97e33377af90edccb4d600e0311b3e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / hpdex-1.0.1-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl

Download URL hpdex-1.0.1-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl
Size 308.3 kB
Tags CPython 3.10 Linux glibc 2.26+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
ca78a57caae089e16ba4b9affb6684d95a3eff5a538dfe5ac8cedab0fcdea508
BLAKE2b-256 checksum
How to use checksums
e4fcafeffbd4d965947770a94879157d098d030fc6c0521a09e3b4ba85185caf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release history Release notifications | RSS feed

This release

1.0.1 This release

8 release files

1.0.0

8 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page