Phylogenetics

Build and analyze phylogenetic trees using MAFFT (multiple alignment), IQ-TREE 2 (maximum likelihood), and FastTree (fast NJ/ML).

How to use it

  1. Hit Copy SKILL.md — or use the Claude Code line below to get every file.
  2. Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
    ChatGPT: make a Project and paste it into Instructions.
    Neither? Paste it at the top of a new chat — it works for that chat.
  3. Describe your job in plain words. The AI follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit K-Dense-AI/scientific-agent-skills/skills/phylogenetics#main ~/.claude/skills/phylogenetics

For one project only, change the path to .claude/skills/phylogenetics. This skill also uses dates.txt — copying SKILL.md alone won't be enough. See the folder on GitHub.

Not working?
  • Check which app you pasted it into — the steps above name the right one.
  • Some skills need the paid tier of Claude or ChatGPT.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Show the full text410 lines
phylogenetics/SKILL.md410 lines13.8 KBpushed 52d agoRawView on GitHub

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 2 — 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:

# Conda (recommended for CLI tools)
conda install -c bioconda mafft iqtree fasttree
uv pip install ete3

# ete3's TreeStyle/NodeStyle rendering lives in its Qt backend, so image output
# needs PyQt5 as well; tree parsing and statistics work without it.
uv pip install PyQt5

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

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", "--fftnsi"],
        "fftns": ["mafft", "--fftns"],
        "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 2 — 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 2.

    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 = [
        "iqtree2",
        "-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

# IQ-TREE model selection guide:
# DNA:     TEST → GTR+G, HKY+G, TrN+G
# Protein: TEST → LG+G4, WAG+G, JTT+G, Q.pfam+G
# Codon:   TEST → 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:
        model = "TEST" if sequence_type == "nt" else "TEST"
        iqtree_files = run_iqtree(
            aligned, prefix,
            model=model,
            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 For Pfam-like protein families
Q.bird+G4 Bird-specific Vertebrate proteins

Tip: Use -m TEST to let IQ-TREE automatically select the best model.

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: Always use -m TEST for IQ-TREE unless you have a specific reason
  • 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

1---
2name: phylogenetics
3description: Build and analyze phylogenetic trees using MAFFT (multiple alignment), IQ-TREE 2 (maximum likelihood), and FastTree (fast NJ/ML). Visualize with ETE3 or FigTree. For evolutionary analysis, microbial genomics, viral phylodynamics, protein family analysis, and molecular clock studies.
4license: Unknown
5metadata:
6 version: "1.2"
7 skill-author: Kuan-lin Huang
8---
9 
10# Phylogenetics
11 
12## Overview
13 
14Phylogenetic analysis reconstructs the evolutionary history of biological sequences (genes, proteins, genomes) by inferring the branching pattern of descent. This skill covers the standard pipeline:
15 
161. **MAFFT** — Multiple sequence alignment
172. **IQ-TREE 2** — Maximum likelihood tree inference with model selection
183. **FastTree** — Fast approximate maximum likelihood (for large datasets)
194. **ETE3** — Python library for tree manipulation and visualization
20 
21**Installation:**
22```bash
23# Conda (recommended for CLI tools)
24conda install -c bioconda mafft iqtree fasttree
25uv pip install ete3
26 
27# ete3's TreeStyle/NodeStyle rendering lives in its Qt backend, so image output
28# needs PyQt5 as well; tree parsing and statistics work without it.
29uv pip install PyQt5
30```
31 
32## When to Use This Skill
33 
34Use phylogenetics when:
35 
36- **Evolutionary relationships**: Which organism/gene is most closely related to my sequence?
37- **Viral phylodynamics**: Trace outbreak spread and estimate transmission dates
38- **Protein family analysis**: Infer evolutionary relationships within a gene family
39- **Horizontal gene transfer detection**: Identify genes with discordant species/gene trees
40- **Ancestral sequence reconstruction**: Infer ancestral protein sequences
41- **Molecular clock analysis**: Estimate divergence dates using temporal sampling
42- **GWAS companion**: Place variants in evolutionary context (e.g., SARS-CoV-2 variants)
43- **Microbiology**: Species phylogeny from 16S rRNA or core genome phylogeny
44 
45## Standard Workflow
46 
47### 1. Multiple Sequence Alignment with MAFFT
48 
49```python
50import subprocess
51import os
52 
53def run_mafft(input_fasta: str, output_fasta: str, method: str = "auto",
54 n_threads: int = 4) -> str:
55 """
56 Align sequences with MAFFT.
57 
58 Args:
59 input_fasta: Path to unaligned FASTA file
60 output_fasta: Path for aligned output
61 method: 'auto' (auto-select), 'einsi' (accurate), 'linsi' (accurate, slow),
62 'fftnsi' (medium), 'fftns' (fast), 'retree2' (fast)
63 n_threads: Number of CPU threads
64 
65 Returns:
66 Path to aligned FASTA file
67 """
68 methods = {
69 "auto": ["mafft", "--auto"],
70 "einsi": ["mafft", "--genafpair", "--maxiterate", "1000"],
71 "linsi": ["mafft", "--localpair", "--maxiterate", "1000"],
72 "fftnsi": ["mafft", "--fftnsi"],
73 "fftns": ["mafft", "--fftns"],
74 "retree2": ["mafft", "--retree", "2"],
75 }
76 
77 cmd = methods.get(method, methods["auto"])
78 cmd += ["--thread", str(n_threads), "--inputorder", input_fasta]
79 
80 with open(output_fasta, 'w') as out:
81 result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True)
82 
83 if result.returncode != 0:
84 raise RuntimeError(f"MAFFT failed:\n{result.stderr}")
85 
86 # Count aligned sequences
87 with open(output_fasta) as f:
88 n_seqs = sum(1 for line in f if line.startswith('>'))
89 print(f"MAFFT: aligned {n_seqs} sequences → {output_fasta}")
90 
91 return output_fasta
92 
93# MAFFT method selection guide:
94# Few sequences (<200), accurate: linsi or einsi
95# Many sequences (<1000), moderate: fftnsi
96# Large datasets (>1000): fftns or auto
97# Ultra-fast (>10000): mafft --retree 1
98```
99 
100### 2. Trim Alignment (Optional but Recommended)
101 
102```python
103def trim_alignment_trimal(aligned_fasta: str, output_fasta: str,
104 method: str = "automated1") -> str:
105 """
106 Trim poorly aligned columns with TrimAl.
107 
108 Methods:
109 - 'automated1': Automatic heuristic (recommended)
110 - 'gappyout': Remove gappy columns
111 - 'strict': Strict gap threshold
112 """
113 cmd = ["trimal", f"-{method}", "-in", aligned_fasta, "-out", output_fasta, "-fasta"]
114 result = subprocess.run(cmd, capture_output=True, text=True)
115 if result.returncode != 0:
116 print(f"TrimAl warning: {result.stderr}")
117 # Fall back to using the untrimmed alignment
118 import shutil
119 shutil.copy(aligned_fasta, output_fasta)
120 return output_fasta
121```
122 
123### 3. IQ-TREE 2 — Maximum Likelihood Tree
124 
125```python
126def run_iqtree(aligned_fasta: str, output_prefix: str,
127 model: str = "TEST", bootstrap: int = 1000,
128 n_threads: int = 4, extra_args: list = None) -> dict:
129 """
130 Build a maximum likelihood tree with IQ-TREE 2.
131 
132 Args:
133 aligned_fasta: Aligned FASTA file
134 output_prefix: Prefix for output files
135 model: 'TEST' for automatic model selection, or specify (e.g., 'GTR+G' for DNA,
136 'LG+G4' for proteins, 'JTT+G' for proteins)
137 bootstrap: Number of ultrafast bootstrap replicates (1000 recommended)
138 n_threads: Number of threads ('AUTO' to auto-detect)
139 extra_args: Additional IQ-TREE arguments
140 
141 Returns:
142 Dict with paths to output files
143 """
144 cmd = [
145 "iqtree2",
146 "-s", aligned_fasta,
147 "--prefix", output_prefix,
148 "-m", model,
149 "-B", str(bootstrap), # Ultrafast bootstrap
150 "-T", str(n_threads),
151 "--redo" # Overwrite existing results
152 ]
153 
154 if extra_args:
155 cmd.extend(extra_args)
156 
157 result = subprocess.run(cmd, capture_output=True, text=True)
158 
159 if result.returncode != 0:
160 raise RuntimeError(f"IQ-TREE failed:\n{result.stderr}")
161 
162 # Print model selection result
163 log_file = f"{output_prefix}.log"
164 if os.path.exists(log_file):
165 with open(log_file) as f:
166 for line in f:
167 if "Best-fit model" in line:
168 print(f"IQ-TREE: {line.strip()}")
169 
170 output_files = {
171 "tree": f"{output_prefix}.treefile",
172 "log": f"{output_prefix}.log",
173 "iqtree": f"{output_prefix}.iqtree", # Full report
174 "model": f"{output_prefix}.model.gz",
175 }
176 
177 print(f"IQ-TREE: Tree saved to {output_files['tree']}")
178 return output_files
179 
180# IQ-TREE model selection guide:
181# DNA: TEST → GTR+G, HKY+G, TrN+G
182# Protein: TEST → LG+G4, WAG+G, JTT+G, Q.pfam+G
183# Codon: TEST → MG+F3X4
184 
185# For temporal (molecular clock) analysis, add:
186# extra_args = ["--date", "dates.txt", "--clock-test", "--date-CI", "95"]
187```
188 
189### 4. FastTree — Fast Approximate ML
190 
191For large datasets (>1000 sequences) where IQ-TREE is too slow:
192 
193```python
194def run_fasttree(aligned_fasta: str, output_tree: str,
195 sequence_type: str = "nt", model: str = "gtr",
196 n_threads: int = 4) -> str:
197 """
198 Build a fast approximate ML tree with FastTree.
199 
200 Args:
201 sequence_type: 'nt' for nucleotide or 'aa' for amino acid
202 model: For nt: 'gtr' (recommended) or 'jc'; for aa: 'lg', 'wag', 'jtt'
203 """
204 if sequence_type == "nt":
205 cmd = ["FastTree", "-nt", "-gtr"]
206 else:
207 cmd = ["FastTree", f"-{model}"]
208 
209 cmd += [aligned_fasta]
210 
211 with open(output_tree, 'w') as out:
212 result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True)
213 
214 if result.returncode != 0:
215 raise RuntimeError(f"FastTree failed:\n{result.stderr}")
216 
217 print(f"FastTree: Tree saved to {output_tree}")
218 return output_tree
219```
220 
221### 5. Tree Analysis and Visualization with ETE3
222 
223```python
224from ete3 import Tree, TreeStyle, NodeStyle, TextFace, PhyloTree
225import matplotlib.pyplot as plt
226 
227def load_tree(tree_file: str) -> Tree:
228 """Load a Newick tree file."""
229 t = Tree(tree_file)
230 print(f"Tree: {len(t)} leaves, {len(list(t.traverse()))} nodes")
231 return t
232 
233def basic_tree_stats(t: Tree) -> dict:
234 """Compute basic tree statistics."""
235 leaves = t.get_leaves()
236 distances = [t.get_distance(l1, l2) for l1 in leaves[:min(50, len(leaves))]
237 for l2 in leaves[:min(50, len(leaves))] if l1 != l2]
238 
239 stats = {
240 "n_leaves": len(leaves),
241 "n_internal_nodes": len(t) - len(leaves),
242 "total_branch_length": sum(n.dist for n in t.traverse()),
243 "max_leaf_distance": max(distances) if distances else 0,
244 "mean_leaf_distance": sum(distances)/len(distances) if distances else 0,
245 }
246 return stats
247 
248def find_mrca(t: Tree, leaf_names: list) -> Tree:
249 """Find the most recent common ancestor of a set of leaves."""
250 return t.get_common_ancestor(*leaf_names)
251 
252def visualize_tree(t: Tree, output_file: str = "tree.png",
253 show_branch_support: bool = True,
254 color_groups: dict = None,
255 width: int = 800) -> None:
256 """
257 Render phylogenetic tree to image.
258 
259 Args:
260 t: ETE3 Tree object
261 color_groups: Dict mapping leaf_name → color (for coloring taxa)
262 show_branch_support: Show bootstrap values
263 """
264 ts = TreeStyle()
265 ts.show_leaf_name = True
266 ts.show_branch_support = show_branch_support
267 ts.mode = "r" # 'r' = rectangular, 'c' = circular
268 
269 if color_groups:
270 for node in t.traverse():
271 if node.is_leaf() and node.name in color_groups:
272 nstyle = NodeStyle()
273 nstyle["fgcolor"] = color_groups[node.name]
274 nstyle["size"] = 8
275 node.set_style(nstyle)
276 
277 t.render(output_file, tree_style=ts, w=width, units="px")
278 print(f"Tree saved to: {output_file}")
279 
280def midpoint_root(t: Tree) -> Tree:
281 """Root tree at midpoint (use when outgroup unknown)."""
282 t.set_outgroup(t.get_midpoint_outgroup())
283 return t
284 
285def prune_tree(t: Tree, keep_leaves: list) -> Tree:
286 """Prune tree to keep only specified leaves."""
287 t.prune(keep_leaves, preserve_branch_length=True)
288 return t
289```
290 
291### 6. Complete Analysis Script
292 
293```python
294import subprocess, os
295from ete3 import Tree
296 
297def full_phylogenetic_analysis(
298 input_fasta: str,
299 output_dir: str = "phylo_results",
300 sequence_type: str = "nt",
301 n_threads: int = 4,
302 bootstrap: int = 1000,
303 use_fasttree: bool = False
304) -> dict:
305 """
306 Complete phylogenetic pipeline: align → trim → tree → visualize.
307 
308 Args:
309 input_fasta: Unaligned FASTA
310 sequence_type: 'nt' (nucleotide) or 'aa' (amino acid/protein)
311 use_fasttree: Use FastTree instead of IQ-TREE (faster for large datasets)
312 """
313 os.makedirs(output_dir, exist_ok=True)
314 prefix = os.path.join(output_dir, "phylo")
315 
316 print("=" * 50)
317 print("Step 1: Multiple Sequence Alignment (MAFFT)")
318 aligned = run_mafft(input_fasta, f"{prefix}_aligned.fasta",
319 method="auto", n_threads=n_threads)
320 
321 print("\nStep 2: Tree Inference")
322 if use_fasttree:
323 tree_file = run_fasttree(
324 aligned, f"{prefix}.tree",
325 sequence_type=sequence_type,
326 model="gtr" if sequence_type == "nt" else "lg"
327 )
328 else:
329 model = "TEST" if sequence_type == "nt" else "TEST"
330 iqtree_files = run_iqtree(
331 aligned, prefix,
332 model=model,
333 bootstrap=bootstrap,
334 n_threads=n_threads
335 )
336 tree_file = iqtree_files["tree"]
337 
338 print("\nStep 3: Tree Analysis")
339 t = Tree(tree_file)
340 t = midpoint_root(t)
341 
342 stats = basic_tree_stats(t)
343 print(f"Tree statistics: {stats}")
344 
345 print("\nStep 4: Visualization")
346 visualize_tree(t, f"{prefix}_tree.png", show_branch_support=True)
347 
348 # Save rooted tree
349 rooted_tree_file = f"{prefix}_rooted.nwk"
350 t.write(format=1, outfile=rooted_tree_file)
351 
352 results = {
353 "aligned_fasta": aligned,
354 "tree_file": tree_file,
355 "rooted_tree": rooted_tree_file,
356 "visualization": f"{prefix}_tree.png",
357 "stats": stats
358 }
359 
360 print("\n" + "=" * 50)
361 print("Phylogenetic analysis complete!")
362 print(f"Results in: {output_dir}/")
363 return results
364```
365 
366## IQ-TREE Model Guide
367 
368### DNA Models
369 
370| Model | Description | Use case |
371|-------|-------------|---------|
372| `GTR+G4` | General Time Reversible + Gamma | Most flexible DNA model |
373| `HKY+G4` | Hasegawa-Kishino-Yano + Gamma | Two-rate model (common) |
374| `TrN+G4` | Tamura-Nei | Unequal transitions |
375| `JC` | Jukes-Cantor | Simplest; all rates equal |
376 
377### Protein Models
378 
379| Model | Description | Use case |
380|-------|-------------|---------|
381| `LG+G4` | Le-Gascuel + Gamma | Best average protein model |
382| `WAG+G4` | Whelan-Goldman | Widely used |
383| `JTT+G4` | Jones-Taylor-Thornton | Classical model |
384| `Q.pfam+G4` | pfam-trained | For Pfam-like protein families |
385| `Q.bird+G4` | Bird-specific | Vertebrate proteins |
386 
387**Tip:** Use `-m TEST` to let IQ-TREE automatically select the best model.
388 
389## Best Practices
390 
391- **Alignment quality first**: Poor alignment → unreliable trees; check alignment manually
392- **Use `linsi` for small (<200 seq), `fftns` or `auto` for large alignments**
393- **Model selection**: Always use `-m TEST` for IQ-TREE unless you have a specific reason
394- **Bootstrap**: Use ≥1000 ultrafast bootstraps (`-B 1000`) for branch support
395- **Root the tree**: Unrooted trees can be misleading; use outgroup or midpoint rooting
396- **FastTree for >5000 sequences**: IQ-TREE becomes slow; FastTree is 10–100× faster
397- **Trim long alignments**: TrimAl removes unreliable columns; improves tree accuracy
398- **Check for recombination** in viral/bacterial sequences before building trees (`RDP4`, `GARD`)
399 
400## Additional Resources
401 
402- **MAFFT**: https://mafft.cbrc.jp/alignment/software/
403- **IQ-TREE 2**: http://www.iqtree.org/ | Tutorial: https://www.iqtree.org/workshop/molevol2022
404- **FastTree**: http://www.microbesonline.org/fasttree/
405- **ETE3**: http://etetoolkit.org/
406- **FigTree** (GUI visualization): https://tree.bio.ed.ac.uk/software/figtree/
407- **iTOL** (web visualization): https://itol.embl.de/
408- **MUSCLE** (alternative aligner): https://www.drive5.com/muscle/
409- **TrimAl** (alignment trimming): https://vicfero.github.io/trimal/
410 

Discussion

Alternatives

Also in Genomics & omics
AnndataData structure for annotated matrices in single-cell analysis. Use when working with .h5ad files or integrating with the scverse ecosystem. This is the data format skill—for analysis workflows use scanpy; for probabilistic models use scvi-tools; for population-scale queries use cellxgene-census.Science · MITArboretoInfer gene regulatory networks (GRNs) from gene expression data using scalable algorithms (GRNBoost2, GENIE3). Use when analyzing transcriptomics data (bulk RNA-seq, single-cell RNA-seq) to identify transcription factor-target gene relationships and regulatory interactions. Supports distributed computation for large-scale datasets.Science · MITBiopython: Computational Molecular Biology in PythonComprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.Science · MITBulk rnaseqEnd-to-end bulk RNA-seq orchestrator — takes raw FASTQ reads through QC and trimming (FastQC, fastp/Trim Galore), alignment and quantification (STAR, Salmon, featureCounts), assembles a gene-level counts matrix, then hands off to differential expression (pydeseq2), pathway/GSEA enrichment (pathway-enrichment), and publication figures (scientific-visualization). Use whenever the user has bulk RNA-seq reads or quant output and wants a complete, reproducible differential-expression workflow — e.g. "analyze my RNA-seq", "FASTQ to DESeq2", "run nf-core/rnaseq", "STAR/Salmon quantification", "build a counts matrix for DESeq2", or "go from reads to differentially expressed genes and enriched pathways". Routes between an nf-core/rnaseq (Nextflow) path and a standalone STAR/Salmon path, and covers experimental design, strandedness, and QC gates. For single-cell RNA-seq use the scanpy skill instead.Science · MIT