alterlab-phylogenetics
Build phylogenetic trees end-to-end from raw sequences — MAFFT multiple sequence alignment, optional TrimAl trimming, IQ-TREE 3 maximum-likelihood inference with model selection and bootstraps, FastTree for large datasets, then visualize with ETE3 or FigTree. Use when reconstruct
Install
npx skills add https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/bioinformatics/alterlab-phylogenetics
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alterlab-ieu-alterlab-academic-skills@llmmart
git clone https://github.com/AlterLab-IEU/AlterLab-Academic-Skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole alterlab-ieu/alterlab-academic-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Phylogenetics
Overview
Phylogenetic analysis reconstructs the evolutionary history of biological sequences (genes, proteins, genomes) by inferring the branching pattern of descent. This skill covers the standard pipeline:
- MAFFT — Multiple sequence alignment
- IQ-TREE 3 — Maximum likelihood tree inference with model selection
- FastTree — Fast approximate maximum likelihood (for large datasets)
- ETE3 — Python library for tree manipulation and visualization
Installation: The aligners and tree builders are compiled CLI tools (not on PyPI). Install the binaries via bioconda or Homebrew; install the Python visualization layer with uv.
# CLI binaries — bioconda (cross-platform) ...
conda install -c bioconda mafft iqtree fasttree trimal # iqtree is now IQ-TREE 3
# ... or Homebrew on macOS (Apple Silicon): IQ-TREE/TrimAl live in the brewsci/bio tap
brew install mafft fasttree
brew tap brewsci/bio && brew install brewsci/bio/iqtree brewsci/bio/trimal
# Python visualization layer
uv pip install "ete3==3.1.3" # also needs numpy<2 and PyQt5 for rendering
Binary name: bioconda's
iqtreepackage is now IQ-TREE 3 (3.1.x) and installs the executablesiqtreeandiqtree3— there is noiqtree2in it. Calliqtree, which works on both the 2.x and 3.x packages; only reach for a versioned name when two releases coexist on PATH. IQ-TREE 3 adds MixtureFinder, multi-tree mixture models (MAST), gene/site concordance factors, and CMAPLE for very large pathogen datasets; the options below are unchanged from 2.x.ETE3 (3.1.3) is the last ete3 release and can be fragile to install on Python ≥3.12 (pins old numpy/PyQt5). If
t.render()fails, fall back to writing the Newick tree and viewing it in FigTree/iTOL, or use the maintained successorete4(note: ete4 changed theTreeStyle/renderAPI, so the snippets below are ete3-specific).
When to Use This Skill
Use phylogenetics when:
- Evolutionary relationships: Which organism/gene is most closely related to my sequence?
- Viral phylodynamics: Trace outbreak spread and estimate transmission dates
- Protein family analysis: Infer evolutionary relationships within a gene family
- Horizontal gene transfer detection: Identify genes with discordant species/gene trees
- Ancestral sequence reconstruction: Infer ancestral protein sequences
- Molecular clock analysis: Estimate divergence dates using temporal sampling
- GWAS companion: Place variants in evolutionary context (e.g., SARS-CoV-2 variants)
- Microbiology: Species phylogeny from 16S rRNA or core genome phylogeny
Does NOT Trigger
| Scenario | Use Instead |
|---|---|
| Manipulating or comparing an existing Newick tree (prune, root, Robinson-Foulds, duplication/speciation) | alterlab-etetoolkit |
| Plain sequence parsing, translation, or format conversion | alterlab-biopython |
| Homology search to find the sequences before aligning them | alterlab-blast |
| Diversity/ordination on a community feature table (UniFrac, PCoA, PERMANOVA) | alterlab-scikit-bio |
| Running the 16S amplicon pipeline that produces the ASVs and tree | alterlab-qiime2-amplicon |
Standard Workflow
1. Multiple Sequence Alignment with MAFFT
import subprocess
import os
def run_mafft(input_fasta: str, output_fasta: str, method: str = "auto",
n_threads: int = 4) -> str:
"""
Align sequences with MAFFT.
Args:
input_fasta: Path to unaligned FASTA file
output_fasta: Path for aligned output
method: 'auto' (auto-select), 'einsi' (accurate), 'linsi' (accurate, slow),
'fftnsi' (medium), 'fftns' (fast), 'retree2' (fast)
n_threads: Number of CPU threads
Returns:
Path to aligned FASTA file
"""
methods = {
"auto": ["mafft", "--auto"],
"einsi": ["mafft", "--genafpair", "--maxiterate", "1000"],
"linsi": ["mafft", "--localpair", "--maxiterate", "1000"],
"fftnsi": ["mafft", "--retree", "2", "--maxiterate", "2"],
"fftns": ["mafft", "--retree", "2", "--maxiterate", "0"],
"retree2": ["mafft", "--retree", "2"],
}
cmd = methods.get(method, methods["auto"])
cmd += ["--thread", str(n_threads), "--inputorder", input_fasta]
with open(output_fasta, 'w') as out:
result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
raise RuntimeError(f"MAFFT failed:\n{result.stderr}")
# Count aligned sequences
with open(output_fasta) as f:
n_seqs = sum(1 for line in f if line.startswith('>'))
print(f"MAFFT: aligned {n_seqs} sequences → {output_fasta}")
return output_fasta
# MAFFT method selection guide:
# Few sequences (<200), accurate: linsi or einsi
# Many sequences (<1000), moderate: fftnsi
# Large datasets (>1000): fftns or auto
# Ultra-fast (>10000): mafft --retree 1
2. Trim Alignment (Optional but Recommended)
def trim_alignment_trimal(aligned_fasta: str, output_fasta: str,
method: str = "automated1") -> str:
"""
Trim poorly aligned columns with TrimAl.
Methods:
- 'automated1': Automatic heuristic (recommended)
- 'gappyout': Remove gappy columns
- 'strict': Strict gap threshold
"""
cmd = ["trimal", f"-{method}", "-in", aligned_fasta, "-out", output_fasta, "-fasta"]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"TrimAl warning: {result.stderr}")
# Fall back to using the untrimmed alignment
import shutil
shutil.copy(aligned_fasta, output_fasta)
return output_fasta
3. IQ-TREE 3 — Maximum Likelihood Tree
def run_iqtree(aligned_fasta: str, output_prefix: str,
model: str = "TEST", bootstrap: int = 1000,
n_threads: int = 4, extra_args: list = None) -> dict:
"""
Build a maximum likelihood tree with IQ-TREE 3.
Args:
aligned_fasta: Aligned FASTA file
output_prefix: Prefix for output files
model: 'TEST' for automatic model selection, or specify (e.g., 'GTR+G' for DNA,
'LG+G4' for proteins, 'JTT+G' for proteins)
bootstrap: Number of ultrafast bootstrap replicates (1000 recommended)
n_threads: Number of threads ('AUTO' to auto-detect)
extra_args: Additional IQ-TREE arguments
Returns:
Dict with paths to output files
"""
cmd = [
"iqtree",
"-s", aligned_fasta,
"--prefix", output_prefix,
"-m", model,
"-B", str(bootstrap), # Ultrafast bootstrap
"-T", str(n_threads),
"--redo" # Overwrite existing results
]
if extra_args:
cmd.extend(extra_args)
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"IQ-TREE failed:\n{result.stderr}")
# Print model selection result
log_file = f"{output_prefix}.log"
if os.path.exists(log_file):
with open(log_file) as f:
for line in f:
if "Best-fit model" in line:
print(f"IQ-TREE: {line.strip()}")
output_files = {
"tree": f"{output_prefix}.treefile",
"log": f"{output_prefix}.log",
"iqtree": f"{output_prefix}.iqtree", # Full report
"model": f"{output_prefix}.model.gz",
}
print(f"IQ-TREE: Tree saved to {output_files['tree']}")
return output_files
# Model selection: ModelFinder Plus (-m MFP) is the DEFAULT — omitting -m runs it.
# -m TEST is the narrower jModelTest/ProtTest-style search (no FreeRate models);
# use it only when you need to match those tools.
# Typical winners — DNA: GTR+F+I+G4, HKY+F+G4; Protein: LG+F+G4, Q.pfam+G4;
# Codon: MG+F3X4.
# For temporal (molecular clock) analysis, add:
# extra_args = ["--date", "dates.txt", "--clock-test", "--date-CI", "95"]
4. FastTree — Fast Approximate ML
For large datasets (>1000 sequences) where IQ-TREE is too slow:
def run_fasttree(aligned_fasta: str, output_tree: str,
sequence_type: str = "nt", model: str = "gtr",
n_threads: int = 4) -> str:
"""
Build a fast approximate ML tree with FastTree.
Args:
sequence_type: 'nt' for nucleotide or 'aa' for amino acid
model: For nt: 'gtr' (recommended) or 'jc'; for aa: 'lg', 'wag', 'jtt'
"""
if sequence_type == "nt":
cmd = ["FastTree", "-nt", "-gtr"]
else:
cmd = ["FastTree", f"-{model}"]
cmd += [aligned_fasta]
with open(output_tree, 'w') as out:
result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
raise RuntimeError(f"FastTree failed:\n{result.stderr}")
print(f"FastTree: Tree saved to {output_tree}")
return output_tree
5. Tree Analysis and Visualization with ETE3
from ete3 import Tree, TreeStyle, NodeStyle, TextFace, PhyloTree
import matplotlib.pyplot as plt
def load_tree(tree_file: str) -> Tree:
"""Load a Newick tree file."""
t = Tree(tree_file)
print(f"Tree: {len(t)} leaves, {len(list(t.traverse()))} nodes")
return t
def basic_tree_stats(t: Tree) -> dict:
"""Compute basic tree statistics."""
leaves = t.get_leaves()
distances = [t.get_distance(l1, l2) for l1 in leaves[:min(50, len(leaves))]
for l2 in leaves[:min(50, len(leaves))] if l1 != l2]
stats = {
"n_leaves": len(leaves),
"n_internal_nodes": len(t) - len(leaves),
"total_branch_length": sum(n.dist for n in t.traverse()),
"max_leaf_distance": max(distances) if distances else 0,
"mean_leaf_distance": sum(distances)/len(distances) if distances else 0,
}
return stats
def find_mrca(t: Tree, leaf_names: list) -> Tree:
"""Find the most recent common ancestor of a set of leaves."""
return t.get_common_ancestor(*leaf_names)
def visualize_tree(t: Tree, output_file: str = "tree.png",
show_branch_support: bool = True,
color_groups: dict = None,
width: int = 800) -> None:
"""
Render phylogenetic tree to image.
Args:
t: ETE3 Tree object
color_groups: Dict mapping leaf_name → color (for coloring taxa)
show_branch_support: Show bootstrap values
"""
ts = TreeStyle()
ts.show_leaf_name = True
ts.show_branch_support = show_branch_support
ts.mode = "r" # 'r' = rectangular, 'c' = circular
if color_groups:
for node in t.traverse():
if node.is_leaf() and node.name in color_groups:
nstyle = NodeStyle()
nstyle["fgcolor"] = color_groups[node.name]
nstyle["size"] = 8
node.set_style(nstyle)
t.render(output_file, tree_style=ts, w=width, units="px")
print(f"Tree saved to: {output_file}")
def midpoint_root(t: Tree) -> Tree:
"""Root tree at midpoint (use when outgroup unknown)."""
t.set_outgroup(t.get_midpoint_outgroup())
return t
def prune_tree(t: Tree, keep_leaves: list) -> Tree:
"""Prune tree to keep only specified leaves."""
t.prune(keep_leaves, preserve_branch_length=True)
return t
6. Complete Analysis Script
import subprocess, os
from ete3 import Tree
def full_phylogenetic_analysis(
input_fasta: str,
output_dir: str = "phylo_results",
sequence_type: str = "nt",
n_threads: int = 4,
bootstrap: int = 1000,
use_fasttree: bool = False
) -> dict:
"""
Complete phylogenetic pipeline: align → trim → tree → visualize.
Args:
input_fasta: Unaligned FASTA
sequence_type: 'nt' (nucleotide) or 'aa' (amino acid/protein)
use_fasttree: Use FastTree instead of IQ-TREE (faster for large datasets)
"""
os.makedirs(output_dir, exist_ok=True)
prefix = os.path.join(output_dir, "phylo")
print("=" * 50)
print("Step 1: Multiple Sequence Alignment (MAFFT)")
aligned = run_mafft(input_fasta, f"{prefix}_aligned.fasta",
method="auto", n_threads=n_threads)
print("\nStep 2: Tree Inference")
if use_fasttree:
tree_file = run_fasttree(
aligned, f"{prefix}.tree",
sequence_type=sequence_type,
model="gtr" if sequence_type == "nt" else "lg"
)
else:
# -m TEST auto-detects the alphabet (nt vs aa) and selects the best model.
iqtree_files = run_iqtree(
aligned, prefix,
model="TEST",
bootstrap=bootstrap,
n_threads=n_threads
)
tree_file = iqtree_files["tree"]
print("\nStep 3: Tree Analysis")
t = Tree(tree_file)
t = midpoint_root(t)
stats = basic_tree_stats(t)
print(f"Tree statistics: {stats}")
print("\nStep 4: Visualization")
visualize_tree(t, f"{prefix}_tree.png", show_branch_support=True)
# Save rooted tree
rooted_tree_file = f"{prefix}_rooted.nwk"
t.write(format=1, outfile=rooted_tree_file)
results = {
"aligned_fasta": aligned,
"tree_file": tree_file,
"rooted_tree": rooted_tree_file,
"visualization": f"{prefix}_tree.png",
"stats": stats
}
print("\n" + "=" * 50)
print("Phylogenetic analysis complete!")
print(f"Results in: {output_dir}/")
return results
IQ-TREE Model Guide
DNA Models
| Model | Description | Use case |
|---|---|---|
GTR+G4 |
General Time Reversible + Gamma | Most flexible DNA model |
HKY+G4 |
Hasegawa-Kishino-Yano + Gamma | Two-rate model (common) |
TrN+G4 |
Tamura-Nei | Unequal transitions |
JC |
Jukes-Cantor | Simplest; all rates equal |
Protein Models
| Model | Description | Use case |
|---|---|---|
LG+G4 |
Le-Gascuel + Gamma | Best average protein model |
WAG+G4 |
Whelan-Goldman | Widely used |
JTT+G4 |
Jones-Taylor-Thornton | Classical model |
Q.pfam+G4 |
Pfam-trained (QMaker) | General protein families |
Q.bird+G4 |
Bird clade-specific (QMaker) | Bird proteins; siblings: Q.mammal, Q.insect, Q.yeast, Q.plant |
Tip: IQ-TREE selects the model automatically by default (-m MFP); pass an explicit model
only when you need to fix it.
Best Practices
- Alignment quality first: Poor alignment → unreliable trees; check alignment manually
- Use
linsifor small (<200 seq),fftnsorautofor large alignments - Model selection: ModelFinder Plus is IQ-TREE's default — just omit
-m(or pass-m MFP).-m TESTrestricts the search to jModelTest/ProtTest-style models, so use it only to match those tools - Bootstrap: Use ≥1000 ultrafast bootstraps (
-B 1000) for branch support - Root the tree: Unrooted trees can be misleading; use outgroup or midpoint rooting
- FastTree for >5000 sequences: IQ-TREE becomes slow; FastTree is 10–100× faster
- Trim long alignments: TrimAl removes unreliable columns; improves tree accuracy
- Check for recombination in viral/bacterial sequences before building trees (
RDP4,GARD)
Additional Resources
- MAFFT: https://mafft.cbrc.jp/alignment/software/
- IQ-TREE 3: http://www.iqtree.org/ | Tutorial: https://www.iqtree.org/workshop/molevol2022
- FastTree: http://www.microbesonline.org/fasttree/
- ETE3: http://etetoolkit.org/
- FigTree (GUI visualization): https://tree.bio.ed.ac.uk/software/figtree/
- iTOL (web visualization): https://itol.embl.de/
- MUSCLE (alternative aligner): https://www.drive5.com/muscle/
- TrimAl (alignment trimming): https://vicfero.github.io/trimal/
Files (alterlab-academic-skills)
-
evals
-
evals.json 4.7 KB
{ "skill": "alterlab-phylogenetics", "evals": [ { "id": "full-ml-tree-from-fasta", "prompt": "I have a FASTA of about 150 unaligned protein sequences from a gene family. Align them, trim the bad columns, infer a maximum-likelihood tree with automatic model selection and 1000 bootstraps, then root it and give me a figure.", "expected_output": "Invokes alterlab-phylogenetics: runs MAFFT (linsi/einsi for ~150 seqs) to align, TrimAl to trim, IQ-TREE 2 with -m TEST for model selection and -B 1000 ultrafast bootstraps, then midpoint-roots and visualizes with ETE3. Full align -> trim -> ML tree -> visualize pipeline.", "assertions": [ { "type": "should_trigger", "value": true }, { "type": "output_contains", "value": "IQ-TREE" }, { "type": "behavior", "value": "Sequences MAFFT alignment, optional TrimAl, then IQ-TREE 2 with model selection (-m TEST) and -B 1000 bootstraps." } ] }, { "id": "fasttree-large-dataset", "prompt": "I've got an alignment of around 8000 nucleotide sequences and IQ-TREE is taking forever. What's the fast way to get an approximate ML tree out of this?", "expected_output": "Invokes alterlab-phylogenetics: recommends FastTree for the large dataset (FastTree -nt -gtr for nucleotides), noting it is 10-100x faster than IQ-TREE for thousands of sequences, as the appropriate fast approximate-ML choice.", "assertions": [ { "type": "should_trigger", "value": true }, { "type": "output_contains", "value": "FastTree" }, { "type": "behavior", "value": "Recommends FastTree (with -nt -gtr) for the large alignment as the fast approximate-ML alternative to IQ-TREE." } ] }, { "id": "mafft-method-selection", "prompt": "I'm aligning around 60 closely related sequences and accuracy matters more than speed. Which MAFFT mode should I use and how do I run it?", "expected_output": "Invokes alterlab-phylogenetics: recommends an accurate MAFFT mode for the small set (linsi via --localpair --maxiterate 1000, or einsi via --genafpair) since accuracy is preferred for <200 sequences, and shows the run command.", "assertions": [ { "type": "should_trigger", "value": true }, { "type": "output_contains", "value": "MAFFT" }, { "type": "behavior", "value": "Selects an accurate MAFFT mode (linsi/einsi) for the small sequence set per the method-selection guidance." } ] }, { "id": "viral-molecular-clock-recombination", "prompt": "I'm doing phylodynamics on a set of dated SARS-CoV-2 genomes and want to estimate divergence dates. Anything I should check before building the tree, and how do I add the temporal calibration?", "expected_output": "Invokes alterlab-phylogenetics: warns to check for recombination first (RDP4/GARD) in viral sequences, then builds the tree and adds molecular-clock / temporal calibration via IQ-TREE 2 --date with dates.txt (and --date-CI). Phylodynamics/molecular-clock workflow.", "assertions": [ { "type": "should_trigger", "value": true }, { "type": "behavior", "value": "Advises a recombination check (RDP4/GARD) before tree-building and uses IQ-TREE --date for temporal/molecular-clock calibration." } ] }, { "id": "near-miss-etetoolkit", "prompt": "I already have a finished Newick tree. I just want to prune it to a subset of taxa keeping branch lengths, detect duplication versus speciation events on the gene tree, and compute the Robinson-Foulds distance to a second tree.", "expected_output": "Should NOT trigger alterlab-phylogenetics. Manipulating an existing tree (prune, duplication/speciation event detection via get_descendant_evol_events, robinson_foulds comparison) is alterlab-etetoolkit territory. phylogenetics is the upstream align-and-infer pipeline and defers pure tree manipulation/comparison to the ETE Toolkit skill.", "assertions": [ { "type": "should_not_trigger", "value": true }, { "type": "output_contains", "value": "etetoolkit" } ] }, { "id": "near-miss-biopython", "prompt": "I just need to parse a multi-record FASTA file, reverse-complement each sequence, translate it to protein, and write the results back out to a new FASTA.", "expected_output": "Should NOT trigger alterlab-phylogenetics. Basic sequence parsing, reverse-complement, translation, and FASTA I/O is general sequence handling for alterlab-biopython, with no alignment or tree inference involved. phylogenetics is specifically for reconstructing evolutionary trees.", "assertions": [ { "type": "should_not_trigger", "value": true }, { "type": "output_contains", "value": "biopython" } ] } ] }
-
-
references
-
iqtree_inference.md 5.5 KB
# IQ-TREE Phylogenetic Inference Reference > Commands below call `iqtree`. bioconda's `iqtree` package is IQ-TREE 3 (3.1.x) and installs > `iqtree` and `iqtree3`; the older 2.x package installed `iqtree2`. The options here are the > same on both. ## Basic Command Syntax ```bash iqtree -s alignment.fasta --prefix output -B 1000 -T AUTO --redo # -m MFP is the default ``` ## Key Parameters | Flag | Description | Default | |------|-------------|---------| | `-s` | Input alignment file | Required | | `--prefix` | Output file prefix | alignment name | | `-m` | Substitution model (or TEST) | GTR+G | | `-B` | Ultrafast bootstrap replicates | Off | | `-b` | Standard bootstrap replicates (slow) | Off | | `-T` | Number of threads (or AUTO) | 1 | | `-o` | Outgroup taxa name(s) | None (unrooted) | | `--redo` | Overwrite existing results | Off | | `-alrt` | SH-aLRT test replicates | Off | ## Model Selection ```bash # Full model testing (automatically selects best model) iqtree -s alignment.fasta -m TEST --prefix test_run -B 1000 -T 4 # Specify model explicitly iqtree -s alignment.fasta -m GTR+G4 --prefix gtr_run -B 1000 # Protein sequences iqtree -s protein.fasta -m TEST --prefix prot_tree -B 1000 # Codon-based analysis iqtree -s codon.fasta -m GY --prefix codon_tree -B 1000 ``` ## Bootstrapping Methods ### Ultrafast Bootstrap (UFBoot, recommended) ```bash iqtree -s alignment.fasta -B 1000 # 1000 replicates # Values ≥95 are reliable # ~10× faster than standard bootstrap ``` ### Standard Bootstrap ```bash iqtree -s alignment.fasta -b 100 # 100 replicates (very slow) ``` ### SH-aLRT Test (fast alternative) ```bash iqtree -s alignment.fasta -alrt 1000 -B 1000 # Both SH-aLRT and UFBoot # SH-aLRT ≥80 AND UFBoot ≥95 = well-supported branch ``` ## Branch Support Interpretation | Bootstrap Value | Interpretation | |----------------|----------------| | ≥ 95 | Well-supported (strongly supported) | | 70–94 | Moderately supported | | 50–69 | Weakly supported | | < 50 | Unreliable (not supported) | ## Output Files | File | Description | |------|-------------| | `{prefix}.treefile` | Best ML tree in Newick format | | `{prefix}.iqtree` | Full analysis report | | `{prefix}.log` | Computation log | | `{prefix}.contree` | Consensus tree from bootstrap | | `{prefix}.splits.nex` | Network splits | | `{prefix}.bionj` | BioNJ starting tree | | `{prefix}.model.gz` | Saved model parameters | ## Advanced Analyses ### Molecular Clock (Dating) ```bash # Temporal analysis with sampling dates iqtree -s alignment.fasta -m GTR+G \ --date dates.tsv \ # Tab-separated: taxon_name YYYY-MM-DD --clock-test \ # Test for clock-like evolution --date-CI 95 \ # 95% CI for node dates --prefix dated_tree ``` ### Concordance Factors ```bash # Gene concordance factor (gCF) - requires multiple gene alignments iqtree --gcf gene_trees.nwk \ --tree main_tree.treefile \ --cf-verbose \ --prefix cf_analysis ``` ### Ancestral Sequence Reconstruction ```bash iqtree -s alignment.fasta -m LG+G4 \ -asr \ # Marginal ancestral state reconstruction --prefix anc_tree # Output: {prefix}.state (ancestral sequences per node) ``` ### Partition Model (Multi-Gene) ```bash # Create partition file (partitions.txt): # DNA, gene1 = 1-500 # DNA, gene2 = 501-1000 iqtree -s concat_alignment.fasta \ -p partitions.txt \ -m TEST \ -B 1000 \ --prefix partition_tree ``` ## IQ-TREE Log Parsing ```python def parse_iqtree_log(log_file: str) -> dict: """Extract key results from IQ-TREE log file.""" results = {} with open(log_file) as f: for line in f: if "Best-fit model" in line: results["best_model"] = line.split(":")[1].strip() elif "Log-likelihood of the tree:" in line: results["log_likelihood"] = float(line.split(":")[1].strip()) elif "Number of free parameters" in line: results["free_params"] = int(line.split(":")[1].strip()) elif "Akaike information criterion" in line: results["AIC"] = float(line.split(":")[1].strip()) elif "Bayesian information criterion" in line: results["BIC"] = float(line.split(":")[1].strip()) elif "Total CPU time used" in line: results["cpu_time"] = line.split(":")[1].strip() return results # Example: # results = parse_iqtree_log("output.log") # print(f"Best model: {results['best_model']}") # print(f"Log-likelihood: {results['log_likelihood']:.2f}") ``` ## Common Issues and Solutions | Issue | Likely Cause | Solution | |-------|-------------|---------| | All bootstrap values = 0 | Too few taxa | Need ≥4 taxa for bootstrap | | Very long branches | Alignment artifacts | Re-trim alignment; check for outliers | | Memory error | Too many sequences | Use FastTree; or reduce `-T` to 1 | | Poor model fit | Wrong alphabet | Check nucleotide vs. protein specification | | Identical sequences | Duplicate sequences | Remove duplicates before alignment | ## MAFFT Alignment Guide ```bash # Accurate (< 200 sequences) mafft --localpair --maxiterate 1000 input.fasta > aligned.fasta # Medium (200-1000 sequences) mafft --auto input.fasta > aligned.fasta # Fast (> 1000 sequences) mafft --fftns input.fasta > aligned.fasta # Very large (> 10000 sequences) mafft --retree 1 input.fasta > aligned.fasta # Using multiple threads mafft --thread 8 --auto input.fasta > aligned.fasta ```
-
-
scripts
-
phylogenetic_analysis.py 9.9 KB
""" Phylogenetic Analysis Pipeline =============================== Complete workflow: MAFFT alignment → IQ-TREE 3 tree → ETE3 visualization. Requirements (CLI binaries are NOT on PyPI): conda install -c bioconda mafft iqtree fasttree # or Homebrew: brew install mafft fasttree; brew install brewsci/bio/iqtree uv pip install "ete3==3.1.3" # visualization only (optional) Usage: python phylogenetic_analysis.py sequences.fasta --type nt --threads 4 python phylogenetic_analysis.py proteins.fasta --type aa --fasttree """ import argparse import os import subprocess import sys from pathlib import Path from shutil import which # bioconda's `iqtree` package is IQ-TREE 3 and ships `iqtree` + `iqtree3`; the 2.x # package shipped `iqtree2`. Resolve whichever is on PATH instead of hardcoding one. IQTREE_CANDIDATES = ("iqtree", "iqtree3", "iqtree2") def find_iqtree(): """Return the first IQ-TREE executable on PATH, or None.""" for name in IQTREE_CANDIDATES: if which(name) is not None: return name return None def check_dependencies(use_fasttree: bool = False): """Check that the required CLI tools are on PATH (binaries are not pip-installable).""" tools = {"mafft": "conda install -c bioconda mafft (or: brew install mafft)"} if use_fasttree: tools["FastTree"] = "conda install -c bioconda fasttree (or: brew install fasttree)" missing = [f" {tool}: {hint}" for tool, hint in tools.items() if which(tool) is None] if not use_fasttree and find_iqtree() is None: missing.append( " iqtree (or iqtree3/iqtree2): " "conda install -c bioconda iqtree (or: brew install brewsci/bio/iqtree)" ) if missing: print("Missing dependencies:") for m in missing: print(m) sys.exit(1) print("All dependencies found.") def count_sequences(fasta_file: str) -> int: """Count sequences in a FASTA file.""" with open(fasta_file) as f: return sum(1 for line in f if line.startswith('>')) def run_mafft(input_fasta: str, output_fasta: str, n_threads: int = 4, method: str = "auto") -> str: """Run MAFFT multiple sequence alignment.""" n_seqs = count_sequences(input_fasta) print(f"MAFFT: Aligning {n_seqs} sequences...") # Auto-select method based on dataset size if method == "auto": if n_seqs <= 200: cmd = ["mafft", "--localpair", "--maxiterate", "1000", "--thread", str(n_threads), "--inputorder", input_fasta] elif n_seqs <= 1000: cmd = ["mafft", "--auto", "--thread", str(n_threads), "--inputorder", input_fasta] else: cmd = ["mafft", "--fftns", "--thread", str(n_threads), "--inputorder", input_fasta] else: cmd = ["mafft", f"--{method}", "--thread", str(n_threads), "--inputorder", input_fasta] with open(output_fasta, 'w') as out: result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True) if result.returncode != 0: raise RuntimeError(f"MAFFT failed:\n{result.stderr[:500]}") print(f" Alignment complete → {output_fasta}") return output_fasta def run_iqtree(aligned_fasta: str, prefix: str, seq_type: str = "nt", bootstrap: int = 1000, n_threads: int = 4, outgroup: str = None) -> str: """Run IQ-TREE phylogenetic inference (IQ-TREE 2 or 3, whichever is installed).""" exe = find_iqtree() if exe is None: raise RuntimeError( "No IQ-TREE executable found on PATH " f"(looked for {', '.join(IQTREE_CANDIDATES)}). " "Install with: conda install -c bioconda iqtree" ) print(f"{exe}: Building maximum likelihood tree...") cmd = [ exe, "-s", aligned_fasta, "--prefix", prefix, "-m", "MFP", # ModelFinder Plus (IQ-TREE's default model search) "-B", str(bootstrap), # Ultrafast bootstrap "-T", str(n_threads), "--redo", "-alrt", "1000", # SH-aLRT test ] if outgroup: cmd += ["-o", outgroup] result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: raise RuntimeError(f"IQ-TREE failed:\n{result.stderr[:500]}") tree_file = f"{prefix}.treefile" # Extract best model from log log_file = f"{prefix}.log" if os.path.exists(log_file): with open(log_file) as f: for line in f: if "Best-fit model" in line: print(f" {line.strip()}") print(f" Tree saved → {tree_file}") return tree_file def run_fasttree(aligned_fasta: str, output_tree: str, seq_type: str = "nt") -> str: """Run FastTree (faster alternative for large datasets).""" print("FastTree: Building approximate ML tree (faster)...") if seq_type == "nt": cmd = ["FastTree", "-nt", "-gtr", "-gamma", aligned_fasta] else: cmd = ["FastTree", "-lg", "-gamma", aligned_fasta] with open(output_tree, 'w') as out: result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True) if result.returncode != 0: raise RuntimeError(f"FastTree failed:\n{result.stderr[:500]}") print(f" Tree saved → {output_tree}") return output_tree def visualize_tree(tree_file: str, output_png: str, outgroup: str = None) -> None: """Visualize the phylogenetic tree with ETE3.""" try: from ete3 import Tree, TreeStyle except ImportError: print("ETE3 not installed. Skipping visualization.") print(' Install: uv pip install "ete3==3.1.3"') return t = Tree(tree_file) # Root the tree if outgroup and outgroup in [leaf.name for leaf in t.get_leaves()]: t.set_outgroup(outgroup) print(f" Rooted at outgroup: {outgroup}") else: # Midpoint rooting t.set_outgroup(t.get_midpoint_outgroup()) print(" Applied midpoint rooting") # Style ts = TreeStyle() ts.show_leaf_name = True ts.show_branch_support = True ts.mode = "r" # rectangular try: t.render(output_png, tree_style=ts, w=800, units="px") print(f" Visualization saved → {output_png}") except Exception as e: print(f" Visualization failed (display issue?): {e}") # Save tree in Newick format as fallback rooted_nwk = output_png.replace(".png", "_rooted.nwk") t.write(format=1, outfile=rooted_nwk) print(f" Rooted tree saved → {rooted_nwk}") def tree_summary(tree_file: str) -> dict: """Print summary statistics for the tree.""" try: from ete3 import Tree t = Tree(tree_file) t.set_outgroup(t.get_midpoint_outgroup()) leaves = t.get_leaves() branch_lengths = [n.dist for n in t.traverse() if n.dist > 0] stats = { "n_taxa": len(leaves), "total_branch_length": sum(branch_lengths), "mean_branch_length": sum(branch_lengths) / len(branch_lengths) if branch_lengths else 0, "max_branch_length": max(branch_lengths) if branch_lengths else 0, } print("\nTree Summary:") for k, v in stats.items(): if isinstance(v, float): print(f" {k}: {v:.6f}") else: print(f" {k}: {v}") return stats except Exception as e: print(f"Could not compute tree stats: {e}") return {} def main(): parser = argparse.ArgumentParser(description="Phylogenetic analysis pipeline") parser.add_argument("input", help="Input FASTA file (unaligned)") parser.add_argument("--type", choices=["nt", "aa"], default="nt", help="Sequence type: nt (nucleotide) or aa (amino acid)") parser.add_argument("--threads", type=int, default=4, help="Number of threads") parser.add_argument("--bootstrap", type=int, default=1000, help="Bootstrap replicates for IQ-TREE") parser.add_argument("--fasttree", action="store_true", help="Use FastTree instead of IQ-TREE (faster, less accurate)") parser.add_argument("--outgroup", help="Outgroup taxon name for rooting") parser.add_argument("--mafft-method", default="auto", choices=["auto", "linsi", "einsi", "fftnsi", "fftns"], help="MAFFT alignment method") parser.add_argument("--output-dir", default="phylo_results", help="Output directory") args = parser.parse_args() check_dependencies(use_fasttree=args.fasttree) # Setup os.makedirs(args.output_dir, exist_ok=True) prefix = os.path.join(args.output_dir, Path(args.input).stem) print("=" * 60) print("Phylogenetic Analysis Pipeline") print("=" * 60) print(f"Input: {args.input}") print(f"Sequence type: {args.type}") print(f"Output dir: {args.output_dir}") # Step 1: Multiple Sequence Alignment print("\n[Step 1/3] Multiple Sequence Alignment (MAFFT)") aligned = run_mafft( args.input, f"{prefix}_aligned.fasta", n_threads=args.threads, method=args.mafft_method ) # Step 2: Tree Inference print("\n[Step 2/3] Tree Inference") if args.fasttree: tree_file = run_fasttree(aligned, f"{prefix}.tree", seq_type=args.type) else: tree_file = run_iqtree( aligned, prefix, seq_type=args.type, bootstrap=args.bootstrap, n_threads=args.threads, outgroup=args.outgroup ) # Step 3: Visualization print("\n[Step 3/3] Visualization (ETE3)") visualize_tree(tree_file, f"{prefix}_tree.png", outgroup=args.outgroup) tree_summary(tree_file) print("\n" + "=" * 60) print("Analysis complete!") print(f"Key outputs:") print(f" Aligned sequences: {aligned}") print(f" Tree file: {tree_file}") print(f" Visualization: {prefix}_tree.png") if __name__ == "__main__": main()
-
-
SKILL.md 16.6 KB
--- name: alterlab-phylogenetics description: Build phylogenetic trees end-to-end from raw sequences — MAFFT multiple sequence alignment, optional TrimAl trimming, IQ-TREE 3 maximum-likelihood inference with model selection and bootstraps, FastTree for large datasets, then visualize with ETE3 or FigTree. Use when reconstructing trees from sequences (FASTA) for evolutionary analysis, microbial genomics, viral phylodynamics, protein-family studies, or molecular-clock dating. For manipulating/comparing an EXISTING Newick tree (prune, root, Robinson-Foulds, duplication/speciation events) use alterlab-etetoolkit; for plain sequence parsing/translation use alterlab-biopython. Part of the AlterLab Academic Skills suite. license: MIT allowed-tools: Read Write Edit Bash(python:*) Bash(uv:*) compatibility: "Needs external CLI tools (MAFFT, IQ-TREE 3, FastTree; optionally TrimAl) on PATH — install via bioconda or Homebrew, NOT pip/uv. Python parts (ETE3 for visualization) run under `uv run python`. No API key or account required." metadata: skill-author: AlterLab version: "1.1.0" last_updated: "2026-09-23" --- # Phylogenetics ## Overview Phylogenetic analysis reconstructs the evolutionary history of biological sequences (genes, proteins, genomes) by inferring the branching pattern of descent. This skill covers the standard pipeline: 1. **MAFFT** — Multiple sequence alignment 2. **IQ-TREE 3** — Maximum likelihood tree inference with model selection 3. **FastTree** — Fast approximate maximum likelihood (for large datasets) 4. **ETE3** — Python library for tree manipulation and visualization **Installation:** The aligners and tree builders are compiled CLI tools (not on PyPI). Install the binaries via bioconda or Homebrew; install the Python visualization layer with uv. ```bash # CLI binaries — bioconda (cross-platform) ... conda install -c bioconda mafft iqtree fasttree trimal # iqtree is now IQ-TREE 3 # ... or Homebrew on macOS (Apple Silicon): IQ-TREE/TrimAl live in the brewsci/bio tap brew install mafft fasttree brew tap brewsci/bio && brew install brewsci/bio/iqtree brewsci/bio/trimal # Python visualization layer uv pip install "ete3==3.1.3" # also needs numpy<2 and PyQt5 for rendering ``` > **Binary name:** bioconda's `iqtree` package is now **IQ-TREE 3** (3.1.x) and installs the > executables `iqtree` and `iqtree3` — there is **no `iqtree2`** in it. Call `iqtree`, which > works on both the 2.x and 3.x packages; only reach for a versioned name when two releases > coexist on PATH. IQ-TREE 3 adds MixtureFinder, multi-tree mixture models (MAST), gene/site > concordance factors, and CMAPLE for very large pathogen datasets; the options below are > unchanged from 2.x. > > ETE3 (3.1.3) is the last ete3 release and can be fragile to install on Python ≥3.12 (pins old numpy/PyQt5). If `t.render()` fails, fall back to writing the Newick tree and viewing it in FigTree/iTOL, or use the maintained successor `ete4` (note: ete4 changed the `TreeStyle`/`render` API, so the snippets below are ete3-specific). ## When to Use This Skill Use phylogenetics when: - **Evolutionary relationships**: Which organism/gene is most closely related to my sequence? - **Viral phylodynamics**: Trace outbreak spread and estimate transmission dates - **Protein family analysis**: Infer evolutionary relationships within a gene family - **Horizontal gene transfer detection**: Identify genes with discordant species/gene trees - **Ancestral sequence reconstruction**: Infer ancestral protein sequences - **Molecular clock analysis**: Estimate divergence dates using temporal sampling - **GWAS companion**: Place variants in evolutionary context (e.g., SARS-CoV-2 variants) - **Microbiology**: Species phylogeny from 16S rRNA or core genome phylogeny ### Does NOT Trigger | Scenario | Use Instead | |----------|-------------| | Manipulating or comparing an **existing** Newick tree (prune, root, Robinson-Foulds, duplication/speciation) | `alterlab-etetoolkit` | | Plain sequence parsing, translation, or format conversion | `alterlab-biopython` | | Homology search to *find* the sequences before aligning them | `alterlab-blast` | | Diversity/ordination on a community feature table (UniFrac, PCoA, PERMANOVA) | `alterlab-scikit-bio` | | Running the 16S amplicon pipeline that produces the ASVs and tree | `alterlab-qiime2-amplicon` | ## Standard Workflow ### 1. Multiple Sequence Alignment with MAFFT ```python import subprocess import os def run_mafft(input_fasta: str, output_fasta: str, method: str = "auto", n_threads: int = 4) -> str: """ Align sequences with MAFFT. Args: input_fasta: Path to unaligned FASTA file output_fasta: Path for aligned output method: 'auto' (auto-select), 'einsi' (accurate), 'linsi' (accurate, slow), 'fftnsi' (medium), 'fftns' (fast), 'retree2' (fast) n_threads: Number of CPU threads Returns: Path to aligned FASTA file """ methods = { "auto": ["mafft", "--auto"], "einsi": ["mafft", "--genafpair", "--maxiterate", "1000"], "linsi": ["mafft", "--localpair", "--maxiterate", "1000"], "fftnsi": ["mafft", "--retree", "2", "--maxiterate", "2"], "fftns": ["mafft", "--retree", "2", "--maxiterate", "0"], "retree2": ["mafft", "--retree", "2"], } cmd = methods.get(method, methods["auto"]) cmd += ["--thread", str(n_threads), "--inputorder", input_fasta] with open(output_fasta, 'w') as out: result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True) if result.returncode != 0: raise RuntimeError(f"MAFFT failed:\n{result.stderr}") # Count aligned sequences with open(output_fasta) as f: n_seqs = sum(1 for line in f if line.startswith('>')) print(f"MAFFT: aligned {n_seqs} sequences → {output_fasta}") return output_fasta # MAFFT method selection guide: # Few sequences (<200), accurate: linsi or einsi # Many sequences (<1000), moderate: fftnsi # Large datasets (>1000): fftns or auto # Ultra-fast (>10000): mafft --retree 1 ``` ### 2. Trim Alignment (Optional but Recommended) ```python def trim_alignment_trimal(aligned_fasta: str, output_fasta: str, method: str = "automated1") -> str: """ Trim poorly aligned columns with TrimAl. Methods: - 'automated1': Automatic heuristic (recommended) - 'gappyout': Remove gappy columns - 'strict': Strict gap threshold """ cmd = ["trimal", f"-{method}", "-in", aligned_fasta, "-out", output_fasta, "-fasta"] result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: print(f"TrimAl warning: {result.stderr}") # Fall back to using the untrimmed alignment import shutil shutil.copy(aligned_fasta, output_fasta) return output_fasta ``` ### 3. IQ-TREE 3 — Maximum Likelihood Tree ```python def run_iqtree(aligned_fasta: str, output_prefix: str, model: str = "TEST", bootstrap: int = 1000, n_threads: int = 4, extra_args: list = None) -> dict: """ Build a maximum likelihood tree with IQ-TREE 3. Args: aligned_fasta: Aligned FASTA file output_prefix: Prefix for output files model: 'TEST' for automatic model selection, or specify (e.g., 'GTR+G' for DNA, 'LG+G4' for proteins, 'JTT+G' for proteins) bootstrap: Number of ultrafast bootstrap replicates (1000 recommended) n_threads: Number of threads ('AUTO' to auto-detect) extra_args: Additional IQ-TREE arguments Returns: Dict with paths to output files """ cmd = [ "iqtree", "-s", aligned_fasta, "--prefix", output_prefix, "-m", model, "-B", str(bootstrap), # Ultrafast bootstrap "-T", str(n_threads), "--redo" # Overwrite existing results ] if extra_args: cmd.extend(extra_args) result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: raise RuntimeError(f"IQ-TREE failed:\n{result.stderr}") # Print model selection result log_file = f"{output_prefix}.log" if os.path.exists(log_file): with open(log_file) as f: for line in f: if "Best-fit model" in line: print(f"IQ-TREE: {line.strip()}") output_files = { "tree": f"{output_prefix}.treefile", "log": f"{output_prefix}.log", "iqtree": f"{output_prefix}.iqtree", # Full report "model": f"{output_prefix}.model.gz", } print(f"IQ-TREE: Tree saved to {output_files['tree']}") return output_files # Model selection: ModelFinder Plus (-m MFP) is the DEFAULT — omitting -m runs it. # -m TEST is the narrower jModelTest/ProtTest-style search (no FreeRate models); # use it only when you need to match those tools. # Typical winners — DNA: GTR+F+I+G4, HKY+F+G4; Protein: LG+F+G4, Q.pfam+G4; # Codon: MG+F3X4. # For temporal (molecular clock) analysis, add: # extra_args = ["--date", "dates.txt", "--clock-test", "--date-CI", "95"] ``` ### 4. FastTree — Fast Approximate ML For large datasets (>1000 sequences) where IQ-TREE is too slow: ```python def run_fasttree(aligned_fasta: str, output_tree: str, sequence_type: str = "nt", model: str = "gtr", n_threads: int = 4) -> str: """ Build a fast approximate ML tree with FastTree. Args: sequence_type: 'nt' for nucleotide or 'aa' for amino acid model: For nt: 'gtr' (recommended) or 'jc'; for aa: 'lg', 'wag', 'jtt' """ if sequence_type == "nt": cmd = ["FastTree", "-nt", "-gtr"] else: cmd = ["FastTree", f"-{model}"] cmd += [aligned_fasta] with open(output_tree, 'w') as out: result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True) if result.returncode != 0: raise RuntimeError(f"FastTree failed:\n{result.stderr}") print(f"FastTree: Tree saved to {output_tree}") return output_tree ``` ### 5. Tree Analysis and Visualization with ETE3 ```python from ete3 import Tree, TreeStyle, NodeStyle, TextFace, PhyloTree import matplotlib.pyplot as plt def load_tree(tree_file: str) -> Tree: """Load a Newick tree file.""" t = Tree(tree_file) print(f"Tree: {len(t)} leaves, {len(list(t.traverse()))} nodes") return t def basic_tree_stats(t: Tree) -> dict: """Compute basic tree statistics.""" leaves = t.get_leaves() distances = [t.get_distance(l1, l2) for l1 in leaves[:min(50, len(leaves))] for l2 in leaves[:min(50, len(leaves))] if l1 != l2] stats = { "n_leaves": len(leaves), "n_internal_nodes": len(t) - len(leaves), "total_branch_length": sum(n.dist for n in t.traverse()), "max_leaf_distance": max(distances) if distances else 0, "mean_leaf_distance": sum(distances)/len(distances) if distances else 0, } return stats def find_mrca(t: Tree, leaf_names: list) -> Tree: """Find the most recent common ancestor of a set of leaves.""" return t.get_common_ancestor(*leaf_names) def visualize_tree(t: Tree, output_file: str = "tree.png", show_branch_support: bool = True, color_groups: dict = None, width: int = 800) -> None: """ Render phylogenetic tree to image. Args: t: ETE3 Tree object color_groups: Dict mapping leaf_name → color (for coloring taxa) show_branch_support: Show bootstrap values """ ts = TreeStyle() ts.show_leaf_name = True ts.show_branch_support = show_branch_support ts.mode = "r" # 'r' = rectangular, 'c' = circular if color_groups: for node in t.traverse(): if node.is_leaf() and node.name in color_groups: nstyle = NodeStyle() nstyle["fgcolor"] = color_groups[node.name] nstyle["size"] = 8 node.set_style(nstyle) t.render(output_file, tree_style=ts, w=width, units="px") print(f"Tree saved to: {output_file}") def midpoint_root(t: Tree) -> Tree: """Root tree at midpoint (use when outgroup unknown).""" t.set_outgroup(t.get_midpoint_outgroup()) return t def prune_tree(t: Tree, keep_leaves: list) -> Tree: """Prune tree to keep only specified leaves.""" t.prune(keep_leaves, preserve_branch_length=True) return t ``` ### 6. Complete Analysis Script ```python import subprocess, os from ete3 import Tree def full_phylogenetic_analysis( input_fasta: str, output_dir: str = "phylo_results", sequence_type: str = "nt", n_threads: int = 4, bootstrap: int = 1000, use_fasttree: bool = False ) -> dict: """ Complete phylogenetic pipeline: align → trim → tree → visualize. Args: input_fasta: Unaligned FASTA sequence_type: 'nt' (nucleotide) or 'aa' (amino acid/protein) use_fasttree: Use FastTree instead of IQ-TREE (faster for large datasets) """ os.makedirs(output_dir, exist_ok=True) prefix = os.path.join(output_dir, "phylo") print("=" * 50) print("Step 1: Multiple Sequence Alignment (MAFFT)") aligned = run_mafft(input_fasta, f"{prefix}_aligned.fasta", method="auto", n_threads=n_threads) print("\nStep 2: Tree Inference") if use_fasttree: tree_file = run_fasttree( aligned, f"{prefix}.tree", sequence_type=sequence_type, model="gtr" if sequence_type == "nt" else "lg" ) else: # -m TEST auto-detects the alphabet (nt vs aa) and selects the best model. iqtree_files = run_iqtree( aligned, prefix, model="TEST", bootstrap=bootstrap, n_threads=n_threads ) tree_file = iqtree_files["tree"] print("\nStep 3: Tree Analysis") t = Tree(tree_file) t = midpoint_root(t) stats = basic_tree_stats(t) print(f"Tree statistics: {stats}") print("\nStep 4: Visualization") visualize_tree(t, f"{prefix}_tree.png", show_branch_support=True) # Save rooted tree rooted_tree_file = f"{prefix}_rooted.nwk" t.write(format=1, outfile=rooted_tree_file) results = { "aligned_fasta": aligned, "tree_file": tree_file, "rooted_tree": rooted_tree_file, "visualization": f"{prefix}_tree.png", "stats": stats } print("\n" + "=" * 50) print("Phylogenetic analysis complete!") print(f"Results in: {output_dir}/") return results ``` ## IQ-TREE Model Guide ### DNA Models | Model | Description | Use case | |-------|-------------|---------| | `GTR+G4` | General Time Reversible + Gamma | Most flexible DNA model | | `HKY+G4` | Hasegawa-Kishino-Yano + Gamma | Two-rate model (common) | | `TrN+G4` | Tamura-Nei | Unequal transitions | | `JC` | Jukes-Cantor | Simplest; all rates equal | ### Protein Models | Model | Description | Use case | |-------|-------------|---------| | `LG+G4` | Le-Gascuel + Gamma | Best average protein model | | `WAG+G4` | Whelan-Goldman | Widely used | | `JTT+G4` | Jones-Taylor-Thornton | Classical model | | `Q.pfam+G4` | Pfam-trained (QMaker) | General protein families | | `Q.bird+G4` | Bird clade-specific (QMaker) | Bird proteins; siblings: Q.mammal, Q.insect, Q.yeast, Q.plant | **Tip:** IQ-TREE selects the model automatically by default (`-m MFP`); pass an explicit model only when you need to fix it. ## Best Practices - **Alignment quality first**: Poor alignment → unreliable trees; check alignment manually - **Use `linsi` for small (<200 seq), `fftns` or `auto` for large alignments** - **Model selection**: ModelFinder Plus is IQ-TREE's default — just omit `-m` (or pass `-m MFP`). `-m TEST` restricts the search to jModelTest/ProtTest-style models, so use it only to match those tools - **Bootstrap**: Use ≥1000 ultrafast bootstraps (`-B 1000`) for branch support - **Root the tree**: Unrooted trees can be misleading; use outgroup or midpoint rooting - **FastTree for >5000 sequences**: IQ-TREE becomes slow; FastTree is 10–100× faster - **Trim long alignments**: TrimAl removes unreliable columns; improves tree accuracy - **Check for recombination** in viral/bacterial sequences before building trees (`RDP4`, `GARD`) ## Additional Resources - **MAFFT**: https://mafft.cbrc.jp/alignment/software/ - **IQ-TREE 3**: http://www.iqtree.org/ | Tutorial: https://www.iqtree.org/workshop/molevol2022 - **FastTree**: http://www.microbesonline.org/fasttree/ - **ETE3**: http://etetoolkit.org/ - **FigTree** (GUI visualization): https://tree.bio.ed.ac.uk/software/figtree/ - **iTOL** (web visualization): https://itol.embl.de/ - **MUSCLE** (alternative aligner): https://www.drive5.com/muscle/ - **TrimAl** (alignment trimming): https://vicfero.github.io/trimal/
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.