Preprocessing and QC

Fragment matrices, peak calling and the qc namespace of quality-control functions.

rustscenic.preproc

scATAC fragment preprocessing.

Public API: rustscenic.preproc.fragments_to_matrix(fragments_path, peaks_path) -> AnnData rustscenic.preproc.call_peaks(fragments_path, cluster_per_barcode, ...) -> pd.DataFrame rustscenic.preproc.qc.insert_size_stats(fragments_path) -> pd.DataFrame rustscenic.preproc.qc.frip(fragments_path, peaks_path) -> pd.Series rustscenic.preproc.qc.tss_enrichment(fragments_path, tss_df) -> pd.Series

All of these are Rust-native and require no extra Python dependencies beyond the four rustscenic ships with (numpy, pandas, pyarrow, scipy).

Together they replace pycisTopic's fragment parsing, iterative peak calling, and per-cell QC - the MACS2-free, Java-free, Python 3.10–3.13 path through rustscenic's ATAC preprocessing surface.

See docs/atac-preprocessing-scope.md for scope + validation plan.

qc

Per-barcode cell QC metrics.

Invocable as a namespace class so users see the grouping in IDE auto-complete and the module docstring lists them at the top. Each method reads the fragments file once and returns a pandas object indexed by barcode.

insert_size_stats staticmethod

insert_size_stats(fragments_path: str | Path) -> pd.DataFrame

Per-barcode insert-size summary.

Returns DataFrame indexed by barcode with columns: - mean (float) - mean fragment length - median (float) - median fragment length - n_fragments (uint32) - count used in the stats - sub_nucleosomal (uint32) - fragments < 150 bp - mono_nucleosomal (uint32) - 150–299 bp - di_nucleosomal (uint32) - 300–449 bp

Parameters:

Name Type Description Default
fragments_path str | Path

Path to the 10x fragments file used for the per-barcode summaries.

required

frip staticmethod

frip(fragments_path: str | Path, peaks_path: str | Path) -> pd.Series

Per-barcode fraction of reads in peaks.

Returns a pandas Series indexed by barcode. UCSC vs Ensembl chrom conventions are normalised at join time.

Parameters:

Name Type Description Default
fragments_path str | Path

Path to the 10x fragments file.

required
peaks_path str | Path

BED-file path defining the peak intervals used for overlap counts.

required

tss_enrichment staticmethod

tss_enrichment(fragments_path: str | Path, tss: DataFrame) -> pd.Series

Per-barcode TSS enrichment (signal-over-background).

Parameters:

Name Type Description Default
fragments_path str | Path

10x fragments file.

required
tss DataFrame

DataFrame with columns ['chrom', 'position'] listing transcription-start-site coordinates. Chromosome names in either UCSC (chr1) or Ensembl (1) convention are fine - they're normalised to match the fragments' chrom namespace.

required

Returns:

Type Description
pd.Series indexed by barcode with TSS enrichment scores (0.0 if
no fragments overlap any TSS window).

fragments_to_matrix

fragments_to_matrix(
    fragments_path: str | Path,
    peaks_path: str | Path,
    *,
    cell_barcodes: list[str] | None = None,
)

Build a cells × peaks AnnData from fragments + peaks.

Parameters:

Name Type Description Default
fragments_path str or Path

10x cellranger fragments.tsv or fragments.tsv.gz.

required
peaks_path str or Path

Consensus peak BED (plain or .gz). Uses the first three columns; column 4 (if present) is stored as peak_id.

required
cell_barcodes list[str] | None

Optional cell-called barcode whitelist. When supplied, the Rust parser emits matrix rows only for matching barcodes, avoiding a large all-observed-barcode matrix from raw 10x fragments.

None

Returns:

Type Description
AnnData
  • .X is a scipy.sparse.csr_matrix of u32 fragment-record-in-peak counts with shape (n_cells, n_peaks). The optional fifth 10x fragments column is retained separately as total_counts, not used as a matrix multiplier.
  • .obs_names are cell barcodes (from the fragments file).
  • .var_names are peak IDs (from the peak BED, or chrom:start-end if no name column).
  • .obs["fragments_per_cell"] and .obs["total_counts"] are per-cell QC metrics over the full fragments file (before the peak intersection).
Notes

UCSC (chr1) and Ensembl (1) chrom conventions are matched via normalise_chrom - a peak BED in either convention joins to fragments in either convention. Peaks on chroms actually absent from the fragments file are silently dropped.

call_peaks

call_peaks(
    fragments_path: str | Path,
    cluster_per_barcode: Series | ndarray | list[int],
    *,
    n_clusters: int | None = None,
    window_size: int = 50,
    min_fragments_per_window: int = 3,
    quantile_threshold: float = 0.95,
    max_gap: int = 250,
    peak_half_width: int = 250,
    output_bed: str | Path | None = None,
) -> pd.DataFrame

Call iterative consensus peaks from pseudobulked fragments.

Corces-2018-style density-window peak calling: per cluster, tile the genome, threshold at the top-quantile of the cluster's per-window count distribution, merge adjacent significant windows, then union cluster peaks into a consensus set via greedy iterative overlap rejection. No MACS2, no external binaries.

Parameters:

Name Type Description Default
fragments_path str | Path

10x cellranger fragments.tsv[.gz].

required
cluster_per_barcode Series | ndarray | list[int]

Cluster id per barcode (0-indexed). Same length as the fragments' unique-barcode set - i.e. as what fragments_to_matrix(...).obs produces. Use u32::MAX (2**32 - 1) to mark unassigned. Signed arrays may use negative IDs for unassigned barcodes. Accepts a list, numpy array, or a pandas Series (the latter's index is used to align to the fragments' barcode order - the Rust layer will reject a mismatch loudly).

required
n_clusters int | None

Number of distinct cluster ids. Defaults to deriving max(cluster_per_barcode) + 1 inside the Rust peak caller.

None
window_size int

See rustscenic-preproc's PeakCallingConfig. Defaults match the Corces-2018 convention (50 bp windows, top 5 % quantile, 250 bp max gap, 501 bp wide peaks).

50
min_fragments_per_window int

See rustscenic-preproc's PeakCallingConfig. Defaults match the Corces-2018 convention (50 bp windows, top 5 % quantile, 250 bp max gap, 501 bp wide peaks).

50
quantile_threshold int

See rustscenic-preproc's PeakCallingConfig. Defaults match the Corces-2018 convention (50 bp windows, top 5 % quantile, 250 bp max gap, 501 bp wide peaks).

50
max_gap int

See rustscenic-preproc's PeakCallingConfig. Defaults match the Corces-2018 convention (50 bp windows, top 5 % quantile, 250 bp max gap, 501 bp wide peaks).

50
peak_half_width int

See rustscenic-preproc's PeakCallingConfig. Defaults match the Corces-2018 convention (50 bp windows, top 5 % quantile, 250 bp max gap, 501 bp wide peaks).

50
output_bed str | Path | None

If set, write the called peaks as a 4-column BED (chrom, start, end, name) to this path.

None

Returns:

Type Description
DataFrame

One row per consensus peak with columns ['chrom', 'start', 'end', 'name']. name is chrom:start-end.