pyfastcar
A pure-Python re-implementation of FastCAR (Gocht / Berg et al., BMC Genomics 2023) for fast, deterministic per-gene correction of ambient RNA in droplet-based single-cell RNA-seq data.
- AnnData-native — drop-in for the scanpy ecosystem
- No
rpy2, no R install — the empty-droplet detection, per-genegMax/frCprofiling, and count subtraction are all implemented directly in NumPy/SciPy - Same function surface as the R workflow (
determine.background.to.remove→remove.background, plusdescribe.ambient.RNA.sequence/recommend.empty.cutoff) - Bit-for-bit reproducibility against the R reference — FastCAR is fully deterministic (no RNG anywhere), so
pyfastcarmatches R FastCAR exactly (seetests/test_exact_match.py)
This is a standalone mirror of the canonical implementation that lives in
omicverse. All algorithmic work is developed upstream in omicverse and synced here for users who want FastCAR without the full omicverse stack.
Install
pip install pyfastcar
Dependencies: numpy, scipy, pandas, anndata.
What it does
Droplet-based scRNA-seq libraries are contaminated by ambient RNA — transcripts released by lysed cells that end up in every droplet. FastCAR estimates this contamination from the empty droplets of the raw, unfiltered count matrix and subtracts it, gene by gene, from the real cells.
The algorithm is fully deterministic — a handful of vectorised operations on the raw count matrix:
- Empty droplets are the libraries whose total UMI count is below an
empty_droplet_cutoff(thE, default 100). All-zero "unused barcodes" are excluded from the empty-droplet population. - For each gene, compute
gMax— the highest count of that gene in any single empty droplet — andfrC— the fraction of (non-zero) empty droplets that contain the gene. - A gene is corrected only when its
frCclears the allowable contamination fractioncontamination_chance_cutoff(frAA, default 0.005). For those genes,gMaxis subtracted from every cell's count, with negative results floored at zero.
Quick-start (AnnData API)
import anndata as ad
from pyfastcar import correct_anndata
# raw = unfiltered AnnData (cells × genes): real cells + empty droplets
raw = ad.read_h5ad("raw_feature_bc_matrix.h5ad")
cells = ad.read_h5ad("filtered_feature_bc_matrix.h5ad") # the real cells
corrected = correct_anndata(
raw, cells,
empty_droplet_cutoff=100, contamination_chance_cutoff=0.005,
)
corrected.uns["fastcar"]["ambient_rna_profile"] # per-gene subtract amounts
corrected.uns["fastcar"]["diagnostics"] # gMax / frC table
corrected.uns["fastcar"]["n_genes_corrected"]
The wrapper transposes internally, so you always work in the natural cells × genes AnnData orientation.
Low-level functional API (mirrors R one-to-one)
from pyfastcar import (
determine_background_to_remove, remove_background,
describe_ambient_rna_sequence, recommend_empty_cutoff,
)
# full = unfiltered genes × droplets matrix (real cells + empty droplets)
# cells = filtered genes × cells matrix of real cells
profile = determine_background_to_remove(
full, cells, empty_droplet_cutoff=100, contamination_chance_cutoff=0.005)
corrected = remove_background(cells, profile) # ambient-corrected matrix
# pass return_table=True for a per-gene diagnostic DataFrame:
profile, table = determine_background_to_remove(full, cells, return_table=True)
Choosing the empty-droplet cutoff
desc = describe_ambient_rna_sequence(
full, start=50, stop=500, by=25, contamination_chance_cutoff=0.005)
cutoff = recommend_empty_cutoff(desc)
What's included
| Python | R counterpart | Purpose |
|---|---|---|
determine_background_to_remove |
determine.background.to.remove |
per-gene ambient-RNA profile (gMax/frC) |
remove_background |
remove.background |
subtract the profile, floor at zero |
correct_anndata |
(new) | AnnData-native one-shot wrapper |
describe_ambient_rna_sequence |
describe.ambient.RNA.sequence |
empty-droplet cutoff profiling |
recommend_empty_cutoff |
recommend.empty.cutoff |
suggest a cutoff from the profile |
read_cell_matrix / read_full_matrix |
read.cell.matrix / read.full.matrix |
10x CellRanger loaders |
Reproducing R results exactly
FastCAR has no stochastic component — given the same raw matrix and the same
empty_droplet_cutoff / contamination_chance_cutoff, the output is fully
determined. pyfastcar therefore reproduces R FastCAR bit-for-bit: the
per-gene subtraction amounts, the corrected integer matrix and the
threshold-profiling table are all identical.
tests/test_exact_match.py runs the R reference (FastCAR::determine.background.to.remove
remove.background+describe.ambient.RNA.sequence) inside theCMAPenvironment on a deterministic synthetic raw matrix, saves every intermediate, and checks that the Python port reproduces them element-for-element.
pip install -e ".[dev]"
pytest tests/ -q # smoke tests + R-parity tests
pytest tests/test_smoke.py -q # smoke tests only (no R required)
examples/compare_R_vs_Python.ipynb runs both implementations on a real raw
10x dataset (data/pbmc1k_raw.h5ad, a subset of 10x Genomics' pbmc_1k_v3
raw feature-barcode matrix that still contains empty droplets) and visualizes
the bit-exact agreement with omicverse.
Relationship to omicverse
Developed upstream in omicverse:
- Canonical implementation: lives in the omicverse single-cell preprocessing stack
- Standalone mirror (this repo): same code, same API, minus the omicverse packaging
Citation
If you use this package, please cite the original FastCAR paper:
Gocht A.M., Berg M., et al. FastCAR: fast correction for ambient RNA to facilitate differential gene expression analysis in single-cell RNA-sequencing datasets. BMC Genomics 24, 2023.
and acknowledge omicverse / this repo for the Python port.
License
Apache-2.0. The upstream R package FastCAR is GPL-3; this is an independent re-implementation of its published algorithm.
Metadata
Release files for pyfastcar 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyfastcar-0.1.0.tar.gz | 25.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyfastcar-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.1 kB
Release files / pyfastcar-0.1.0.tar.gz
| Download URL | pyfastcar-0.1.0.tar.gz |
|---|---|
| Size | 25.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
696913bae3d15c42af39c8332d3bebe34eb84cdbb3aeb648e2a5f2cf8452aee8
|
|
BLAKE2b-256 checksum How to use checksums |
ea47c3cd89e93891ee6492a3f29aaa2d5a91046300cdb260a69b091c4d5250e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 22, 2026.
Transparency logRelease files / pyfastcar-0.1.0-py3-none-any.whl
| Download URL | pyfastcar-0.1.0-py3-none-any.whl |
|---|---|
| Size | 20.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6f2a77fff8bcc078ad83b3dc995b72db77f69f178daa54281df1daf26864f8cc
|
|
BLAKE2b-256 checksum How to use checksums |
2b30f054df6cf5933cd59db9d3b8551b694def83c799ca94fe942d5100bdda83
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 22, 2026.
Transparency log