Output Formats¶
get_MNV can write TSV, VCF, BCF, and JSON metadata files.
Default TSV Output¶
Default file name:
<input_name>.MNV.tsv
With --sample all, one file is written per sample instead, carrying the sample
in its name:
<input_name>.sample_<SAMPLE>.MNV.tsv
Characters that cannot appear in a file name are replaced, so a sample called
sample/1:bad becomes sample_1_bad. Two samples whose names come out the same
after that replacement would write to one file, so the run stops and names them
instead of letting one overwrite the other.
Each file holds the variants that sample carries, not every variant in the
cohort. A record lists every ALT seen at that site across all samples, so the
sample's GT decides which of them belong to it: a genotype of 0/0 carries
none, 1/2 carries both alleles of a multiallelic record, and a genotype that is
absent or a no-call (./.) keeps the allele, since unknown is not absence. A
sample carrying nothing gets a file with only the header. Two adjacent
substitutions merge into an MNV only for the samples that carry both.
Use this format for spreadsheets, downstream parsing, and quick inspection.
Main columns:
| Column | Meaning |
|---|---|
Chromosome |
Contig name |
Gene |
Gene or feature name. Intergenic variants are marked as intergenic. |
Positions |
One position for SNPs, multiple comma-separated positions for MNVs, in ascending genomic order on both strands. The per-SNV columns (Reference Bases, Base Changes, SNP AA Changes, SNP Codon, Event Components, and the read-support columns) are parallel arrays in that same order. |
Reference Bases |
Reference bases at those positions. |
Base Changes |
Alternative bases. |
AA Changes |
Amino acid change after combining all SNVs in the codon. |
SNP AA Changes |
Amino acid change for each SNV considered separately. |
Local AA Changes |
Exon-local numbering, which is what get_MNV reported before 1.1.2. Identical to AA Changes when the feature has no transcript context (a genes TSV, a prokaryotic or single-exon CDS, and the first exon of a spliced model), and different for the later exons of a spliced transcript, where AA Changes counts from the start of the protein and this counts from the start of the exon. |
Local SNP AA Changes |
Per-SNP amino acid changes in local numbering. |
Variant Type |
SNP, MNV, SNP/MNV, or INDEL. |
Change Type |
Synonymous, non-synonymous, stop gained/lost, unknown, etc. |
Reference Codon |
Original codon, in the transcript's own orientation: on a minus-strand gene these are the coding-strand bases, not the genomic ones, so the codon always translates to the amino acid reported beside it. |
SNP Codon |
Codon with individual SNP substitutions, same orientation as Reference Codon. |
MNV Codon |
Codon with all grouped substitutions, same orientation as Reference Codon. |
Event Class |
Canonical allele event class: snp, mnv, insertion, deletion, delins, complex_indel, or symbolic. |
Event Components |
REF/ALT decomposition such as SNV:10:A>G, INS:10:+T, or DEL:11-12:TG. |
SO Term |
Sequence Ontology consequence term (missense_variant, synonymous_variant, stop_gained, start_lost, frameshift_variant, inframe_deletion, intergenic_variant, …). Variants near an internal exon-exon junction of a spliced transcript also carry a splice term: splice_donor_variant / splice_acceptor_variant (the two essential intronic bases at each intron end, HIGH) or splice_region_variant (the exon's first or last 3 bases, or the intron's 3rd-8th bases, LOW). An exonic change near a junction is combined with its coding consequence, e.g. missense_variant&splice_region_variant for a substitution and frameshift_variant&splice_region_variant for an indel, as one row rather than two. A junction means a real intron: CDS segments that abut or overlap (a ribosomal-slippage join such as SARS-CoV-2 ORF1ab) are one continuous reading frame and carry no splice terms. A variant inside an intron but away from its splice sites is intron_variant (MODIFIER), reported against its gene rather than as intergenic, and a variant in a feature declared non-coding is non_coding_transcript_exon_variant (MODIFIER) with no amino-acid change. |
Impact |
Predicted impact following SnpEff/VEP conventions: HIGH, MODERATE, LOW or MODIFIER. A combined splice/coding consequence keeps the more severe impact. |
Grantham |
Grantham distance and conservation category of a missense change (e.g. 177 (radical)); - for synonymous, nonsense or non-coding changes. |
MNV Consequence Shift |
How the combined MNV compares with its individual SNVs: MNV-gained (more severe than any single SNV, which is what per-SNV annotators miss), MNV-masked (a nonsense SNV rescued by its neighbour) or Concordant. - for single SNVs. |
DBS Class |
COSMIC-style doublet base substitution class for an MNV of two adjacent single-base substitutions, e.g. CC>TT (reverse-complement collapsed, so GG>AA reports as CC>TT). - for single SNVs, indels, and non-adjacent or 3-SNV MNVs. |
Declared Phase |
The phase the input VCF declared for this row's alleles, as cis:12345 (verdict and PS phase set) or just cis when the caller phased without a phase set. Read from a |-separated GT; a / genotype is the caller saying it did not resolve the phase, and is reported as -. |contradicted-by-reads is appended when the BAM leaves the claim no room: a declared cis that not one spanning read carries whole, or a declared trans that every informative read carries. This is the caller's claim, not an observation; the phasing columns beside it are the evidence. - for single-position rows and unphased input. |
MNV Phasing Support |
BAM-derived phasing (linkage) support: among the reads that observe every position of the codon and carry the least-supported constituent SNV, the fraction that also carry the full MNV haplotype. 1.0000 = perfect co-occurrence (a genuine haplotype); low values mean the SNVs largely fall on different molecules (a same-codon coincidence, not a real MNV). 0.0000 is a finding: reads did span the codon and none carried both. - means the question could not be answered: no --bam, a single SNV, or no read reaching across the codon (common when a codon straddles an intron and the fragments are shorter than the intron). |
Haplotype LD |
Read-level linkage disequilibrium (D') between the variants this row claims travel together, over the molecules that observed them. It covers both kinds of multi-variant row: a codon MNV and a local indel haplotype. A co-occurrence ratio cannot separate a haplotype from an accident of frequency: two substitutions on 90% of molecules each are found together on 81% of them by arithmetic alone, and the ratio calls that 0.9. D' measures the excess over what the two frequencies predict, normalised by the most it could have been. +1 = they travel together as far as their frequencies allow, so the MNV is one haplotype. ~0 = they co-occur exactly as chance predicts, so they merely share a codon. -1 = they exclude each other: both present, never on one molecule, which in a haploid population is two competing lineages rather than one variant. With three or more variants the weakest pair decides, since the row claims one molecule carries all of them. It answers a different question from the read count beside it: the count is how many molecules are this combination, while D' is whether its variants co-occur more than their own frequencies predict, so a haplotype carried by few molecules can still be perfectly linked and one carried by many can be a coincidence. - when no molecule observed the variants together, or when one of them is on every such molecule or none, which leaves nothing to correlate. Only present with --bam. |
Haplotype LD p |
Two-tailed Fisher exact p-value for that table, so a D' of 1.0 from four molecules is not read as one from four hundred. - under the same conditions as the column above. Only present with --bam. See Linkage. |
MNV Phasing Reads |
How many reads MNV Phasing Support was computed from, so 1.0000 from 3 reads is not read as 1.0000 from 300. - exactly when that column is -, which is not the same condition as the two linkage columns beside it. Only present with --bam. |
Frameshift Phasing |
What the reads said about this codon sharing molecules with each upstream indel, as trans:1234:0/18: the verdict, the indel's position, and the cis reads out of the reads able to answer. Several are joined with |. A codon that is not labelled frameshifted otherwise looks the same whether the reads proved the indel is on other molecules or nobody asked; - is that second case. Only present with --bam. |
NMD Prediction |
Nonsense-mediated decay prediction for a premature stop under the 50-nt rule: NMD-triggering when the PTC is more than 50 nt upstream of the last exon-exon junction, NMD-escaping when it is in the last exon or within 50 nt of that junction. - for variants without a premature stop and for transcripts with no exon-exon junction. Requires a spliced (GFF/GTF transcript) CDS model whose segments are separated by real introns; a single CDS segment, or segments joined by ribosomal slippage, has no junction. |
HGVS g. |
HGVS genomic descriptor: g.100A>G for an SNV, the allele-bracket g.[28G>T;30T>A] for an MNV, and g.101_102del / g.100_101insTG / g.101delinsC for indels. Not 3'-shifted (uses the input allele placement) and carries no reference-accession prefix. |
HGVS c. |
HGVS coding descriptor for a coding substitution, numbered from the CDS start with coding-strand bases: c.30A>G (SNV) or the allele bracket c.[28G>A;30T>C] (MNV). - for indels and non-coding variants; the protein change (p.) in the AA columns conveys indel consequences. |
Extra columns when --bam is used:
| Column | Meaning |
|---|---|
SNP Reads |
Reads carrying each individual SNV without the full MNV haplotype. On a row where every read carries the whole haplotype this is 0 for each constituent and the count lives in MNV Reads, so the two columns partition the support rather than double-count it. |
SNP Forward Reads |
Forward-strand count for the reads above. |
SNP Reverse Reads |
Reverse-strand count for the reads above. |
MNV Reads |
Reads supporting the full MNV haplotype. |
MNV Forward Reads |
Forward-strand MNV support. |
MNV Reverse Reads |
Reverse-strand MNV support. |
Total Reads |
Depth at the variant positions. |
SNP Frequencies |
Per-position SNP frequencies. |
MNV Frequencies |
MNV haplotype frequency. |
Event Reads |
Exact reads supporting an indel/complex event. |
Event Forward Reads |
Forward-strand exact event support. |
Event Reverse Reads |
Reverse-strand exact event support. |
Event Depth |
Reads with an observed allele across the indel/complex event span. |
Event Frequency |
Exact event reads divided by event depth. |
Exact event support is CIGAR-aware. A read must reconstruct the same local ALT sequence and, for complex haplotypes, contain the expected insertion and deletion components. This prevents net-neutral insertion/deletion combinations from being counted as support merely because their sequence looks like an MNV.
Frequency columns are calculated from BAM support. --min-snp-frequency and
--min-mnv-frequency use these same BAM-derived values. The filters are
independent: --min-snp-frequency applies to individual SNP observations, and
--min-mnv-frequency applies to phased MNV haplotypes. In mixed SNP/MNV
calls, a row or VCF record is kept when either component passes its own active
threshold.
Read-count and strand-support filters (--snp, --mnv, --min-snp-strand,
and --min-mnv-strand) follow the same independent SNP/MNV behavior.
When a codon-level MNV overlaps an indel, the MNV row is kept as a positional
context row but its amino-acid effect is marked Unknown with
Change Type = Indel overlap. If BAM reads support the full combined event,
get_MNV emits a separate exact complex_indel row with the combined REF/ALT,
event components, and event read support.
Indel overlap follows VCF interbase semantics. Deletions overlap a feature by their deleted reference span. Insertions overlap a feature only when the inserted sequence falls between two reference bases inside that feature, so an insertion anchored at the final feature base is reported outside that feature.
Example:
Chromosome Gene Positions Base Changes AA Changes Variant Type Change Type
MTB_anc Rv0095c_Rv0095c 104838 T Asp126Glu SNP Non-synonymous
MTB_anc Rv0095c_Rv0095c 104941, 104942 T, G Gly92Gln SNP/MNV Non-synonymous
VCF Output¶
Write VCF with:
--convert
or write both TSV and VCF with:
--both
Default file name:
<input_name>.MNV.vcf
Use --vcf-gz for compressed output:
<input_name>.MNV.vcf.gz
Common INFO fields:
| Field | Meaning |
|---|---|
GENE |
Gene or feature name |
AA |
Amino acid change |
CT |
Change type |
TYPE |
Variant type |
EC |
Canonical allele event class |
COMP |
REF/ALT event components |
ODP |
Original depth from the input variant file |
OFREQ |
Original allele frequency from the input variant file |
SR, SRF, SRR |
SNP reads: total, forward, reverse |
MR, MRF, MRR |
MNV reads: total, forward, reverse |
DP |
Depth recalculated from BAM |
FREQ |
Frequency recalculated from BAM |
ER, ERF, ERR |
Exact indel/complex event reads: total, forward, reverse |
EDP |
Exact event depth for indel/complex alleles |
EFREQ |
Exact event frequency for indel/complex alleles |
SBP |
SNP strand-bias p-value, in scientific notation (1.923e-7), which is the value --min-strand-bias-p compared |
MSBP |
MNV strand-bias p-value, same notation |
SO, IMPACT |
Sequence Ontology consequence term and predicted impact |
GD |
Grantham distance of a missense change |
MNVSHIFT |
Combined MNV consequence vs. its individual SNVs |
DBS |
COSMIC-style doublet class for adjacent 2-SNV MNVs (e.g. CC>TT) |
MNVPS |
MNV phasing support (of the codon-spanning reads carrying the limiting SNV, the fraction carrying the full haplotype) |
MNVPR |
Reads that ratio was computed from |
FSPH |
Read-level phasing with each upstream indel, as verdict:position:cis/informative |
DPHASE |
Phase the input VCF declared for this row, verdict:phase_set |
LD |
Linkage disequilibrium D-prime between the codon's substitutions |
LDP |
Fisher exact p-value for that linkage table |
NMD |
Nonsense-mediated decay prediction for a premature stop (50-nt rule) |
HGVSG |
HGVS genomic descriptor (MNV allele-bracket ; percent-encoded) |
HGVSC |
HGVS coding descriptor for a coding substitution (; percent-encoded) |
The VCF header records the get_MNV version, command line, and thresholds used.
When --emit-filtered is enabled, VCF records below read-support, frequency,
strand-support, or strand-bias thresholds are written with FILTER tags such as
LowSupport, LowFrequency, StrandSupport, or StrandBias; otherwise they
are skipped.
BCF Output¶
Write BCF with:
--bcf
BCF requires VCF output mode, so use it with --convert or --both.
This is output conversion only; BCF is not accepted as an input format.
The conversion is done by bcftools view, taken from PATH. Without bcftools
installed the run warns, writes no BCF, and still exits 0 with its TSV and VCF
intact. --index-vcf-gz works the same way through tabix.
Default file name:
<input_name>.MNV.bcf
JSON Files¶
Three flags write JSON, and every payload carries a schema_version so a
consumer can tell which shape it is reading.
Summary JSON¶
Write with:
--summary-json run.summary.json
Top-level keys:
| Key | Meaning |
|---|---|
schema_version |
Payload version, currently 1.0.0 |
sample |
The sample named with --sample, or null when the flag was not passed. null does not mean no sample was used: a multi-sample VCF still falls back to the first sample column, and its genotype and FORMAT fields still decide the metrics |
dry_run |
Whether --dry-run was in force |
bam_provided |
Whether a BAM was read, which is what decides the read-support fields |
translation_table |
NCBI translation table number used |
inputs |
The input paths: vcf, fasta, annotation, bam, plus checksums |
output_tsv, output_vcf, output_bcf |
The files this run wrote, null for each one it did not |
contigs |
One entry per contig, with its own counts |
timings |
parse_inputs_ms, process_ms, emit_ms, total_ms |
global |
The run-wide counts, below |
Inside global:
| Key | Meaning |
|---|---|
contig_count |
Contigs seen |
snp_records_in_vcf |
Variant entries kept from the input on that contig. Not the file's line count: an entry the targeted sample's genotype does not carry is not counted, so the same file gives a different number under a different --sample |
mapped_genes |
Genes that had at least one variant on them |
produced_variants |
Annotated variants, before the output filters |
snp_variants, mnv_variants, snp_mnv_variants, indel_variants, intergenic_variants |
The same total split by type |
region_cache_hits, region_cache_misses |
BAM region cache, only meaningful with --bam |
These counts are what get_MNV produced, before the output filters
produced_variants and the per-type counts beside it are taken after
annotation and before the read-support, frequency and strand filters that
decide what reaches the TSV. On a filtered run the two disagree on purpose:
the same command can log produced variants=941 and write a one-row TSV,
and the HTML report, which counts rows, will say 1. Read the summary as
what the annotation found and the TSV as what passed.
--sample all writes a different object, not a longer one
Every key above moves. A --sample all summary has mode set to
sample_all, sample_count, sample_names, one aggregate object holding
run-wide totals, and a samples array holding one object per sample. Each
of those, and aggregate too, has the single-sample shape described above,
so data["global"]["produced_variants"] becomes
data["aggregate"]["global"]["produced_variants"].
aggregate sums the counts across samples and names no output file of its
own: aggregate.output_tsv is null, and the per-sample paths are on the
samples entries. Its contigs array is always empty as well, because the
per-contig breakdown belongs to each sample; read it from the samples
entries. Branch on the key that only one shape has:
import json
data = json.load(open("run.summary.json"))
if data.get("mode") == "sample_all":
totals = data["aggregate"]["global"]
per_sample = {s["sample"]: s["global"] for s in data["samples"]}
else:
totals = data["global"]
per_sample = {data["sample"]: data["global"]}
Run Manifest¶
Write with:
--run-manifest run.manifest.json
| Key | Meaning |
|---|---|
schema_version |
Payload version |
tool_version |
The get_MNV version that ran |
command_line |
The command with every path reduced to its file name and the program written as get_mnv, so the manifest can be shared without publishing the directory layout it ran in. It records what was run, not a line that can be pasted back into a shell |
timestamp_unix |
Seconds since the Unix epoch |
summary |
The whole summary payload, unchanged |
output_checksums |
output_tsv_sha256, output_vcf_sha256, output_bcf_sha256, each null for a file this run did not write |
With --sample all the manifest follows the summary: mode, sample_names,
aggregate and samples, with no top-level summary key, and each entry in
samples carries its own output_checksums.
Error JSON¶
Write errors as JSON with:
--error-json run.error.json
Written only when the run fails, which makes its presence the signal:
| Key | Meaning |
|---|---|
schema_version |
Payload version |
code |
The stable error code, such as E002 |
exit_code |
The process exit status, matching the table in Troubleshooting |
message |
The same text the run printed |
{
"schema_version": "1.0.0",
"code": "E002",
"exit_code": 3,
"message": "Cannot open VCF file '/no/such.vcf': No such file or directory (os error 2)"
}
Interactive HTML report¶
--report <FILE.html> writes a single self-contained HTML file for exploring the
called variants. It embeds its data, loads no external scripts or fonts, and
therefore opens offline by double-clicking and can be attached to an email or
archived with the results.
Open the example report: 941
variants called from the single-sample dataset bundled in example/, so it is the
real output of the command below rather than a mock-up. Regenerate it with
scripts/build_example_report.sh. The cohort views (the matrix rows, haplotype
recurrence) fill out with --sample all or --report-from across several samples.
# Report for one run
get_mnv --vcf sample.vcf --fasta ref.fasta --gff ref.gff --report sample.html
# One report covering every sample of a multi-sample VCF
get_mnv --vcf cohort.vcf --fasta ref.fasta --gff ref.gff --sample all --report cohort.html
# Cohort processed one sample per run: aggregate the TSVs afterwards
get_mnv --report-from results/*.MNV.tsv --report cohort.html
With --report-from no pipeline runs: the report is built from get_MNV TSV files
that already exist, which is the usual shape in a Nextflow or Snakemake workflow
that calls get_MNV once per sample. Each file becomes one sample, labelled by its
file name with the .MNV.tsv suffix removed (TB-001.MNV.tsv becomes TB-001).
The report contains:
- A headline count of the variants currently shown, with supporting tiles for samples, genes, MNV rows and high-impact variants, and the consequence distribution by Sequence Ontology term. All follow the active filters.
-
A variant matrix, plotted as a small genome browser: samples down the side, real genomic coordinates across the top, coloured by the alternate base. Scroll to zoom around the cursor, drag to pan, drag on the whole-contig strip to jump, and double-click to reset. Dragging across the whole-contig strip selects a range; a plain click on it recentres. The region box takes
start-end,contig:start-end, a single coordinate, or a gene name, and frames it. Above the ruler sit density lanes: one small chart each for all calls, SNP, MNV, indel, HIGH impact and the number of distinct samples with a call, binned by genomic position. Each lane carries its own scale and its own maximum, so a rare class is readable next to a common one instead of being flattened at the base of a stack, and theTrackscontrol switches between all lanes, a compact set and none. A gene track marks the extent of each gene's called sites (which is where calls were made, not the annotated gene boundary, which the report does not carry). Zooming in far enough shows the base letter inside each cell. Samples can be ordered by shared profile (identical patterns land together), by variant count or by name, and each sample label carries a bar of how many calls it has in the visible window, which is why there is no separate per-sample chart. Colour means one thing only: the nucleotide hues belong to the matrix cells, every magnitude is neutral, and the reserved status colour marks HIGH impact. Positions called together on the same reads (codon MNVs, phased complex indels) are marked with a tick above their columns, and a contig selector appears for multi-contig data, since a continuous coordinate axis cannot span contigs.A cell is only ever "an ALT call" or "not called". get_MNV output cannot distinguish a reference base from a position with no coverage, so a blank cell is never presented as reference. Read that state as "reference or no coverage".
-
Read-backed haplotypes: the allele combinations get_MNV actually observed on the same reads, that is codon-level MNVs and locally phased complex indels, ranked by how many samples carry each one and shown with their phasing support where a BAM was used. Long-range phasing between distant sites is not inferred and is never displayed.
- A sortable, filterable table of every variant, virtualised so tens of thousands of rows stay responsive. Every column carries its own filter: a checkbox list for sample, contig, gene, variant type, consequence and impact (several values at once, with a search box when the list is long), a contains match for position, base change and amino-acid change, and a min/max range for Grantham and frequency. Column filters combine with each other and with the free-text search box, and they drive the whole page: the headline counts, the charts, the matrix and the haplotype panel all follow the same selection.
- A detail panel for the selected variant with its location, consequence,
HGVS
g./c./p.descriptors, Grantham distance, DBS class, NMD prediction, codons, event components and read support. - Export filtered TSV, which downloads exactly the rows currently shown.
- Links to the source repository and the documentation in the masthead. They are ordinary hyperlinks: nothing is fetched when the report is opened, so it stays self-contained offline.
The report follows the operating-system light or dark theme and has its own
toggle. Because it is built from the TSV, --report needs TSV output: it works
with the default output mode and with --both, but not with --convert alone or
with --dry-run. Read-support columns (frequency, depth) are populated only when
the run used --bam.
Size scales with the number of variants. Repeated fields are dictionary-encoded, so a cohort of tens of thousands of variants stays in the low megabytes.
Notes¶
- For MNV records, depth and frequency are calculated from reads spanning all positions in the grouped haplotype.
- Frequencies are printed with 4 decimal places.
--min-snp-frequencyand--min-mnv-frequencyare values from0to1and require--bam.- SNP and MNV frequency filters are independent, so a strong MNV haplotype is not removed by a stricter SNP-frequency threshold.
- SNP and MNV read-support and strand-support filters are also independent.
--sample allwrites one output set per VCF sample, each holding the variants that sample's genotype carries.--keep-original-infopreserves non-get_MNV INFO fields from the input VCF.