CountMut
Unified ultra-fast strand-aware mutation counter — C backend, Python wrapper.
CountMut counts base/substitution ratios from BAM files with fast C core and a thin Python wrapper. It fuses the two classic ways of walking a BAM:
- pileup-based (
bam_mplp_auto/ pysam pileup) — fast, sees indels/ref-skips, general. - read-walk (countmut's "no pileup") — walk reads directly, only touch the target sites.
Both produce identical output, and the tool can process whole genomes in parallel (threads).
Why it's fast & correct
- The hot loop (BAM read, pileup, per-(site,strand) base counting, mate-overlap
dedup, quality/conversion classification) is in C (
backend/countmut_core, built on the self-contained htslib subset from lh3/minipileup). - Strand-aware (countmut biological-strand rule for paired-end reads).
- Paired-end overlap dedup: at an overlapping position a fragment is counted
once, choosing the best mate by
(mapq, read1, base-qual)— the thing minipileup gets wrong. - Parallel: divides the genome into bins and processes them across threads
(
--threads). - Memory-clean (verified under AddressSanitizer).
Install
pip install -e .
# or, to prebuild the C core:
make backend
Quick start
# strand-aware A->G mutation count (bisulfite / m6A style)
countmut -i in.bam -r ref.fa -o mut.tsv --ref-base A --mut-base G
# per-site base counts (perbase/mpileup style)
countmut -i in.bam -r ref.fa --mode base -o depth.tsv
# alleles -> VCF (minipileup style)
countmut -i in.bam -r ref.fa --mode allele --vcf -o allele.vcf
Modes
| Mode | Output |
|---|---|
mutation |
chrom pos strand motif u0 u1 u2 m0 m1 m2 [o0 o1 o2] (strand-aware substitution table) |
base |
chrom pos [strand] ref depth a c g t n [ins del ref_skip fail] |
allele |
chrom pos ref depth ref_count alt alt_count, or VCF with --vcf |
Filtering with expressions (-e / -p)
Filtering is done with samtools-style filter expressions — there are no
separate --min-mapq/--min-baseq/--trim-* flags; write them as expressions
instead.
-e, --expression <STR>— per-base read filter (samtools SAM fields).-p, --pile-expression <STR>— per-site filter (pileup fields).
Grammar is the samtools filter=STRING expression language (C-style precedence,
&&/||/!, bit fields, tags, regex). See docs/filter_grammar.md.
# keep high-quality, non-5prime, properly paired reads
countmut -i x.bam -r ref.fa -e "mapq >= 20 && bq >= 20 && dist5 >= 2 && flag & PROPER_PAIR"
# restrict to one RG group
countmut -i x.bam -r ref.fa -e "tag('RG') == 'sampleA'"
# report only A-reference sites with depth >= 5 and > 2 G alleles
countmut -i x.bam -r ref.fa -p "ref == 'A' && depth >= 5 && g > 2"
Read variables: mapq, flag (+ flag.dup, flag.unmap, ...), qname, pos,
endpos, pnext, rname, mrname, tlen, qlen, rlen, ncigar, seq,
qual, sclen, hclen, bq, dist5/dist3, strand, [NM]/[RG] tags,
avg(qual), exists([NM]), sqrt(mapq), ...
Site variables: depth, pos, ref, a c g t n, ins, del, ref_skip, fail.
When
-e/-pis given, counting runs on the Python engine (the C core cannot evaluate strings). Without expressions, the fast C backend is used.
Engine selection
--engine {auto|read-walk|pileup} (default auto):
auto→ read-walk formutation(targeted sites), pileup forbase/allele.read-walk/pileup→ force a strategy.
Options
-i/--input, -r/--reference, -o/--output
--mode {mutation,base,allele} --engine {auto,read-walk,pileup}
--region, --threads/-t
--ref-base, --mut-base, --pad, --save-rest
--split-strand, --count-indels, --min-depth, --min-allele-support, --vcf
-e/--expression, -p/--pile-expression
Design
countmut/
cli.py rich CLI (routes to the C core directly)
backend.py builds/loads the C binary and calls it
model.py FilterConfig / MutationConfig / StrandConfig / EngineConfig
core.py the original pure-Python countmut (legacy reference)
backend/
countmut_core.c computation core: read-walk AND pileup engines,
mate-overlap dedup, -e/-p Lua filters (embedded lua5.4)
countmut_expr.c Lua filter-expression evaluator (-e / -p)
countmut_core_main.c CLI wrapper
Makefile builds the `countmut_core` binary (links lua5.4)
Both BAM-walk strategies (--engine read-walk and --engine pileup) are
implemented in C; Python is a thin wrapper and does no counting. -e/-p
filters are embedded-Lua expressions (see docs/filter_grammar.md), evaluated
in C identically by both engines.
Both walks fill the same per-site, per-strand base counts and emit byte-identical
output -- the two strategies are interchangeable, with identical results (and
identical -e/-p filtering) whether you pick read-walk or pileup.
References this tool learns from
- minipileup — pileup walk, filters, allele counting
- perbase / pbr — mate-aware overlap dedup, base counts
- countmut — biological strand, bisulfite NS/Zf/Yf tiers
- mpileup / cpup — base-count output
- samtools
--input-fmt-option filter=STRING— the expression grammar
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 countmut-0.1.4.tar.gz.
File metadata
- Download URL: countmut-0.1.4.tar.gz
- Upload date:
- Size: 38.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
abc896450ba8a71181676b33795c8a2328fe6e0f5487949344dacb05a13a8d6e
|
|
| MD5 |
47c740a6a44f110fd954503bfbffee61
|
|
| BLAKE2b-256 |
26b25476413cadad5c58f69982e204b7218a7496ba7c0d1fcfea1e7b242b1463
|
Provenance
The following attestation bundles were made for countmut-0.1.4.tar.gz:
Publisher:
publish.yml on y9c/countmut
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
countmut-0.1.4.tar.gz -
Subject digest:
abc896450ba8a71181676b33795c8a2328fe6e0f5487949344dacb05a13a8d6e - Sigstore transparency entry: 2649843371
- Sigstore integration time:
-
Permalink:
y9c/countmut@a80fe4d1b75579292f0951aa444ed75cd64a2847 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/y9c
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a80fe4d1b75579292f0951aa444ed75cd64a2847 -
Trigger Event:
push
-
Statement type:
File details
Details for the file countmut-0.1.4-py3-none-any.whl.
File metadata
- Download URL: countmut-0.1.4-py3-none-any.whl
- Upload date:
- Size: 29.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a2f36c53c116de270c3516c87871dc1d71224865150c1620eeb54b1c59b5788
|
|
| MD5 |
04b80c89a415ef57d10e897e1f244a20
|
|
| BLAKE2b-256 |
58a7e7063b4438c4b73166f5e57f494180cef24a171a140d99eba042f49b4655
|
Provenance
The following attestation bundles were made for countmut-0.1.4-py3-none-any.whl:
Publisher:
publish.yml on y9c/countmut
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
countmut-0.1.4-py3-none-any.whl -
Subject digest:
2a2f36c53c116de270c3516c87871dc1d71224865150c1620eeb54b1c59b5788 - Sigstore transparency entry: 2649843406
- Sigstore integration time:
-
Permalink:
y9c/countmut@a80fe4d1b75579292f0951aa444ed75cd64a2847 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/y9c
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a80fe4d1b75579292f0951aa444ed75cd64a2847 -
Trigger Event:
push
-
Statement type: