Expected output

This page describes every file ZipStrain writes, the columns in each, and how to read them. Profile outputs are written as parquet. Convert any parquet table to CSV explicitly with zipstrain utilities parquet-to-csv when text output is needed. The examples below come from Tutorial #3 (three replicates of the ZymoBIOMICS standard, Sylph route).


zipstrain profile

A profile run writes one folder per sample directly under the run directory, plus shared profiling_assets/ (auto-generated inputs + logs):

out_profile/
├── SRR12324251/
│   ├── SRR12324251_profile.parquet        # per-position base counts
│   ├── SRR12324251_genome_stats.parquet   # per-genome summary
│   ├── SRR12324251_gene_stats.parquet     # per-gene summary
│   ├── SRR12324251_SNVs.parquet           # SNPs vs. reference
│   └── intermediate_files/                # scratch — safe to ignore
├── SRR12324252/ …
└── profiling_assets/                      # null model, bed, contract, logs

<sample>_genome_stats.parquet

One row per genome: how well it was covered, whether it is present, and its identity.

Column Meaning
genome Genome identifier (a GTDB accession on the Sylph route)
coverage Mean read depth across the genome (over its full length)
coverage_median, coverage_std Median and std. dev. of depth across covered positions
breadth Fraction of the genome covered by ≥ 1 read
genome_length Genome length (bp)
gap_mean, gap_std Mean / std. dev. of the gap length between consecutive mapped reads
5x_cov_sites Number of positions covered ≥ 5×
heterogeneity Mean per-site nucleotide diversity (within-sample microdiversity)
ber Breadth-error ratio: observed breadth ÷ breadth expected at this coverage (~1 for an evenly-covered genome)
fug Read-distribution uniformity (fraction of non-excess gaps); ~0.632 under a random distribution
reads_mapped Reads mapped to the genome
ref_ani Population ANI of the sample to the reference genome (%) — a position counts as matching if the reference allele is present among the reads
conANI_reference Consensus ANI to the reference (%) — a position counts as matching only if the reference base is the sample's consensus (majority) base. Lower than ref_ani at polymorphic sites. Both are reported only when --reference-fasta is used
SNS_count, SNV_count Number of fixed substitutions (SNS) and polymorphic sites (SNV+con_SNV+pop_SNV) — the per-genome totals of the SNV table. Reference route only
presence Automated present / absent call (see Tutorial #3)
genome_taxonomy GTDB lineage — Sylph route only

Example (one present genome):

genome           coverage  breadth  ber     fug     reads_mapped  ref_ani  presence  genome_taxonomy
GCF_000006945.2  380.34    0.9211   0.9211  0.6467  6579418       99.09    present   d__Bacteria;…;s__Salmonella enterica

Here breadth ≈ 0.92 at 380× coverage and presence = present — a genuine member of the community. Trace genomes appear with breadth < 0.05 and presence = absent.

<sample>_gene_stats.parquet

One row per gene, populated only when gene annotations are supplied (profile --gene-fasta, or a gene FASTA on the Sylph route). Empty otherwise.

Column Meaning
genome Genome the gene belongs to
gene Gene identifier
length Gene length (bp)
breadth Fraction of the gene covered by ≥ 1 read
coverage Mean read depth across the gene
ref_ani ANI of the sample to the reference for this gene (%)

<sample>_profile.parquet

The core output: one row per reference position with the observed base counts. This is what comparisons are computed from. It is large (tens of millions of rows), and profile runs do not write CSV sidecars.

Column Meaning
chrom Scaffold / contig name
genome Genome the scaffold belongs to (NA if unbinned)
gene Gene at this position (NA if none / unannotated)
pos Position on the scaffold (1-based)
A, C, G, T Read counts that pass both the Poisson error ceiling and --min-freq allele-frequency filter
ref_base_bitmask Reference base as a bitmask: A = 1, C = 2, G = 4, T = 8 (0 = reference base unknown). Only present when --reference-fasta was used

Example:

chrom                    genome           gene  pos      A   C   G   T   ref_base_bitmask
GCA_016925055.1__CP0704… GCA_016925055.1  NA    3892095  0   51  0   0   0
GCA_016925055.1__CP0704… GCA_016925055.1  NA    3892097  51  0   0   0   0

The null model stores the largest count still compatible with sequencing error, so an allele survives only when its count is strictly greater than that ceiling. --min-freq is applied against the original A+C+G+T depth before any allele is removed and defaults to 0. The generated model supports coverage through 50,000 by default; profiling raises an error instead of silently altering a position when its depth exceeds the model.

<sample>_SNVs.parquet

Every covered position that is divergent from the reference — either polymorphic or a substitution — classified in the style of inStrain's SNVs.tsv. Skipped without --reference-fasta (or with --no-snvs). Because the profile counts are sequence-error adjusted, a base with count > 0 is a "passing" allele.

Column Meaning
chrom, genome, gene, pos Location (pos is 1-based)
position_coverage Total (error-adjusted) depth at the position
allele_count Number of passing alleles (bases with count > 0)
ref_base, con_base, var_base Reference, consensus (majority), and top variant base
ref_freq, con_freq, var_freq Frequencies of those bases over position_coverage
A, C, G, T Per-base counts
class SNP classification (below)
ref_base_bitmask Reference base bitmask (A=1, C=2, G=4, T=8)

class values (with alleles = passing bases, con = consensus, ref = reference):

  • SNS — one allele, con ≠ ref: a fixed substitution.
  • SNV — ≥2 alleles, con == ref: reference is the majority, with a minor variant.
  • con_SNV — ≥2 alleles, con ≠ ref, reference still present among the alleles.
  • pop_SNV — ≥2 alleles, con ≠ ref, reference absent: population-divergent (these are what pull ref_ani/popANI below 100%).

Monomorphic reference matches are not emitted. The per-genome SNS_count and SNV_count (= SNV+con_SNV+pop_SNV) in genome_stats are the totals of these.

Coordinates are 1-based (comparing with inStrain)

ZipStrain reports pos as 1-based (the samtools/mpileup convention), whereas inStrain's SNVs.tsv uses 0-based position. When intersecting the two tables, add 1 to inStrain's position (or subtract 1 from ZipStrain's pos) before matching, or every position will appear to disagree.


zipstrain compare

Both methods (standard and matrix) write a single comparison table at the top of the run directory: all_comparisons.parquet (+ .csv), or all_gene_comparisons.parquet with --compare-genes.

all_comparisons.parquet

One row per genome, per pair of samples.

Column Meaning
sample_1, sample_2 The two samples being compared
genome Genome compared between them
total_positions Positions covered in both samples (≥ --min-cov) — the basis of the comparison
share_allele_pos Of those, positions that match under the selected ANI method
genome_ani Genome-wide ANI (%) = share_allele_pos / total_positions × 100. The parquet metadata key zipstrain_compare_ani_method records whether this is popani, conani, or cosani_<threshold>
max_consecutive_length Longest run of consecutive shared positions (an identical-by-state / IBS measure)

With gene ranges available, genome-level gene identity summary columns are also added by the default --calculate all: shared_genes_count, identical_gene_count, perc_id_genes.

ANI method is stored in the parquet header

--ani-method conani changes how the ANI match indicator is computed, but it does not add separate consensus-specific output columns to the comparison table. Check zipstrain_compare_ani_method in the parquet metadata to know what genome_ani represents. Matrix mode supports popani only.

Example (same-strain replicates → 100% popANI):

sample_1     sample_2     genome           total_positions  share_allele_pos  genome_ani  max_consecutive_length
SRR12324251  SRR12324252  GCA_031316495.1  3983156          3983156           100.0           768237

Interpreting popANI. genome_ani near 100.0 means the same strain is present in both samples. A common same-strain threshold is ≥ 99.999% — stringent enough to distinguish strains that diverged only a few years apart. Because ZipStrain uses population ANI (a position matches if any read supports the shared base), co-existing minor variants confirm rather than break a match; see Tutorial #3 for the popANI-vs-consensus-ANI discussion.