BamNado
High-performance tools and utilities for working with BAM and BigWig files in modern genomics workflows. BamNado is written in Rust for speed and low memory use and provides both a command-line interface and Python bindings.
Overview
BamNado is designed for efficient, streaming manipulation of BAM files and signal tracks. It focuses on fast coverage generation, flexible filtering, and lightweight post-processing of bedGraph and BigWig data.
Common use cases:
- Rapid generation of coverage tracks from large BAM files
- Filtering reads by tags or barcodes to produce targeted BigWigs
- Fragment-aware coverage for ATAC-seq and related assays
- BigWig comparison and aggregation across samples
- Post-processing of binned signal tracks for visualization
Useful in workflows including single-cell and Micro-Capture-C (MCC), and many others.
Documentation: API docs on docs.rs
Features
- High-performance, streaming implementations in Rust
- Cross-platform support (Linux, macOS, Windows)
- BAM → bedGraph / BigWig coverage generation
- Fragment-aware and strand-specific pileups
- Read filtering by mapping quality, length, strand, fragment size, tags, and barcodes
- BigWig comparison (subtraction, ratio, log-ratio)
- BigWig aggregation (sum, mean, median, min, max)
- bedGraph post-processing with
collapse-bedgraph - Python bindings for selected functionality
Installation
Pre-built binaries (recommended)
Download the appropriate binary from the releases page.
After downloading, move to your user bin directory (no root required):
chmod +x bamnado
mv bamnado ~/.local/bin/bamnado
Ensure ~/.local/bin is on your PATH (add to ~/.bashrc or ~/.zshrc if needed):
export PATH="$HOME/.local/bin:$PATH"
Alternatively, you can run the binary directly from the download location:
./bamnado --help
cargo binstall
If you have cargo-binstall installed, this downloads the pre-built binary automatically:
cargo binstall bamnado
Docker
docker pull ghcr.io/alsmith151/bamnado:latest
docker run --rm ghcr.io/alsmith151/bamnado:latest --help
Images are available for linux/amd64 and linux/arm64.
Cargo
If you have Rust installed:
cargo install bamnado
Build from source
git clone https://github.com/alsmith151/BamNado.git
cd BamNado
cargo build --release
Optional dependency: samtools
samtools is not required but is strongly recommended if your BAM files have non-standard or incomplete headers (e.g. files produced by CellRanger). BamNado automatically falls back to samtools view -H to parse the header when the built-in parser fails. Without samtools on your PATH, BamNado will error on such files.
Install via conda or your system package manager:
conda install -c bioconda samtools
# or
brew install samtools
Command-line interface
Getting help
bamnado --help # List all commands
bamnado <command> --help # Help for a specific command
Available commands
| Command | Aliases | Description |
|---|---|---|
bam-coverage |
coverage |
BAM → bedGraph / BigWig coverage |
multi-bam-coverage |
multi-coverage |
Merge coverage from multiple BAM files into one track |
split |
— | Split BAM by tag or barcode |
split-exogenous |
split-spikein |
Split BAM into endogenous / exogenous reads |
modify |
— | Filter and/or adjust reads in a BAM file |
bigwig-compare |
compare-bigwigs |
Compare two BigWig files bin by bin |
bigwig-aggregate |
aggregate-bigwigs |
Aggregate multiple BigWig files into one track |
bigwig-infer-scale |
infer-scale |
Infer scaling factor and library size from a normalised BigWig |
bam-normalize |
norm-factors |
Compute between-sample scaling factors from BAM files |
collapse-bedgraph |
collapse |
Collapse adjacent equal-score bins in a bedGraph |
Read filtering
All coverage commands share common read filter flags:
| Flag | Default | Description |
|---|---|---|
--strand |
both |
forward, reverse, or both |
--proper-pairs |
off | Keep only properly-paired reads |
--min-mapq |
20 | Minimum mapping quality |
--min-length |
20 | Minimum read length (bp) |
--max-length |
1000 | Maximum read length (bp) |
--min-fragment-len |
— | Minimum insert size (bp); paired-end only |
--max-fragment-len |
— | Maximum insert size (bp); paired-end only |
--blacklist |
— | BED file of regions to exclude |
--barcode-allowlist |
— | Text file of cell barcodes (one per line) |
--read-group |
— | Keep only this read group |
--tag / --tag-value |
— | Keep reads where TAG == VALUE |
--ignore-duplicates |
off | Exclude PCR/optical duplicate reads |
--ignore-secondary |
off | Exclude secondary alignments |
--ignore-supplementary |
off | Exclude supplementary alignments |
Coverage-specific flags
| Flag | Default | Description |
|---|---|---|
--normalize |
raw |
Signal normalization method: raw, rpkm, or cpm |
--fragment-counts |
off | Count fragments instead of individual read alignments |
--ignore-scaffolds |
off | Skip scaffold or unplaced chromosomes |
--threads |
6 |
Threads used when writing BigWig output |
Examples
Generate basic coverage
bamnado bam-coverage \
--bam sample.bam \
--output coverage.bedgraph \
--bin-size 100
Generate high-quality, normalized coverage
bamnado bam-coverage \
--bam sample.bam \
--output coverage_hq.bw \
--bin-size 100 \
--normalize rpkm \
--min-mapq 30 \
--proper-pairs
Extract nucleosome-free regions from ATAC-seq
bamnado bam-coverage \
--bam atac.bam \
--output nfr_forward.bw \
--bin-size 10 \
--strand forward \
--min-fragment-len 100 \
--max-fragment-len 200 \
--fragment-counts \
--min-mapq 20
Filter by cell barcode (single-cell)
bamnado bam-coverage \
--bam possorted_genome_bam.bam \
--output cell_coverage.bw \
--bin-size 100 \
--barcode-allowlist barcodes.txt \
--fragment-counts
Filter by SAM tag (MCC viewpoint)
bamnado bam-coverage \
--bam mcc.bam \
--output BCL2_viewpoint.bw \
--bin-size 50 \
--tag VP \
--tag-value BCL2 \
--fragment-counts \
--min-mapq 30
Compare two BigWig files
bamnado bigwig-compare \
--bw1 sample_treated.bw \
--bw2 sample_control.bw \
--comparison log-ratio \
--pseudocount 1e-3 \
--threads 6 \
-o treated_vs_control.bw
Aggregate multiple BigWig files
bamnado bigwig-aggregate \
--bigwigs sample1.bw sample2.bw sample3.bw \
--method mean \
--threads 6 \
-o mean_coverage.bw
Simplify bedGraph file
bamnado collapse-bedgraph \
--input signal.bedgraph \
--output signal.collapsed.bedgraph
Normalize between ChIP-seq samples
bamnado bam-normalize \
--bams chip1.bam chip2.bam chip3.bam \
--method csaw-background \
--bin-size 10000 \
--blacklist blacklist.bed
Apply the reported scale_factor for each sample to bam-coverage -f (or
bigwig-aggregate --scale-factors) when generating tracks for that sample.
Between-sample normalization
Comparing ChIP-seq / CUT&Tag / CUT&RUN samples side by side requires removing
composition and technical bias, not just sequencing depth. A sample with a stronger
pulldown has more of its library consumed by peaks, which deflates its background and
makes every other region look artificially low relative to a shallower or weaker-pulldown
sample. Simple total-count (CPM) scaling does not correct for this — it's the same
problem csaw::normFactors / edgeR::calcNormFactors (TMM) solve in R/Bioconductor.
bamnado bam-normalize bins the genome, counts filtered reads (or fragments) per bin per
sample, and reports a per-sample scaling factor plus the underlying bin count matrix for
downstream sanity checks (e.g. M-A plots). It reports factors only — it does not generate
bigwigs itself.
Method choice
| Method | When to use |
|---|---|
csaw-background (default) |
Typical composition bias: TMM restricted to large background windows with the most-enriched bins excluded |
tmm |
TMM over all bins, without excluding enriched regions |
cpm |
Naive depth-only baseline, useful for comparison against the other methods |
median-of-ratios |
A robust DESeq2-style alternative estimator |
spike-in |
When a spike-in genome was used and is trusted more than a background estimate |
Applying the result
scale_factor is centred on 1 (the geometric mean across samples), so it can be passed
directly to bam-coverage -f <factor> or bigwig-aggregate --scale-factors for each
corresponding sample.
Use --counts-out counts.tsv to write the underlying bin count matrix (chrom, start, end,
one column per sample) for QC, e.g. plotting an M-A plot to confirm the fitted factor sits
at the centre of the trimmed M-value cloud.
Spike-in caveat
The spike-in method reads mapped-read counts directly from each BAM's index (no BAM
record scan), so those counts are unfiltered by MAPQ, duplicates, or
secondary/supplementary status — the same basis as bam-coverage's internal mapped-read
count. --exogenous-prefix is required and --bin-size is ignored for this method.
Python API
Installing the Python package also installs the bamnado CLI, including all Rust CLI
commands and flags:
pip install bamnado
bamnado bam-coverage --help
BamNado provides Python bindings for high-performance BAM signal generation with flexible read filtering. Install from PyPI:
pip install bamnado
# or
uv pip install bamnado
Quick start
import bamnado
import numpy as np
# Generate coverage from a BAM file
signal = bamnado.get_signal_for_chromosome(
bam_path="input.bam",
chromosome_name="chr1",
bin_size=50,
)
print(f"Mean coverage: {np.mean(signal):.2f}")
ReadFilter class
Customize read filtering with the ReadFilter class. All parameters are optional:
import bamnado
# Create a filter for high-quality, properly-paired reads
strict_filter = bamnado.ReadFilter(
min_mapq=30,
proper_pair=True,
min_length=50,
)
# Apply the filter
signal = bamnado.get_signal_for_chromosome(
bam_path="input.bam",
chromosome_name="chr1",
bin_size=100,
read_filter=strict_filter,
)
Filter parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
min_mapq |
int |
0 |
Minimum mapping quality |
proper_pair |
bool |
False |
Require properly paired reads |
min_length |
int |
0 |
Minimum read length (bp) |
max_length |
int |
1000 |
Maximum read length (bp) |
strand |
str |
"both" |
"forward", "reverse", or "both" |
min_fragment_length |
int | None |
None |
Minimum insert size (bp); paired-end only |
max_fragment_length |
int | None |
None |
Maximum insert size (bp); paired-end only |
blacklist_bed |
str | None |
None |
BED file of excluded regions |
whitelisted_barcodes |
list[str] | None |
None |
Cell barcodes to include (CB tag) |
read_group |
str | None |
None |
Read group to keep (RG tag) |
filter_tag |
str | None |
None |
SAM tag to filter on (e.g. "VP") |
filter_tag_value |
str | None |
None |
Required value for filter_tag |
Usage examples
Basic coverage
import bamnado
import numpy as np
signal = bamnado.get_signal_for_chromosome(
bam_path="sample.bam",
chromosome_name="chr1",
bin_size=50,
scale_factor=1.0,
use_fragment=False,
)
print(f"Signal shape: {signal.shape}, type: {signal.dtype}")
print(f"Mean: {np.mean(signal):.2f}, Max: {np.max(signal):.2f}")
Nucleosome-free regions (ATAC-seq)
import bamnado
# Forward-strand reads, 100–200 bp fragments
nfr_filter = bamnado.ReadFilter(
strand="forward",
min_fragment_length=100,
max_fragment_length=200,
min_mapq=20,
)
nfr_signal = bamnado.get_signal_for_chromosome(
bam_path="atac.bam",
chromosome_name="chr1",
bin_size=10,
use_fragment=True,
read_filter=nfr_filter,
)
Barcode-filtered coverage (single-cell)
import bamnado
# Get coverage for specific cell barcodes
cell_barcodes = ["ACGTACGT-1", "TGCATGCA-1", "AAATAAAA-1"]
barcode_filter = bamnado.ReadFilter(
whitelisted_barcodes=cell_barcodes,
min_mapq=25,
)
signal = bamnado.get_signal_for_chromosome(
bam_path="possorted_genome_bam.bam",
chromosome_name="chr1",
bin_size=100,
use_fragment=True,
read_filter=barcode_filter,
)
Tag-filtered coverage (e.g. MCC viewpoint)
import bamnado
# Get coverage for specific viewpoint
vp_filter = bamnado.ReadFilter(
filter_tag="VP",
filter_tag_value="BCL2",
min_mapq=30,
)
vp_signal = bamnado.get_signal_for_chromosome(
bam_path="mcc.bam",
chromosome_name="chr1",
bin_size=50,
use_fragment=True,
read_filter=vp_filter,
)
High-confidence coverage
import bamnado
# High-quality, properly-paired reads
confident_filter = bamnado.ReadFilter(
min_mapq=30,
proper_pair=True,
min_length=50,
min_fragment_length=100,
max_fragment_length=500,
)
signal = bamnado.get_signal_for_chromosome(
bam_path="sample.bam",
chromosome_name="chr1",
bin_size=50,
scale_factor=1e6, # CPM normalization
use_fragment=True,
read_filter=confident_filter,
)
get_signal_for_chromosome parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
bam_path |
str |
— | Path to indexed BAM file |
chromosome_name |
str |
— | Chromosome to process (e.g. "chr1") |
bin_size |
int |
50 |
Bin width in base pairs |
scale_factor |
float |
1.0 |
Linear scaling factor |
use_fragment |
bool |
False |
Use fragment (pair) coordinates instead of read |
ignore_scaffold_chromosomes |
bool |
True |
Skip non-standard chromosomes |
read_filter |
ReadFilter | None |
None |
Optional read filter |
Returns: numpy.ndarray of dtype float32 with length = chromosome size / bin_size
Note: Fragment length filtering (min_fragment_length, max_fragment_length) requires paired-end BAM files and will raise ValueError on single-end data.
Between-sample scaling factors
compute_scale_factors mirrors the bam-normalize CLI command — see
Between-sample normalization for the underlying method.
import bamnado
result = bamnado.compute_scale_factors(
["chip1.bam", "chip2.bam", "chip3.bam"],
method="csaw-background",
bin_size=10000,
)
print(result.sample_names, result.scale_factors)
# Include the bin count matrix for QC (e.g. an M-A plot)
result = bamnado.compute_scale_factors(
["chip1.bam", "chip2.bam"],
method="tmm",
return_counts=True,
)
counts = result.counts() # (n_bins, n_samples) uint64 array, or None
compute_scale_factors parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
bam_paths |
list[str] |
— | Input BAM file paths, one per sample |
method |
str |
"csaw-background" |
"tmm", "csaw-background", "cpm", "median-of-ratios", or "spike-in" |
bin_size |
int |
10000 |
Genomic bin width in bp; ignored for "spike-in" |
sample_names |
list[str] | None |
None |
Defaults to each BAM's file stem, disambiguated if duplicated |
exclude_top_percent |
float |
5.0 |
"csaw-background" only: percent of highest-mean bins dropped before estimating |
reference_sample |
str | None |
None |
TMM reference sample; defaults to the one closest to the geometric mean |
logratio_trim |
float |
0.3 |
TMM trim fraction for M-values |
sum_trim |
float |
0.05 |
TMM trim fraction for A-values |
exogenous_prefix |
str | None |
None |
Required for method="spike-in" |
use_fragment |
bool |
False |
Count fragments (pairs) instead of individual reads |
ignore_scaffold_chromosomes |
bool |
True |
Skip scaffold / unplaced chromosomes |
read_filter |
ReadFilter | None |
None |
Applied per sample |
return_counts |
bool |
False |
Populate ScaleFactorResult.counts() |
Returns: ScaleFactorResult with method, sample_names, library_sizes,
norm_factors, scale_factors, reference_sample, n_bins_total, n_bins_used, and
(when return_counts=True) counts(), bin_chroms, bin_starts, bin_ends.
Development
cargo build --release
cargo test
License
Apache-2.0 OR MIT
Metadata
Release files for bamnado 0.9.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 | Provenance | |
|---|---|---|---|---|
| bamnado-0.9.0.tar.gz | 138.9 kB | Without provenance | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| bamnado-0.9.0-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| bamnado-0.9.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| bamnado-0.9.0-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| bamnado-0.9.0-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 72.1 MB
Release files / bamnado-0.9.0.tar.gz
| Download URL | bamnado-0.9.0.tar.gz |
|---|---|
| Size | 138.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca48412759c5d7f186698575a4203ffdc07290653afcd0f461a328181313d467
|
|
BLAKE2b-256 checksum How to use checksums |
556999802c32c66d7bb62ce708f82e230fc94496a7b16e33afa59c1fe928f61d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|
Release files / bamnado-0.9.0-cp310-abi3-win_amd64.whl
| Download URL | bamnado-0.9.0-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 17.6 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
9317535eec97d20d192e3d8f68aa78d9fc824df609350d3746b23374581bb9cf
|
|
BLAKE2b-256 checksum How to use checksums |
629c97860e71921071038d0f94be3afae670a351465a92a5690eadef25622889
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|
Release files / bamnado-0.9.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | bamnado-0.9.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 19.0 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
5422267afd817b906457cf23e0afd907832a8492215b8108680c1c3b90caa70c
|
|
BLAKE2b-256 checksum How to use checksums |
39e7d74be18265eac354a5429b2d8cf49c406c0b79f81258bc929fb7ade569ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|
Release files / bamnado-0.9.0-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | bamnado-0.9.0-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 17.4 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
56b90b1eff514e1892156fa88a6884cbaad8cbbe137f0e2552f8a7681f9d1952
|
|
BLAKE2b-256 checksum How to use checksums |
14691e7ad41970a739ba33669fd0716be977cbaa490b6cbdb2f47f751559ffd5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|
Release files / bamnado-0.9.0-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | bamnado-0.9.0-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 18.0 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
ae8f146e2b7311235dac6c16bc38049e3f49c96ec2a79bd23e74c6bb204e7c44
|
|
BLAKE2b-256 checksum How to use checksums |
5e9537f2d78c28ae99385eaacdc141bfc9384ce7ca56a2f2f00dbb9e86cce99c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.14.1
|
Security for bamnado 0.9.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.