Classify sequencing reads by non-human taxonomic content (kraken2), including the allele-based non-human fraction of reads supporting a variant.
Project description
nonhuman-screen
Classify sequencing reads by non-human taxonomic content using kraken2, and reduce the per-read verdicts to a non-human fraction (NHF) plus per-domain breakdowns (bacterial / archaeal / fungal / protist / viral / UniVec-Core).
The headline capability is the allele-based NHF: given a BAM/CRAM and a variant, compute the non-human fraction of the reads that support that variant's ALT allele — in one call, no boilerplate:
from nonhuman_screen import classify_variant_alt_reads
v = classify_variant_alt_reads(
"sample.bam", "kraken2_db", "chr1", 12345, "A", "T", ref_fasta="ref.fa",
)
v.nonhuman_fraction # 0.0–1.0 over ALT-supporting reads
v.fractions.bacterial # per-domain breakdown
v.supporting_reads # denominator
nonhuman-screen is sample-agnostic: it classifies whatever reads you hand
it and never asks whose they are. The same call works on a proband, a parent, a
tumour, or a plain FASTQ — so it can back higher-level analyses (e.g. flagging
inherited variant calls that are actually parental contamination) without
knowing anything about pedigrees.
Extracted from
kmer-denovo-filter, where this methodology was originally developed, so it can be reused independently.
How it works (in one paragraph)
Reads are written to a temporary FASTQ and classified by kraken2's LCA
algorithm. Each read's assigned taxid is mapped to a domain using the
database's nodes.dmp taxonomy. A read counts as non-human only if it is
classified and lies outside the human lineage, outside the human clade, and
outside UniVec-Core. Two conservative guards reduce false positives: a
human-homology guard (any k-mer voting for human, taxid 9606, drops the read
from every non-human numerator — important for integrating viruses such as
HBV/HPV/ERVs) and UniVec-Core exclusion (synthetic vector/adapter sequences,
taxid 81077, are tracked separately and never counted as contamination). Both
mechanisms are defined relative to human, so the kraken2 database must contain
the human genome — PrackenDB does. See
docs/methodology.md.
A failed kraken2 run raises Kraken2Error (CLI: exit 3) instead of returning
nonhuman_fraction = 0.0: for a contamination screen, a broken run that looks
clean is the one result you can't afford. See
methodology.md §7.
Install
pip install nonhuman-screen # core engine (standard library only)
pip install 'nonhuman-screen[bam]' # + BAM/CRAM & allele-based NHF (pysam)
The bam extra is required for the BAM/allele helpers and for the
nonhuman-screen CLI (both classify modes import pysam). The core install
covers only Kraken2Runner.classify_sequences on in-memory reads.
You also need the kraken2 binary on PATH and a kraken2 database — see
docs/database.md.
Usage
Allele-based NHF for many variants (batched — one kraken2 call)
from nonhuman_screen import classify_variants_alt_reads
variants = [("chr1", 12345, "A", "T"), ("chr2", 999, "G", "GAC")] # pos 0-based
for v in classify_variants_alt_reads("sample.bam", "kraken2_db", variants,
ref_fasta="ref.fa"):
print(v.variant_key, v.nonhuman_fraction, v.supporting_reads)
The reads supporting all variants' ALT alleles are gathered and classified in a single kraken2 invocation (its database load dominates runtime), then split back per variant. You never re-implement fetch/allele-filter/classify yourself.
Classify an arbitrary set of reads
from nonhuman_screen import Kraken2Runner
result = Kraken2Runner("kraken2_db").classify_sequences(
{"read1": "ACGT...", "read2": "TTGCA..."}
)
result.nonhuman_fraction # 0.0–1.0
result.fractions() # TaxonomicFractions(nonhuman=…, bacterial=…, …)
result.taxonomy_available # False if nodes.dmp was missing (fractions unreliable)
result.nonhuman_read_names # set of read names
Command line
# Per-variant allele NHF table (headline)
nonhuman-screen classify \
--bam sample.bam --kraken2-db kraken2_db --ref-fasta ref.fa \
--variants candidates.vcf.gz --out-prefix sample_contam
# -> sample_contam.variant_nhf.tsv, sample_contam.summary.json
# Whole-BAM summary
nonhuman-screen classify --bam sample.bam --kraken2-db kraken2_db
See docs/cli.md.
Coordinate convention
All pos arguments in the Python API are 0-based (pysam reference
coordinates); variant keys are "{chrom}:{pos}:{ref}:{alt}". The CLI reads a
VCF and handles the conversion for you.
API summary
| Symbol | Needs [bam] |
Purpose |
|---|---|---|
Kraken2Runner.classify_sequences(seqs) |
no | Classify {name: seq} → ClassificationResult |
ClassificationResult |
no | Per-domain read-name sets, counts, fractions(), nonhuman_fraction, taxonomy_available, classification_failed |
Kraken2Error |
no | Raised when a kraken2 run did not produce a trustworthy tally (opt out with strict=False) |
TaxonomicFractions |
no | Per-domain fractions; from_result / over_reads |
read_supports_alt(read, variant_pos, ref, alt) |
no | Does an aligned read carry the ALT allele? |
parse_kmer_votes(kmer_string) |
no | Parse kraken2 per-read k-mer detail into taxid votes |
VariantNHF |
no | Result of the allele-based helpers: variant_key, nonhuman_fraction, supporting_reads, fractions, to_dict() (pos 0-based) |
classify_variant_alt_reads(...) |
yes | Allele-based NHF for one variant → VariantNHF |
classify_variants_alt_reads(...) |
yes | Batched allele-based NHF for many variants |
classify_reads_from_bam(...) |
yes | Classify reads by name and/or locus |
reads_supporting_alt(...) |
yes | Read names supporting an ALT allele |
Docker
The included Dockerfile installs the pinned kraken2 (v2.17.1) and
the package with the [bam] extra:
docker build -t nonhuman-screen .
docker run --rm -v "$PWD:/data" nonhuman-screen \
classify --bam /data/sample.bam --kraken2-db /data/kraken2_db \
--variants /data/calls.vcf.gz --out-prefix /data/contam
The image entrypoint is nonhuman-screen; mount your BAM and kraken2 database in.
You still supply the kraken2 database yourself (see docs/database.md).
Testing
pip install -e '.[bam,dev]'
pytest
Unit tests need nothing extra. The integration and functional tests additionally
need the kraken2 binary and self-skip without it. The functional tests run
the CLI end-to-end against committed Streptococcus controls — a strep-injected
BAM (positive), clean human BAMs (negative), and a broken database (which must
exit 3, not report 0.0). Their rationale, how the fixtures are built, what they
assert, and what they do not establish are documented in
docs/testing.md.
Releasing
Releases are published to PyPI
automatically by GitHub Actions using
Trusted Publishing (OIDC — no API
tokens), via .github/workflows/release.yml:
- Continuous builds — every push to
mainpublishes<major>.<minor>.<run-number>(the<major>.<minor>base is read frompyproject.toml), so the latest code is always installable from PyPI. - Tagged releases — pushing a
vX.Y.Ztag publishes exactly that version:git tag v0.2.0 git push origin v0.2.0
- Bumping the minor/major — edit
versioninpyproject.tomlonmain; continuous builds then track the new base.
To switch to deliberate, semver-only releases, remove main from the workflow's
push.branches trigger (leaving only tags).
License
MIT — see LICENSE.
Project details
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 nonhuman_screen-0.1.7.tar.gz.
File metadata
- Download URL: nonhuman_screen-0.1.7.tar.gz
- Upload date:
- Size: 45.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a96c90fca676feb1c3b980fc252d556c1727177ad42ab8d5b1dd50c96d6f736c
|
|
| MD5 |
977fded970d65879be09199bb291273e
|
|
| BLAKE2b-256 |
85de15b7404e271c302fc5b06903406987b018cf7b82b8bf07820ade060b0f25
|
Provenance
The following attestation bundles were made for nonhuman_screen-0.1.7.tar.gz:
Publisher:
release.yml on jlanej/nonhuman-screen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nonhuman_screen-0.1.7.tar.gz -
Subject digest:
a96c90fca676feb1c3b980fc252d556c1727177ad42ab8d5b1dd50c96d6f736c - Sigstore transparency entry: 2249729027
- Sigstore integration time:
-
Permalink:
jlanej/nonhuman-screen@bbe5b70fa32a94603ac4e9351a56b6426da77aad -
Branch / Tag:
refs/heads/main - Owner: https://github.com/jlanej
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bbe5b70fa32a94603ac4e9351a56b6426da77aad -
Trigger Event:
push
-
Statement type:
File details
Details for the file nonhuman_screen-0.1.7-py3-none-any.whl.
File metadata
- Download URL: nonhuman_screen-0.1.7-py3-none-any.whl
- Upload date:
- Size: 27.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bdc0d024bb8f6899fe62a1b82d678b8a1eedef7ede7b4456a2c48502d20f99c
|
|
| MD5 |
37f59406629cdb166715ea74413b8aa8
|
|
| BLAKE2b-256 |
13dd6a68f28840e29efe472867be40b174a064c127851d3716371b4373e1d145
|
Provenance
The following attestation bundles were made for nonhuman_screen-0.1.7-py3-none-any.whl:
Publisher:
release.yml on jlanej/nonhuman-screen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nonhuman_screen-0.1.7-py3-none-any.whl -
Subject digest:
4bdc0d024bb8f6899fe62a1b82d678b8a1eedef7ede7b4456a2c48502d20f99c - Sigstore transparency entry: 2249729180
- Sigstore integration time:
-
Permalink:
jlanej/nonhuman-screen@bbe5b70fa32a94603ac4e9351a56b6426da77aad -
Branch / Tag:
refs/heads/main - Owner: https://github.com/jlanej
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bbe5b70fa32a94603ac4e9351a56b6426da77aad -
Trigger Event:
push
-
Statement type: