Skip to main content

codonyat

codon-yat — A codon-aware amino acid variant typer from SAM alignments of viral NGS data.

A Python package for codon-level amino acid variant calling from SAM alignments of viral amplicon sequencing, including:

  • SAM parsing that honors amplicon labels and strand orientation.
  • CIGAR-aware codon extraction that correctly handles insertions, deletions, and soft clips.
  • Ratio balancing, entropy tracking, and TSV/XML exporters mirroring the legacy Perl outputs.
  • Configurable CLI flags for strand ratio bounds, entropy sensitivity, and target protein.
  • Opt-in research statistics (Wilson CI, strand-bias test, diversity indices) and AAVF v1.0. Research use only.

Highlights

  • Produces consistent TSV/XML diagnostics while exposing Python objects (SamContainer, FullReference, etc.) that downstream tooling can import.
  • Validates every input file before parsing to surface malformed SAM/FASTA/amplicon data early.
  • Logs per-position entropy and strand balance for easier debugging in CI or local runs.
  • Supports any protein target via --protein (one name, a comma-separated list, or all; defaults to RT).

Installation

pip install codonyat

Requires Python 3.10 or newer. The only runtime dependency is Biopython.

CLI usage

Once installed, the codonyat entry point is available:

codonyat sample.sam reference.fasta amplicons.tsv

For multi-record references or complex annotations:

codonyat sample.sam reference.fasta amplicons.tsv --annotation annotations.tsv --protein S

Options

Flag Default Description
--annotation None Optional TSV annotation file (record, protein, description, intervals, strand)
--protein RT Protein(s) to analyse: one name, a comma-separated list (e.g. PR,RT,INT, counted in a single pass) or all (every protein annotated in the reference header or TSV). Names must match the FASTA header annotations
--ratio-upper 3.162 Upper bound for strand-ratio balancing
--ratio-lower 0.316 Lower bound for strand-ratio balancing
--entropy-threshold 0.0 Minimum Shannon entropy to mark a position as diverse
--min-baseq off Count a codon only if all 3 bases have Phred-like quality ≥ this value
--stats off Optional stats TSV (CI, strand-bias, flags, diversity)
--aavf off Optional AAVF v1.0 path (no CAT/SRVL)
--min-freq 0.01 Frequency floor (fraction) for lowfreq / AAVF FILTER
--min-coverage 10 Coverage floor for lowcov / AAVF FILTER
--sb-alpha 0.05 Fisher p-value cutoff for the sb flag
--collapsed-read-counts off Treat a read name ending in _<digits> as a collapsed duplicate count. Off by default: each SAM row counts once

Output

The CLI writes two files alongside the input SAM:

  • [sam-file].tsv — TSV with columns: FILE, REFERENCE, [RECORD], PROTEIN, VARIANT, POSITION, FREQ, FWCOV, RVCOV, TOTALCOV, RATIO
    • The RECORD column is added only for multi-record references (v1.2.0+)
    • Single-record outputs remain byte-identical to v1.1.0
    • Rows are ordered by genomic position (codon index) along the protein's intervals
    • For multi-interval proteins, this follows the biological reading order through all segments
  • [sam-file].xml — per-position <Depth>, <FwCover>, <RvCover>, <Variants>
    • With several proteins, each protein's positions are wrapped in <Protein name="…">; single-protein XML is unchanged

Default TSV/XML are byte-identical to 1.2.1 when the opt-in flags are omitted.

Research statistics (opt-in)

--stats PATH writes a sidecar TSV with LEVEL = variant, codon, or gene:

  • variant: frequency as a fraction, Wilson 95% CI, Fisher strand-bias p, flags (lowfreq, sb, lowcov)
  • codon / gene: Shannon entropy (raw and normalised), Gini–Simpson, nucleotide π, amino-acid π

--aavf PATH writes AAVF v1.0. FILTER is taken from the flags above. The file does not contain resistance or surveillance fields. Formulas and citations: docs/methods.md.

codonyat-runner reads.sam reference.fasta amps.tsv \
  --protein all --stats sample.stats.tsv --aavf sample.aavf

Reference FASTA format

The reference FASTA supports both single-record and multi-record files. Protein annotations can be provided either in the FASTA header or via a separate TSV file.

FASTA header annotation (backward compatible)

Protein annotations in the format Name(Description):start-end, separated by semicolons:

>K03455|HIVHXB2CG PR(Protease):2253-2549;RT(Reverse Transcriptase):2550-3869;INT(Integrase):4230-5093
TGGAAGGGCTAATTCACTCCCAACGAAGAC...

Multi-interval proteins

For proteins spanning multiple intervals (frameshift, splicing, or circular origin-crossing), use comma-separated intervals. All coordinates are 1-based inclusive:

>MN908947.3 ORF1ab(Polyprotein):266-13468,13468-21555
ATTAAAGGTTTATACCTTCCCAGGTAACAA...

The interval 266-13468,13468-21555 represents a -1 programmed ribosomal frameshift where base 13468 is read twice. Semicolons separate different proteins in the header.

TSV annotation format (v1.2.0+)

For complex annotations, use a TSV file with the --annotation flag:

record	protein	description	intervals	strand
MN908947.3	S	Spike glycoprotein	21563-25384	+
MN908947.3	ORF1ab	Polyprotein	266-13468;13468-21555	+
NC_003977.2	P	DNA polymerase	2307-3221;1-1623	+

Important: Minus-strand genes are not supported and will be rejected with a clear error message.

Package API

Import aa_caller to reuse the core objects:

from aa_caller import SamContainer, FullReference, parse_amplicons

Python wrapper

Use call_variants to run variant calling from Python without touching the CLI:

from pathlib import Path
from aa_caller import call_variants

result = call_variants(
    sam_path=Path("reads.sam"),
    reference_path=Path("reference.fasta"),
    amplicons_path=Path("amps.tsv"),
    protein="PR,RT,INT",  # or "all", "RT", or ["PR", "RT"]
    ratio_upper=3.2,
)

print(result.csv_path, result.xml_path)
print(result.container.variants.keys())

If you already have an argparse.Namespace or mapping of the CLI arguments, call_variants_from_args adapts them directly:

from argparse import Namespace

args = Namespace(
    sam_file="reads.sam",
    reference_file="reference.fasta",
    amplicons_file="amps.tsv",
    protein="RT",
    ratio_upper=3.2,
)

call_variants_from_args(args)

CLI wrapper

A lightweight runner at codonyat-runner exposes the same arguments as call_variants_from_args:

codonyat-runner reads.sam reference.fasta amps.tsv --protein PR,RT,INT --ratio-upper 3.1 --csv-path results.tsv

Development

Install the repository with the optional dev tooling so your local environment matches CI:

pip install --upgrade pip
pip install -e .[dev]

Now you can run the same checks as .github/workflows/python-tests.yml:

ruff check .
python -m pytest
python -m build && twine check dist/*

Project structure

codonyat/
├── aa_caller/            # Python package
│   ├── cli.py            # CLI entry point (codonyat)
│   ├── runner.py         # Python API (call_variants, call_variants_from_args) and codonyat-runner
│   ├── container.py      # SamContainer — variant aggregation engine
│   ├── sam.py            # SamEntry — CIGAR-aware SAM record parser
│   ├── reference.py      # FullReference — FASTA + protein annotation loader
│   ├── models.py         # Protein, Amplicon, Variant, Qual dataclasses
│   ├── validators.py     # Input file validation
│   ├── genetic_code.py   # Codon translation table
│   ├── constants.py      # Default thresholds
│   ├── metrics.py        # Wilson CI, Fisher, diversity indices
│   ├── stats_writer.py   # --stats TSV
│   └── aavf.py           # AAVF v1.0 writer
├── tests/                # pytest suite (unit + integration)
├── docs/index.html       # Project page (GitHub Pages)
├── docs/methods.md       # Formulas and citations for 1.3.0 outputs
└── pyproject.toml        # Package metadata

Releasing

Publishing to PyPI is done by .github/workflows/publish-pypi.yml when a GitHub release is published:

  1. Set version in pyproject.toml (e.g. 1.0.3) and merge it to main.
  2. Create a tag v<version> and a GitHub release from it.
  3. The workflow builds the sdist and wheel, runs twine check, checks that the tag matches the package version, and publishes via PyPI trusted publishing (GitHub environment pypi).

License

MIT

Metadata

Release files for codonyat 1.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for codonyat 1.3.1
File Size Uploaded
codonyat-1.3.1.tar.gz 47.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for codonyat 1.3.1
File Interpreter ABI Platform
codonyat-1.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 81.2 kB

Release files / codonyat-1.3.1.tar.gz

Download URL codonyat-1.3.1.tar.gz
Size 47.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d0d562da38c4fbd1cc7d47b6bc43cbcaf9979e9c6f9e8b2e9ea25840c2dd2db4
BLAKE2b-256 checksum
How to use checksums
5d62a89974a4737c52e411a9109c3275d7c54c658d62b5aa479991b3c073edbb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / codonyat-1.3.1-py3-none-any.whl

Download URL codonyat-1.3.1-py3-none-any.whl
Size 34.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
46fae11d72b2e6dfe454a48b96589a5887ff8904f1daf9dad2bddff9c45cd526
BLAKE2b-256 checksum
How to use checksums
e8d772c0d1d9d5f0f2dcc6b31592e955ac293529fd411593aa3aa2f24a1934e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release history Release notifications | RSS feed

This release

1.3.1 This release

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page