Skip to main content

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

PyPI Python versions CI License: MIT

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 main publishes <major>.<minor>.<run-number> (the <major>.<minor> base is read from pyproject.toml), so the latest code is always installable from PyPI.
  • Tagged releases — pushing a vX.Y.Z tag publishes exactly that version:
    git tag v0.2.0
    git push origin v0.2.0
    
  • Bumping the minor/major — edit version in pyproject.toml on main; 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

nonhuman_screen-0.1.7.tar.gz (45.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nonhuman_screen-0.1.7-py3-none-any.whl (27.9 kB view details)

Uploaded Python 3

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

Hashes for nonhuman_screen-0.1.7.tar.gz
Algorithm Hash digest
SHA256 a96c90fca676feb1c3b980fc252d556c1727177ad42ab8d5b1dd50c96d6f736c
MD5 977fded970d65879be09199bb291273e
BLAKE2b-256 85de15b7404e271c302fc5b06903406987b018cf7b82b8bf07820ade060b0f25

See more details on using hashes here.

Provenance

The following attestation bundles were made for nonhuman_screen-0.1.7.tar.gz:

Publisher: release.yml on jlanej/nonhuman-screen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for nonhuman_screen-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 4bdc0d024bb8f6899fe62a1b82d678b8a1eedef7ede7b4456a2c48502d20f99c
MD5 37f59406629cdb166715ea74413b8aa8
BLAKE2b-256 13dd6a68f28840e29efe472867be40b174a064c127851d3716371b4373e1d145

See more details on using hashes here.

Provenance

The following attestation bundles were made for nonhuman_screen-0.1.7-py3-none-any.whl:

Publisher: release.yml on jlanej/nonhuman-screen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page