$npx -y skills add LeonChaoX/qinyan-academic-skills --skill phylogeneticsBuild 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 studi
| 1 | # Phylogenetics |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | 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: |
| 6 | |
| 7 | 1. **MAFFT** — Multiple sequence alignment |
| 8 | 2. **IQ-TREE 2** — Maximum likelihood tree inference with model selection |
| 9 | 3. **FastTree** — Fast approximate maximum likelihood (for large datasets) |
| 10 | 4. **ETE3** — Python library for tree manipulation and visualization |
| 11 | |
| 12 | **Installation:** |
| 13 | ```bash |
| 14 | # Conda (recommended for CLI tools) |
| 15 | conda install -c bioconda mafft iqtree fasttree |
| 16 | pip install ete3 |
| 17 | ``` |
| 18 | |
| 19 | ## When to Use This Skill |
| 20 | |
| 21 | Use phylogenetics when: |
| 22 | |
| 23 | - **Evolutionary relationships**: Which organism/gene is most closely related to my sequence? |
| 24 | - **Viral phylodynamics**: Trace outbreak spread and estimate transmission dates |
| 25 | - **Protein family analysis**: Infer evolutionary relationships within a gene family |
| 26 | - **Horizontal gene transfer detection**: Identify genes with discordant species/gene trees |
| 27 | - **Ancestral sequence reconstruction**: Infer ancestral protein sequences |
| 28 | - **Molecular clock analysis**: Estimate divergence dates using temporal sampling |
| 29 | - **GWAS companion**: Place variants in evolutionary context (e.g., SARS-CoV-2 variants) |
| 30 | - **Microbiology**: Species phylogeny from 16S rRNA or core genome phylogeny |
| 31 | |
| 32 | ## Standard Workflow |
| 33 | |
| 34 | ### 1. Multiple Sequence Alignment with MAFFT |
| 35 | |
| 36 | ```python |
| 37 | import subprocess |
| 38 | import os |
| 39 | |
| 40 | def run_mafft(input_fasta: str, output_fasta: str, method: str = "auto", |
| 41 | n_threads: int = 4) -> str: |
| 42 | """ |
| 43 | Align sequences with MAFFT. |
| 44 | |
| 45 | Args: |
| 46 | input_fasta: Path to unaligned FASTA file |
| 47 | output_fasta: Path for aligned output |
| 48 | method: 'auto' (auto-select), 'einsi' (accurate), 'linsi' (accurate, slow), |
| 49 | 'fftnsi' (medium), 'fftns' (fast), 'retree2' (fast) |
| 50 | n_threads: Number of CPU threads |
| 51 | |
| 52 | Returns: |
| 53 | Path to aligned FASTA file |
| 54 | """ |
| 55 | methods = { |
| 56 | "auto": ["mafft", "--auto"], |
| 57 | "einsi": ["mafft", "--genafpair", "--maxiterate", "1000"], |
| 58 | "linsi": ["mafft", "--localpair", "--maxiterate", "1000"], |
| 59 | "fftnsi": ["mafft", "--fftnsi"], |
| 60 | "fftns": ["mafft", "--fftns"], |
| 61 | "retree2": ["mafft", "--retree", "2"], |
| 62 | } |
| 63 | |
| 64 | cmd = methods.get(method, methods["auto"]) |
| 65 | cmd += ["--thread", str(n_threads), "--inputorder", input_fasta] |
| 66 | |
| 67 | with open(output_fasta, 'w') as out: |
| 68 | result = subprocess.run(cmd, stdout=out, stderr=subprocess.PIPE, text=True) |
| 69 | |
| 70 | if result.returncode != 0: |
| 71 | raise RuntimeError(f"MAFFT failed:\n{result.stderr}") |
| 72 | |
| 73 | # Count aligned sequences |
| 74 | with open(output_fasta) as f: |
| 75 | n_seqs = sum(1 for line in f if line.startswith('>')) |
| 76 | print(f"MAFFT: aligned {n_seqs} sequences → {output_fasta}") |
| 77 | |
| 78 | return output_fasta |
| 79 | |
| 80 | # MAFFT method selection guide: |
| 81 | # Few sequences (<200), accurate: linsi or einsi |
| 82 | # Many sequences (<1000), moderate: fftnsi |
| 83 | # Large datasets (>1000): fftns or auto |
| 84 | # Ultra-fast (>10000): mafft --retree 1 |
| 85 | ``` |
| 86 | |
| 87 | ### 2. Trim Alignment (Optional but Recommended) |
| 88 | |
| 89 | ```python |
| 90 | def trim_alignment_trimal(aligned_fasta: str, output_fasta: str, |
| 91 | method: str = "automated1") -> str: |
| 92 | """ |
| 93 | Trim poorly aligned columns with TrimAl. |
| 94 | |
| 95 | Methods: |
| 96 | - 'automated1': Automatic heuristic (recommended) |
| 97 | - 'gappyout': Remove gappy columns |
| 98 | - 'strict': Strict gap threshold |
| 99 | """ |
| 100 | cmd = ["trimal", f"-{method}", "-in", aligned_fasta, "-out", output_fasta, "-fasta"] |
| 101 | result = subprocess.run(cmd, capture_output=True, text=True) |
| 102 | if result.returncode != 0: |
| 103 | print(f"TrimAl warning: {result.stderr}") |
| 104 | # Fall back to using the untrimmed alignment |
| 105 | import shutil |
| 106 | shutil.copy(aligned_fasta, output_fasta) |
| 107 | return output_fasta |
| 108 | ``` |
| 109 | |
| 110 | ### 3. IQ-TREE 2 — Maximum Likelihood Tree |
| 111 | |
| 112 | ```python |
| 113 | def run_iqtree(aligned_fasta: str, output_prefix: str, |
| 114 | model: str = "TEST", bootstrap: int = 1000, |
| 115 | n_threads: int = 4, extra_args: list = None) -> dict: |
| 116 | """ |
| 117 | Build a maximum likelihood tree with IQ-TREE 2. |
| 118 | |
| 119 | Args: |
| 120 | aligned_fasta: Aligned FASTA file |
| 121 | output_prefix: Prefix for output files |
| 122 | model: 'TEST' for automatic model selection, or specify (e.g., 'GTR+G' for DNA, |
| 123 | 'LG+G4' for proteins, 'JTT+G' for proteins) |
| 124 | bootstrap: Number of ultrafast bootstrap replicates (1000 recommended) |
| 125 | n_threads: Number of threads ('AUTO' to auto-detect) |
| 126 | extra_args: Additional IQ-TREE arguments |
| 127 | |
| 128 | Returns: |
| 129 | Dict with paths to |