This pipeline calculates synonymous and non-synonymous mutation rates across influenza virus sequences by analyzing phylogenetic trees and sequence data. It processes multiple influenza subtypes (e.g., H1, H3, N1, N2), genome segments (e.g., HA, NA, PB2), and tree subsets (host groups, geographic regions) as specified in the configuration.
flu-mut-rates/
├── Snakefile # Main workflow definition
├── config.yaml # Configuration file
├── scripts/
│ ├── make_coding_sites.py # Generate coding sites file
│ ├── make_count_dfs.py # Count mutations along phylogenetic trees
│ ├── align_proteins.py # Align proteins across subtypes
│ ├── rates_model.py # Fit neutral models to mutation rates
│ ├── augment_expected_rates.py # Add CG/GC to expected rates table
│ ├── ExpectedCalc.py # Calculate expected mutation counts
│ └── render_fitness_heatmaps.py # Export single-panel heatmap PNGs for figures
├── notebooks/
│ ├── compute_rates.ipynb # Calculate mutation rates
│ ├── analyze_genome_wide_rates.ipynb # Visualize and analyze rates
│ ├── analyze_site_specific_rates.ipynb # Site-specific rate analysis with SHAPE-MaP
│ ├── compute_fitness_effects.ipynb # Compute per-mutation fitness effects
│ ├── process_shapemap_data.ipynb # Process SHAPE-MaP reactivity data
│ ├── process_dms_data_yu_ha.ipynb # Process Yu et al. HA DMS data
│ ├── process_dms_data_bloom_np.ipynb # Process Bloom et al. NP DMS data
│ ├── process_dms_data_soh_pb2.ipynb # Process Soh et al. PB2 DMS data
│ ├── process_dms_data_wang_na.ipynb # Process Wang et al. NA DMS data
│ ├── process_dms_data_li_pb1.ipynb # Process Li et al. PB1 DMS data
│ ├── process_dms_data_hom_m1.ipynb # Process Hom et al. M1 DMS data
│ ├── process_dms_data_teo_nep.ipynb # Process Teo et al. NEP DMS data
│ ├── analyze_fitness_effects.ipynb # Compare fitness effects to DMS data
│ ├── summarize_filter_logs.ipynb # Summarize mutation filter statistics
│ ├── compute_subset_rates.ipynb # Compute rates for subset trees (host, geographic, split-half)
│ ├── compute_subset_fitness_effects.ipynb # Compute AA fitness effects per subset
│ ├── analyze_subset_fitness_effects.ipynb # Compare fitness effects between subsets
│ └── compose_figures.ipynb # Assemble multi-panel figures from individual PNGs
├── logs/ # Log files for pipeline runs
├── data/ # Input data (organized by segment, subtype, and host)
│ ├── packaging_signal_boundaries.csv # Packaging signal boundaries per segment (from Li et al. 2021)
│ ├── splice_site_boundaries.csv # Canonical M2 and NEP splice-site coordinates (from Lamb & Lai 1980)
│ ├── alternative_orf_boundaries.csv # Alternative-frame ORFs that overlap the main CDS (PA-X, Jagger et al. 2012)
│ ├── HA/
│ │ ├── H1/
│ │ │ ├── final_tree_root.fasta # Origin sequence of final_tree.pb.gz
│ │ │ ├── curated_reference.gff
│ │ │ ├── curated_reference.gtf
│ │ │ ├── final_tree.pb.gz # Global tree (all hosts)
│ │ │ ├── host_ancestral/
│ │ │ │ └── combined_ancestral_states.tab # Per-node host assignments
│ │ │ └── geographic_trees/
│ │ │ ├── north_america_tree.pb.gz
│ │ │ ├── europe_tree.pb.gz
│ │ │ └── asia_tree.pb.gz
│ │ └── H3/
│ │ └── ...
│ └── NA/
│ └── ...
└── results/ # Output directory
Edit config.yaml to specify:
- HA and NA subtypes to analyze
- Genome segments to analyze
- Host groups to analyze
- Geographic groups to analyze
- Data directory locations
Example configuration:
ha_subtypes:
- "H1"
- "H3"
- "H5"
na_subtypes:
- "N1"
- "N2"
segments:
- "HA"
- "NA"
- "PB2"
# ... other segments
host_groups:
- "human"
- "avian"
geographic_groups:
- "north_america"
- "europe"
- "asia"
# Random branch-partition robustness check
split_half_groups:
- "split_a"
- "split_b"
split_half_seed: 42
data_dir: "../flu-usher/results"
output_dir: "results"Unit tests for scripts/ run with pytest from the repository root:
pytest scripts/scripts/test_expected_calc.py covers the guards that check a tree's mutations against
the origin sequence it was handed (see check_tree_origin and parse_haplotype_muts in
scripts/ExpectedCalc.py). Passing the wrong fasta — for example curated_root.fasta,
which is the reroot target's sequence rather than the tree root's — silently corrupts
every reconstructed ancestral sequence, so both guards raise instead.
Three CSV tables in data/ define regions of the genome that are under additional non-neutral constraint on otherwise-synonymous mutations. They are consumed by notebooks/compute_rates.ipynb (to build the exclude_from_mut_rate_analysis flag used during rate aggregation and neutral-model fitting; see Step 4) and by notebooks/analyze_fitness_effects.ipynb (to overlay the affected regions on per-site fitness-effect plots; see Step 12).
data/packaging_signal_boundaries.csv — length of each segment's packaging signal at either vRNA terminus.
- Columns:
segment,end(3prime_vRNAor5prime_vRNA),nt(packaging-signal length in nucleotides),reference(literature source). - Interpretation: site coordinates in the pipeline are CDS-based and 1-indexed, running 5′→3′ along the positive-sense mRNA. The
3prime_vRNAend corresponds to the CDS start, so a valuen3flags CDS sites[1, n3]. The5prime_vRNAend corresponds to the CDS end, so a valuen5flags CDS sites[cds_len − n5 + 1, cds_len], wherecds_lenis the segment's CDS length (available fromresults/{segment}/{subtype}/coding_sites.csv). Anntvalue of 0 means no packaging signal is annotated at that terminus (e.g. PB2 at the 3′ vRNA end).
data/splice_site_boundaries.csv — canonical M2 (MP segment) and NEP (NS segment) splice junctions.
- Columns:
segment,product(M2orNEP),feature(5prime_splice_siteor3prime_splice_site),position(half-integer CDS coordinate),reference. - Interpretation: positions are half-integers (e.g.
26.5) because splicing cuts between two adjacent nucleotides rather than at a single site; the half-integer places the junction between sitesfloor(position)andceil(position). Rate aggregation (Step 4) flags a 10-nt window|site − position| ≤ 5around each junction (e.g. position26.5→ sites 22–31); the fitness-effect overlays (Step 12) draw a vertical marker at the half-integer coordinate so the marker sits between the two flanking sites.
data/alternative_orf_boundaries.csv — alternative-frame ORFs that overlap a segment's main CDS, where mutations that look synonymous in the main CDS may be non-synonymous in the overlapping ORF. Currently lists PA-X on the PA segment.
- Columns:
segment,product(e.g.PA-X),start,end(1-indexed, inclusive nucleotide positions in the segment CDS, same coordinate system ascounts_df['site']),reference. - Interpretation: rate aggregation (Step 4) flags every site
swithstart ≤ s ≤ end. PA-X is a +1-frame ribosomal-frameshift product of PA: the first 570 nt are shared with PA in-frame, and the unique X portion spans nt 572–760. Only the unique frameshifted portion is listed here.
For each segment and subtype combination, the pipeline:
- Reads the tree's origin sequence (
final_tree_root.fasta) and gene annotation (GFF) - Identifies coding and non-coding regions
- Maps each nucleotide site to its codon position (1, 2, 3 or "noncoding")
- Generates a CSV file with site-specific information including gene, codon site, and codon position
Note: Coding sites are shared across all host groups for a given segment/subtype combination.
The pipeline performs two types of mutation counting:
For each segment and subtype combination:
- Takes the coding sites file from Step 1
- Reads the global phylogenetic tree (
final_tree.pb.gz), its origin sequence (final_tree_root.fasta), and GTF annotation - Traverses the tree to count mutations at each branch across all hosts
- Classifies mutations as synonymous or non-synonymous
- In the same traversal, randomly partitions every passing branch into
split_aorsplit_bby a coin flip seeded withconfig["split_half_seed"](a robustness control: see Step 16) - Outputs four files at the segment/subtype level:
mutation_counts.csv- Summary of mutations by site and typeparent_child_pairs.csv- Detailed mutation records for each branchsplit_a/mutation_counts.csv- Mutation counts restricted to a random half of branchessplit_b/mutation_counts.csv- Mutation counts on the complementary half
Rather than counting mutations on separate per-host trees, host stratification is now done on the global tree using ancestral host-state reconstructions. For each segment and subtype:
- Takes the coding sites file from Step 1
- Reads the global tree (
final_tree.pb.gz), its origin sequence (final_tree_root.fasta), GTF annotation, and a host-state TSV (host_ancestral/combined_ancestral_states.tab) that maps every node — tips and internal nodes alike — to a host group via thenodeandhost_groupcolumns - Traverses the global tree and counts a branch only when its parent and child carry the same unambiguous host. Branches are skipped when either endpoint is ambiguous (a node assigned more than one host state), and the output is restricted to the host labels listed in
config["host_groups"]. This keeps each counted mutation attributable to a single, confidently inferred host lineage. - Classifies mutations as synonymous or non-synonymous
- Outputs a single file pair per segment/subtype (not one per host), each carrying a
hostcolumn that records the host group of the branch:host_stratified/mutation_counts.csv- Mutation counts by site and type, with ahostcolumnhost_stratified/parent_child_pairs.csv- Detailed mutation records for each branch, with ahostcolumn
For each segment, subtype, and geographic group combination:
- Takes the coding sites file from Step 1
- Reads the geographic phylogenetic tree (
geographic_trees/{geo}_tree.pb.gz), its origin sequence (final_tree_root.fasta, inherited from the global tree since the geographic trees are extracted after rerooting), and GTF annotation - Traverses the tree to count mutations at each branch for that geographic region
- Classifies mutations as synonymous or non-synonymous
- Outputs two files per geographic group:
mutation_counts.csv- Summary of mutations by site and typeparent_child_pairs.csv- Detailed mutation records for each branch
For HA and NA segments only:
- Aligns protein sequences across all subtypes for cross-subtype comparisons
- Uses MUSCLE for multiple sequence alignment
- Outputs aligned protein files to
results/aligned_proteins/{segment}/
Aggregates all mutation count data and calculates rates:
- Combines mutation counts from all segments, subtypes, and hosts
- Computes genome-wide mutation rates by mutation type and class (synonymous, nonsynonymous, nonsense)
- Calculates segment-wide rates for each segment
- Computes motif-level rates (3-mer sequence context)
- Determines evolutionary opportunity thresholds
- Calculates site-specific mutation rates
Outputs six CSV files at the results root:
counts.csv- Aggregated mutation countsgenome_wide_rates.csv- Genome-wide mutation ratessegment_wide_rates.csv- Segment-specific mutation ratesmotif_level_genome_wide_rates.csv- Context-dependent ratesevo_opp_thresholds.csv- Evolutionary opportunity filterssite_specific_substitution_rates.csv- Per-site mutation rates
Executes an analysis notebook that:
- Visualizes synonymous mutation rates across mutation types
- Compares rates between hosts (human vs avian)
- Analyzes nonsynonymous and nonsense mutation rates relative to synonymous rates
- Compares influenza rates with SARS-CoV-2 and HIV
- Examines symmetry between complement mutation types
Fits log-linear models to predict synonymous mutation rates:
- Base model: No context factors (mutation type only)
- Local context model: Includes 3-mer sequence motif
- Full model: Includes both local context (motif) and global context (segment)
Each model outputs:
expected_rates_by_predictor.csv- Predicted rates for each combination of factorsmodel_performance.csv- Model fit statistics (MSE, variance, R²)
Note: CG and GC mutation types are excluded from model fitting due to insufficient data.
Creates a complete expected rates table:
- Loads fitted rates from the full model (local + global context)
- Adds missing CG and GC mutation types using empirical rates: motif-specific genome-wide rates are used where available (12 of 16 motifs per type), with segment-wide rates as a fallback for the remaining motifs
- Generates all 16 possible 3-mer motifs for CG and GC
- Validates that all 12 mutation types have balanced entry counts
- Outputs
expected_rates.csvwith complete coverage of all mutation types
Computes fitness effects of nucleotide and amino acid mutations by comparing observed mutation counts to neutral model expectations:
- Joins
counts.csvwithexpected_rates.csvto compute expected counts per site under the neutral model - Aggregates counts by nucleotide mutation to compute per-nucleotide-mutation fitness effects as log((actual_count + 0.5) / (expected_count + 0.5))
- Computes per-site synonymous fitness effects as the mean of per-nucleotide-mutation
delta_fitnessvalues across synonymous mutations at each site - Aggregates all nucleotide mutations by resulting amino acid change to compute per-amino-acid-mutation fitness effects as log((actual_count + 0.5) / (expected_count + 0.5))
Outputs three CSV files at the results root:
nt_fitness_effects.csv- Per-nucleotide-mutation fitness effectssitewise_synonymous_fitness_effects.csv- Per-site synonymous fitness effects (mean of per-nucleotide-mutation effects)aa_fitness_effects.csv- Per-amino-acid-mutation fitness effects
Processes SHAPE-MaP RNA secondary structure reactivity data from Dadonaite et al. 2019:
- Reads per-nucleotide SHAPE reactivity values from the source Excel file
- Aligns each segment's reactivity profile to the pipeline reference sequences using MUSCLE
- Outputs
results/shapemap/all_data.csv— SHAPE reactivities mapped to reference site coordinates for all segments (HA and NA excluded)
Processes raw deep mutational scanning (DMS) data from external sources into standardized formats for comparison with fitness effects. Each experiment is handled by a separate notebook:
process_dms_data_yu_ha.ipynb (Yu et al., HA):
- Aligns the DMS experiment HA sequence to the H3 tree reference sequence using MUSCLE to establish site numbering correspondence
- Merges DMS phenotype measurements with the numbering map
- Outputs
results/dms_data/Yu_HA/processed_dms_data.csv— processed HA DMS data with tree reference site numbering
process_dms_data_bloom_np.ipynb (Bloom et al., NP):
- Verifies the DMS sequence matches the NP tree reference (Aichi 1968)
- Computes log-ratio mutation effects (log(preference / wt_preference))
- Outputs
results/dms_data/Bloom_NP/processed_dms_data.csv— NP DMS data with log-ratio fitness effects
process_dms_data_soh_pb2.ipynb (Soh et al., PB2):
- Aligns the DMS sequence to the PB2 reference using MUSCLE
- Verifies the alignment has no gaps and reports percent identity (QC only; raw data is used directly by Step 11)
process_dms_data_wang_na.ipynb (Wang et al., NA):
- Aligns the DMS NA sequence to the N1 tree reference using MUSCLE to establish site numbering correspondence
- Merges fitness measurements with the numbering map
- Outputs
results/dms_data/Wang_NA/processed_dms_data.csv— NA DMS data with tree reference site numbering
process_dms_data_li_pb1.ipynb (Li et al., PB1):
- Aligns the DMS PB1 sequence to the tree reference using MUSCLE to establish site numbering correspondence
- Merges fitness measurements with the numbering map
- Outputs
results/dms_data/Li_PB1/processed_dms_data.csv— PB1 DMS data with tree reference site numbering
process_dms_data_hom_m1.ipynb (Hom et al., M1):
- Aligns the DMS M1 sequence to the tree reference using MUSCLE to establish site numbering correspondence
- Converts amino acid preferences to log-ratio fitness effects
- Outputs
results/dms_data/Hom_M1/processed_dms_data.csv— M1 DMS data with tree reference site numbering
process_dms_data_teo_nep.ipynb (Teo et al., NEP):
- Reconstructs the NEP reference protein from the NS segment reference by concatenating exon 1 (nt 1–30) and exon 2 (nt 503–838)
- Aligns the DMS NEP sequence (PR8 strain) to the reference using MUSCLE; uses actual DMS site numbers for coordinate mapping to handle non-consecutive site numbering
- Outputs
results/dms_data/Teo_NEP/processed_dms_data.csv— NEP DMS data with tree reference site numbering
process_dms_data_chen_pa.ipynb (Chen et al., PA):
- Parses the AA mutation column to extract wildtype AA, DMS site, and mutant AA; excludes indel and stop codon mutations
- Averages the two no-drug fitness replicates (P1NO-1-fit, P1NO-2-fit) and groups by AA mutation (multiple nucleotide changes can produce the same AA change)
- Computes log-scale DMS effects:
log(mean fitness without drug) - Aligns the DMS sequence (first 240 AA of PA) to the PA tree reference using MUSCLE to establish site numbering correspondence
- Outputs
results/dms_data/Chen_PA/processed_dms_data.csv— PA DMS data with tree reference site numbering
Executes an analysis notebook that:
- Visualizes per-site synonymous mutation rates across the genome
- Examines the relationship between local sequence context (motif) and site-specific rates
- Compares model performance (base, local context, full context) across segments
- Integrates SHAPE-MaP RNA structure data to examine the relationship between secondary structure and mutation rates
Executes an analysis notebook that:
- Plots distributions of fitness effects by mutation class across all genes
- Examines per-site synonymous fitness effects across the genome, with overlays highlighting regions under non-coding selection: packaging signals at vRNA termini (from
data/packaging_signal_boundaries.csv), alternative-frame ORFs that overlap the main CDS such as PA-X on segment PA (fromdata/alternative_orf_boundaries.csv), and canonical splice sites for M2 (MP) and NEP (NS) (fromdata/splice_site_boundaries.csv). Splice-site positions are stored as half-integer midpoints (e.g. 26.5) because splice events cut between two adjacent nucleotides rather than at a single site; the half-integer places the rendered vertical marker between the flanking sites on the figure - Compares evolutionary fitness effects to experimentally measured DMS effects for seven proteins: HA/H3 (Yu et al. 2025), NP (Bloom et al. 2014), M1 (Hom et al. 2019), NEP (Teo et al. 2024), PB1 (Li et al. 2023), PB2 (Soh et al. 2019), and NA/N1 (Wang et al. 2023)
Executes a diagnostic notebook that parses the log files produced by the mutation-counting rules and consolidates filter statistics across all counting passes. For each pass (global tree and host-stratified global tree), the notebook reports:
- Total nodes and internal nodes
- Branch counts: total examined, passing, and filtered
- Mutation counts: total, passing, and filtered (broken down by reason: too many mutations, zero mutations, duplicate codon targets)
The notebook produces three figures:
- Branch filter summary per tree: passing branch counts (log scale) and % passing, grouped by host
- Mutation filter summary per tree: passing mutation counts (log scale) and % passing, grouped by host
- Stacked bar breakdown of % mutations by filter reason, faceted by host
Aggregates mutation counts from all subsets (host-stratified, geographic, split-half):
- Reads mutation counts from each subset directory
- Labels each row with
subset(group name) andsubset_type(host/geographic/split_half) - Computes evolutionary opportunity and mutation rates
- Outputs
results/subset_counts.csv
Computes amino-acid-level fitness effects for each subset:
- Reads
subset_counts.csvandexpected_rates.csv(from the global neutral model) - Merges to compute expected counts per mutation under the neutral model
- Filters to mutations with at least 10 actual or expected counts
- Groups by amino acid mutation and computes fitness effects as log((actual + 0.5) / (expected + 0.5))
- Outputs
results/subset_aa_fitness_effects.csv
Compares fitness effects between subsets:
- For each pair of subsets within the same type (e.g., human vs avian, north_america vs europe, split_a vs split_b):
- Matches nonsynonymous amino acid mutations present in both subsets
- Creates scatter plots of fitness effects with Pearson correlation
- Filters to mutations meeting the count threshold in both subsets
The split_a vs split_b comparison is a noise-floor control: both halves are random samples from the same global tree, so their Pearson R per protein represents the maximum agreement we could expect from any half-data comparison. Lower agreement between, say, human vs avian is then interpretable as biological signal above this floor rather than data-amount noise.
Execute the full pipeline with:
snakemake --cores <N>Or run specific targets:
# Process global tree for a specific segment/subtype
snakemake --cores 8 results/HA/H1/mutation_counts.csv results/HA/H1/parent_child_pairs.csv
# Process host-stratified counts for a specific segment/subtype (single file with a host column)
snakemake --cores 8 results/HA/H1/host_stratified/mutation_counts.csv
# Process both global and host-stratified counts for a segment/subtype
snakemake --cores 8 results/HA/H1/mutation_counts.csv results/HA/H1/host_stratified/mutation_counts.csv
# Process a geographic subset tree
snakemake --cores 8 results/HA/H3/north_america/mutation_counts.csv
# Run subset fitness effect analysis
snakemake --cores 8 results/subset_aa_fitness_effects.csvThe pipeline generates the following output files:
-
Coding Sites File:
results/{segment}/{subtype}/coding_sites.csv- Maps each nucleotide position to its coding properties
- Shared across all analyses for a given segment/subtype combination
- Columns:
site— nucleotide position in the reference sequence (0-indexed)codon_position— position within the codon (1, 2, 3, or"noncoding")codon_site— index of the codon within the genegene— gene name the site belongs to (or"noncoding")
-
Global Tree Outputs:
results/{segment}/{subtype}/mutation_counts.csv- Aggregated mutation counts across all hosts- Columns:
site— nucleotide position in the reference sequence (1-indexed)nt_mut— nucleotide mutation string (e.g."A1C": wildtype + position + mutant)wt_nt— wildtype nucleotidemut_nt— mutant nucleotidegene— gene containing the sitecodon_position— position within the codon (1, 2, or 3)codon_site— index of the codon within the gene (1-indexed)wt_codon— wildtype codon sequencemut_codon— mutant codon sequencewt_aa— wildtype amino acid (single-letter code)mut_aa— mutant amino acid (single-letter code)aa_mut— amino acid mutation string (e.g."M1L": wildtype AA + codon site + mutant AA)parent_motif— 3-mer sequence context of the parent node at the mutated siteactual_count— number of times this mutation was observed across tree branchesbranch_length— total branch length over which the mutation opportunity was countedmut_class— mutation class ("synonymous","nonsynonymous","nonsense", or"noncoding")mut_type— two-character mutation type (e.g."AC"for A→C)
- Columns:
parent_child_pairs.csv- Detailed branch-level mutation information- Columns:
parent_name— identifier of the parent node in the phylogenetic treechild_name— identifier of the child node in the phylogenetic treeparent— full nucleotide sequence reconstructed at the parent nodechild— full nucleotide sequence reconstructed at the child nodebranch_length— branch length between parent and child nodes
- Columns:
-
Host-Stratified Outputs:
results/{segment}/{subtype}/host_stratified/mutation_counts.csv- Mutation counts from the global tree, restricted to branches whose parent and child share the same unambiguous host (same columns as globalmutation_counts.csv, plus ahostcolumn giving the branch's host group)parent_child_pairs.csv- Detailed branch-level mutation information (same columns as globalparent_child_pairs.csv, plus ahostcolumn)
3b. Geographic Tree Outputs: results/{segment}/{subtype}/{geo}/
mutation_counts.csv- Aggregated mutation counts for the specific geographic region (same columns as globalmutation_counts.csv)parent_child_pairs.csv- Detailed branch-level mutation information (same columns as globalparent_child_pairs.csv)
- Aligned Proteins (HA and NA only):
results/aligned_proteins/{segment}/- Cross-subtype protein alignments
Located in the results/ root directory:
-
Mutation Rate Files:
counts.csv- Combined mutation counts from all segments and subtypes- Columns: same as
mutation_counts.csvexceptparent_motifis renamed tomotif, plus:motif— 3-mer sequence context of the mutation (centered on the mutated site)subtype— influenza subtype (e.g."H1","N2", or"all")segment— genome segment (e.g."HA","PB2")segment_subtype— combined segment and subtype label (e.g."HA_H1")segment_length— length of the genome segment in nucleotideshost— host group ("human","avian", or"all")evo_opp— evolutionary opportunityrate— per-site mutation rate
- Columns: same as
genome_wide_rates.csv- Mutation rates by type and class (synonymous, nonsynonymous, nonsense)- Columns:
mut_type— two-character mutation type (e.g."AC"for A→C)mut_class— mutation class ("synonymous","nonsynonymous", or"nonsense")host— host group ("human","avian", or"all")actual_count— total number of observed mutations of this type/class/hostevo_opp— evolutionary opportunityrate— mutation ratesyn_rate— synonymous mutation rate for the same host and mutation typerel_rate— rate relative to the synonymous rate (rate / syn_rate)
- Columns:
segment_wide_rates.csv- Segment-specific mutation rates- Columns: same as
genome_wide_rates.csvminussyn_rateandrel_rate, plus:segment— genome segment (e.g."HA","PB2")
- Columns: same as
motif_level_genome_wide_rates.csv- Context-dependent rates (3-mer motifs)- Columns:
mut_type— two-character mutation type (e.g."AC"for A→C)motif— 3-mer sequence context (centered on the mutated site)host— host group ("human","avian", or"all")actual_count— total number of observed mutations for this type/motif/hostevo_opp— evolutionary opportunityrate— mutation rate
- Columns:
evo_opp_thresholds.csv- Evolutionary opportunity thresholds for filtering- Columns: same as
genome_wide_rates.csvminusrel_rate, plus:evo_opp_threshold— minimum evolutionary opportunity a site must have to be included in rate calculations for this mutation type/class/host
- Columns: same as
site_specific_substitution_rates.csv- Per-site mutation rates- Columns: same as
counts.csv, plus:evo_opp_threshold— minimum evolutionary opportunity threshold for this mutation type/class/host (fromevo_opp_thresholds.csv)unclipped_rate— raw per-site mutation rate before applying the floormin_rate— floor rate applied to sites below the evolutionary opportunity threshold (set to the synonymous genome-wide rate)
- Columns: same as
-
Neutral Model Outputs:
results/neutral_model/base/- Model with mutation type onlyexpected_rates_by_predictor.csv- Columns:
mut_type— two-character mutation type (e.g."AC"for A→C)predicted_rate— model-predicted synonymous mutation rate
- Columns:
model_performance.csv- Columns (same for all three models):
mut_type— two-character mutation type (e.g."AC"for A→C)mse— mean squared error of the model fitvar— variance of the observed rates (baseline MSE with no predictors)r2— R² of the model fit
- Columns (same for all three models):
local_context/- Model with mutation type + 3-mer motifexpected_rates_by_predictor.csv- Columns: same as
base/expected_rates_by_predictor.csv, plus:motif— 3-mer sequence context
- Columns: same as
model_performance.csv
local_context+global_context/- Model with mutation type + motif + segmentexpected_rates_by_predictor.csv- Columns: same as
local_context/expected_rates_by_predictor.csv, plus:segment— genome segment (e.g."HA","PB2")
- Columns: same as
model_performance.csv
-
Complete Expected Rates:
results/expected_rates.csv- Full model predictions augmented with CG and GC empirical rates
- Contains all 12 mutation types × 16 motifs × 8 segments (1536 rows)
- Columns: same as
local_context+global_context/expected_rates_by_predictor.csv(mut_type,segment,motif,predicted_rate), but with complete coverage of all 12 mutation types; CG and GC rates are filled in from the augmentation step (Step 7) rather than model predictions
-
Fitness Effect Files:
nt_fitness_effects.csv- Per-nucleotide-mutation fitness effects- Columns:
host— host group ("human","avian", or"all")subtype— influenza subtype (e.g."H1","N2", or"all")segment— genome segment (e.g."HA","PB2")gene— gene containing the sitesite— nucleotide position in the reference sequence (1-indexed)wt_nt— wildtype nucleotidemut_nt— mutant nucleotident_mut— nucleotide mutation string (e.g."A1C")mut_class— mutation class ("synonymous","nonsynonymous","nonsense", or"noncoding")actual_count— total observed occurrences of this nucleotide mutationexpected_count— total expected occurrences under the neutral modeldelta_fitness— estimated fitness effect: log((actual_count + 0.5) / (expected_count + 0.5))
- Columns:
sitewise_synonymous_fitness_effects.csv- Per-site synonymous fitness effects- Columns:
host— host group ("human","avian", or"all")subtype— influenza subtype (e.g."H1","N2", or"all")segment— genome segment (e.g."HA","PB2")gene— gene containing the sitesite— nucleotide position in the reference sequence (1-indexed)delta_fitness— estimated fitness effect: mean of per-nucleotide-mutationdelta_fitnessvalues (fromnt_fitness_effects.csv) across synonymous mutations at this site
- Columns:
aa_fitness_effects.csv- Per-amino-acid-mutation fitness effects- Columns:
host— host group ("human","avian", or"all")subtype— influenza subtype (e.g."H1","N2", or"all")segment— genome segment (e.g."HA","PB2")gene— gene containing the sitecodon_site— index of the codon within the gene (1-indexed)wt_aa— wildtype amino acid (single-letter code)mut_aa— mutant amino acid (single-letter code)aa_mut— amino acid mutation string (e.g."M1L")mut_class— mutation class ("synonymous","nonsynonymous", or"nonsense")actual_count— total observed nucleotide mutations resulting in this amino acid changeexpected_count— total expected mutations under the neutral modeldelta_fitness— estimated fitness effect: log((actual_count + 0.5) / (expected_count + 0.5))
- Columns:
-
SHAPE-MaP Data:
results/shapemap/all_data.csv- SHAPE-MaP RNA structure reactivities (Dadonaite et al. 2019) mapped to reference site coordinates for internal segments
-
Subset Analysis Outputs:
subset_counts.csv- Aggregated mutation counts from all subset trees (host, geographic, split-half)- Same columns as
counts.csv, excepthostis replaced by:subset— subset name (e.g."human","north_america","split_a")subset_type— subset category ("host","geographic","split_half")
- Same columns as
subset_aa_fitness_effects.csv- Per-amino-acid-mutation fitness effects for each subset- Columns:
subset— subset namesubset_type— subset categorysubtype— influenza subtypesegment— genome segmentgene— gene namecodon_site— codon site within the genewt_aa— wildtype amino acidmut_aa— mutant amino acidaa_mut— amino acid mutation stringmut_class— mutation classactual_count— total observed mutationsexpected_count— total expected mutations under the neutral modeldelta_fitness— estimated fitness effect: log((actual_count + 0.5) / (expected_count + 0.5))
- Columns:
- Processed DMS Data:
results/dms_data/
Yu_HA/processed_dms_data.csv- HA DMS phenotypes (Yu et al. 2025) with tree reference site numberingBloom_NP/processed_dms_data.csv- NP DMS preferences (Bloom et al. 2014) with log-ratio fitness effects.process_dms_data_soh_pb2.done- Sentinel file marking completion of Soh et al. PB2 alignment QCWang_NA/processed_dms_data.csv- NA DMS fitness effects (Wang et al. 2023) with tree reference site numberingLi_PB1/processed_dms_data.csv- PB1 DMS fitness effects (Li et al. 2023) with tree reference site numberingHom_M1/processed_dms_data.csv- M1 DMS fitness effects (Hom et al. 2019) with tree reference site numberingTeo_NEP/processed_dms_data.csv- NEP DMS fitness effects (Teo et al. 2024) with tree reference site numberingChen_PA/processed_dms_data.csv- PA DMS fitness effects (Chen et al. 2024) for first 240 AA with tree reference site numbering
results/
├── HA/
│ ├── H1/
│ │ ├── coding_sites.csv
│ │ ├── mutation_counts.csv
│ │ ├── parent_child_pairs.csv
│ │ ├── host_stratified/
│ │ │ ├── mutation_counts.csv # has a host column
│ │ │ └── parent_child_pairs.csv # has a host column
│ │ ├── north_america/
│ │ │ ├── mutation_counts.csv
│ │ │ └── parent_child_pairs.csv
│ │ ├── europe/
│ │ │ ├── mutation_counts.csv
│ │ │ └── parent_child_pairs.csv
│ │ ├── asia/
│ │ │ ├── mutation_counts.csv
│ │ │ └── parent_child_pairs.csv
│ │ ├── split_a/
│ │ │ └── mutation_counts.csv
│ │ └── split_b/
│ │ └── mutation_counts.csv
│ └── H3/
│ └── ...
├── aligned_proteins/
│ ├── HA/
│ └── NA/
├── counts.csv
├── genome_wide_rates.csv
├── segment_wide_rates.csv
├── motif_level_genome_wide_rates.csv
├── evo_opp_thresholds.csv
├── site_specific_substitution_rates.csv
├── expected_rates.csv
├── nt_fitness_effects.csv
├── sitewise_synonymous_fitness_effects.csv
├── aa_fitness_effects.csv
├── shapemap/
│ └── all_data.csv
├── dms_data/
│ ├── Yu_HA/
│ │ └── processed_dms_data.csv
│ ├── Bloom_NP/
│ │ └── processed_dms_data.csv
│ ├── Wang_NA/
│ │ └── processed_dms_data.csv
│ ├── Li_PB1/
│ │ └── processed_dms_data.csv
│ ├── Hom_M1/
│ │ └── processed_dms_data.csv
│ ├── Teo_NEP/
│ │ └── processed_dms_data.csv
│ └── Chen_PA/
│ └── processed_dms_data.csv
├── .process_dms_data_soh_pb2.done
├── .summarize_filter_logs.done
├── subset_counts.csv
├── subset_aa_fitness_effects.csv
├── .analyze_subset_fitness_effects.done
└── neutral_model/
├── base/
│ ├── expected_rates_by_predictor.csv
│ └── model_performance.csv
├── local_context/
│ ├── expected_rates_by_predictor.csv
│ └── model_performance.csv
└── local_context+global_context/
├── expected_rates_by_predictor.csv
└── model_performance.csv
Two interactive fitness effect dashboards are hosted via GitHub Pages under docs/ and exported using marimo in WASM mode:
- AA dashboard (
docs/aa/index.html): per-amino-acid-mutation fitness effects (notebooks/aa_fitness_heatmap_dashboard.py) - NT dashboard (
docs/nt/index.html): per-nucleotide-mutation fitness effects (notebooks/nt_fitness_heatmap_dashboard.py)
To regenerate the HTML exports after modifying a dashboard notebook, run the corresponding Snakemake rule:
# Regenerate the NT dashboard
snakemake --cores 1 docs/nt/index.html --forcerun export_nt_dashboard
# Regenerate the AA dashboard
snakemake --cores 1 docs/aa/index.html --forcerun export_dashboardEach rule exports the notebook as a self-contained WASM app, patches the theme to dark, and copies the required result files into the docs/{aa,nt}/results/ directory so they are accessible to the app at runtime.
The same heatmap visuals shown in the dashboards are exported as static PNGs by rule render_fitness_heatmaps, which calls scripts/render_fitness_heatmaps.py. Each snapshot is a single-panel, dark-themed heatmap (no zoom bar, no line plot) for a fixed gene + site range; the per-PNG SPECS dict in the script controls which slices are rendered. Outputs land in results/figures/ and are consumed by notebooks/compose_figures.ipynb to build the manuscript figure panels:
aa_heatmap.png— PA amino-acid fitness effects (sites 506–540)nt_heatmap.png— MP synonymous nucleotide fitness effectsMP_5SS.png/MP_3SS.png— M2 5′/3′ splice-site windowsNS_5SS.png/NS_3SS.png— NEP 5′/3′ splice-site windows
The chart-construction code is intentionally duplicated between the snapshot script and the marimo dashboards so the dashboards remain self-contained for WASM export. Visual specs (color scale, AA/NT row order) should be kept in sync between the two if changed.
- Python 3.10+
- Snakemake
- Jupyter (for notebook execution)
- MUSCLE (for protein alignment)
- Standard scientific Python packages (pandas, numpy, scipy, matplotlib, seaborn, biopython)
Create the conda environment from the provided file:
conda env create -f environment.yml
conda activate flu-mut-ratesThe pipeline will automatically execute Jupyter notebooks as part of the workflow using jupyter nbconvert.