|
| 1 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
| 3 | + |
| 4 | += Cladistics.jl |
| 5 | +:toc: preamble |
| 6 | +:icons: font |
| 7 | + |
| 8 | +image:https://img.shields.io/badge/Project-Topology-9558B2[Project Topology,link="TOPOLOGY.md"] |
| 9 | +image:https://img.shields.io/badge/Completion-75%25-yellow[Completion Status,link="TOPOLOGY.md"] |
| 10 | +image:https://img.shields.io/badge/license-PMPL--1.0--or--later-blue.svg[License,link="LICENSE"] |
| 11 | +image:https://img.shields.io/badge/julia-1.6+-purple.svg[Julia,link="https://julialang.org"] |
| 12 | + |
| 13 | +A Julia package for phylogenetic analysis and cladistics — the study of |
| 14 | +evolutionary relationships among organisms. |
| 15 | + |
| 16 | +== Overview |
| 17 | + |
| 18 | +Cladistics is a method of biological classification that groups organisms based |
| 19 | +on their evolutionary ancestry and shared derived characteristics (synapomorphies). |
| 20 | +This package provides computational tools for reconstructing and analyzing |
| 21 | +phylogenetic trees from molecular sequence data and morphological characters. |
| 22 | + |
| 23 | +=== What is Cladistics? |
| 24 | + |
| 25 | +* *Clade*: A monophyletic group consisting of an ancestor and all its descendants |
| 26 | +* *Synapomorphy*: A shared derived characteristic that defines a clade |
| 27 | +* *Phylogenetic Tree*: A branching diagram showing evolutionary relationships |
| 28 | +* *Parsimony*: The principle that the simplest evolutionary explanation (fewest changes) is preferred |
| 29 | +* *Bootstrap Analysis*: Statistical method to assess confidence in tree topology |
| 30 | + |
| 31 | +== Features |
| 32 | + |
| 33 | +=== Distance-Based Methods |
| 34 | + |
| 35 | +* *Multiple Distance Metrics*: Hamming, p-distance, Jukes-Cantor 1969, Kimura 2-parameter |
| 36 | +* *UPGMA*: Unweighted Pair Group Method with Arithmetic Mean (assumes molecular clock) |
| 37 | +* *Neighbor-Joining*: Does not assume molecular clock, handles rate variation |
| 38 | + |
| 39 | +=== Character-Based Methods |
| 40 | + |
| 41 | +* *Maximum Parsimony*: Find trees requiring fewest evolutionary changes |
| 42 | +* *Fitch Algorithm*: Efficient parsimony score calculation |
| 43 | +* *Parsimony-Informative Sites*: Identify characters useful for phylogenetic inference |
| 44 | + |
| 45 | +=== Tree Analysis |
| 46 | + |
| 47 | +* *Bootstrap Support*: Assess confidence in tree topology (1000+ replicates) |
| 48 | +* *Clade Identification*: Extract well-supported monophyletic groups |
| 49 | +* *Robinson-Foulds Distance*: Compare tree topologies quantitatively |
| 50 | +* *Tree Rooting*: Root unrooted trees using outgroup taxa |
| 51 | +* *Newick Format*: Export trees in standard phylogenetic format |
| 52 | + |
| 53 | +== Installation |
| 54 | + |
| 55 | +[source,julia] |
| 56 | +---- |
| 57 | +using Pkg |
| 58 | +Pkg.add("Cladistics") |
| 59 | +---- |
| 60 | + |
| 61 | +== Quick Start |
| 62 | + |
| 63 | +[source,julia] |
| 64 | +---- |
| 65 | +using Cladistics |
| 66 | +using Plots |
| 67 | +
|
| 68 | +# 1. DNA sequence data (aligned) |
| 69 | +sequences = [ |
| 70 | + "ATCGATCGATCG", # Species A |
| 71 | + "ATCGATCGATCG", # Species B (same as A) |
| 72 | + "ATCGTTCGATCG", # Species C |
| 73 | + "TTCGATCGATCG", # Species D |
| 74 | + "TTCGTTCGATCC" # Species E (outgroup) |
| 75 | +] |
| 76 | +taxa_names = ["Species_A", "Species_B", "Species_C", "Species_D", "Species_E"] |
| 77 | +
|
| 78 | +# 2. Calculate evolutionary distances |
| 79 | +dmat = distance_matrix(sequences, method=:jc69) |
| 80 | +
|
| 81 | +# 3. Build phylogenetic tree using UPGMA |
| 82 | +upgma_tree = upgma(dmat, taxa_names=taxa_names) |
| 83 | +newick = tree_to_newick(upgma_tree) |
| 84 | +
|
| 85 | +# 4. Build tree using Neighbor-Joining |
| 86 | +nj_tree = neighbor_joining(dmat, taxa_names=taxa_names) |
| 87 | +
|
| 88 | +# 5. Compare tree topologies |
| 89 | +rf_distance = tree_distance(upgma_tree, nj_tree) |
| 90 | +
|
| 91 | +# 6. Bootstrap analysis for confidence |
| 92 | +support = bootstrap_support(sequences, replicates=1000, method=:nj) |
| 93 | +
|
| 94 | +# 7. Identify well-supported clades |
| 95 | +clades = identify_clades(upgma_tree, 0.95) |
| 96 | +---- |
| 97 | + |
| 98 | +== Distance Methods Comparison |
| 99 | + |
| 100 | +[source,julia] |
| 101 | +---- |
| 102 | +using Cladistics |
| 103 | +sequences = ["ATCG", "ATCG", "TTCG", "TTCC"] |
| 104 | +
|
| 105 | +hamming = distance_matrix(sequences, method=:hamming) # Simple counting |
| 106 | +p_dist = distance_matrix(sequences, method=:p_distance) # Proportion of differences |
| 107 | +jc69 = distance_matrix(sequences, method=:jc69) # Jukes-Cantor |
| 108 | +k2p = distance_matrix(sequences, method=:k2p) # Kimura 2-parameter |
| 109 | +---- |
| 110 | + |
| 111 | +== Tree Construction Methods |
| 112 | + |
| 113 | +=== UPGMA: Simple but assumes molecular clock |
| 114 | + |
| 115 | +[source,julia] |
| 116 | +---- |
| 117 | +dmat = [0.0 0.2 0.4; 0.2 0.0 0.3; 0.4 0.3 0.0] |
| 118 | +tree = upgma(dmat, taxa_names=["Human", "Chimp", "Gorilla"]) |
| 119 | +# Good for: recent divergences, molecular clock valid |
| 120 | +# Not good for: ancient divergences, variable rates |
| 121 | +---- |
| 122 | + |
| 123 | +=== Neighbor-Joining: No molecular clock assumption |
| 124 | + |
| 125 | +[source,julia] |
| 126 | +---- |
| 127 | +dmat = [0.0 0.5 0.8 1.0; 0.5 0.0 0.6 0.9; 0.8 0.6 0.0 0.4; 1.0 0.9 0.4 0.0] |
| 128 | +tree = neighbor_joining(dmat, taxa_names=["A", "B", "C", "D"]) |
| 129 | +# Good for: variable evolutionary rates, large datasets |
| 130 | +---- |
| 131 | + |
| 132 | +== Bootstrap Analysis |
| 133 | + |
| 134 | +[source,julia] |
| 135 | +---- |
| 136 | +using Cladistics, Random |
| 137 | +Random.seed!(42) |
| 138 | +sequences = [ |
| 139 | + "ATCGATCGATCGATCG", |
| 140 | + "ATCGATCGATCGATCG", |
| 141 | + "ATCGTTCGATCGATCG", |
| 142 | + "TTCGATCGATCGATCG", |
| 143 | + "TTCGTTCGATCGTTCC" |
| 144 | +] |
| 145 | +# 1000 bootstrap replicates — resamples alignment columns with replacement |
| 146 | +support = bootstrap_support(sequences, replicates=1000, method=:nj) |
| 147 | +# Interpret: >95% Strong, 70-95% Moderate, <70% Weak |
| 148 | +---- |
| 149 | + |
| 150 | +== Maximum Parsimony |
| 151 | + |
| 152 | +[source,julia] |
| 153 | +---- |
| 154 | +using Cladistics |
| 155 | +alignment = ["ATCG", "ATCG", "TTCG", "TTCC"] |
| 156 | +char_matrix = character_state_matrix(alignment) |
| 157 | +informative_sites = parsimony_informative_sites(char_matrix) |
| 158 | +dmat = distance_matrix(alignment, method=:hamming) |
| 159 | +tree = upgma(dmat, taxa_names=["Tax1", "Tax2", "Tax3", "Tax4"]) |
| 160 | +score = calculate_parsimony_score(tree, char_matrix) |
| 161 | +# Lower score = more parsimonious (preferred) |
| 162 | +---- |
| 163 | + |
| 164 | +== Real-World Example: Primate Phylogeny |
| 165 | + |
| 166 | +[source,julia] |
| 167 | +---- |
| 168 | +using Cladistics |
| 169 | +primate_sequences = [ |
| 170 | + "ATCGATCGATCGATCGATCG", # Human |
| 171 | + "ATCGATCGATCGATCGATCG", # Chimp |
| 172 | + "ATCGATCGTTCGATCGATCG", # Gorilla |
| 173 | + "ATCGTTCGTTCGATCGTTCG", # Orangutan |
| 174 | + "TTCGTTCGTTCGTTCGTTCG" # Lemur (outgroup) |
| 175 | +] |
| 176 | +taxa = ["Human", "Chimp", "Gorilla", "Orangutan", "Lemur"] |
| 177 | +dmat = distance_matrix(primate_sequences, method=:k2p) |
| 178 | +tree = neighbor_joining(dmat, taxa_names=taxa) |
| 179 | +rooted_tree = root_tree(tree, "Lemur") |
| 180 | +support = bootstrap_support(primate_sequences, replicates=100, method=:nj) |
| 181 | +newick = tree_to_newick(rooted_tree) |
| 182 | +---- |
| 183 | + |
| 184 | +== Key Concepts |
| 185 | + |
| 186 | +=== Molecular Clock |
| 187 | + |
| 188 | +* Assumption: constant evolutionary rate across lineages |
| 189 | +* When valid: recent species, similar generation times |
| 190 | +* Methods: UPGMA assumes molecular clock |
| 191 | +* When violated: use Neighbor-Joining |
| 192 | + |
| 193 | +=== Bootstrap Support Interpretation |
| 194 | + |
| 195 | +* `>95%`: Publish with confidence |
| 196 | +* `70–95%`: Mention uncertainty |
| 197 | +* `<70%`: Weak support, collect more data |
| 198 | + |
| 199 | +=== Parsimony vs Distance |
| 200 | + |
| 201 | +* *Parsimony*: Find tree requiring fewest character changes; NP-hard but theoretically clear |
| 202 | +* *Distance*: Fast, scales to large datasets; loses some information from pairwise comparisons |
| 203 | + |
| 204 | +== References |
| 205 | + |
| 206 | +=== Classic Papers |
| 207 | + |
| 208 | +* Felsenstein, J. (1985). "Confidence limits on phylogenies: An approach using the bootstrap." _Evolution_, 39(4), 783–791. |
| 209 | +* Saitou, N., & Nei, M. (1987). "The neighbor-joining method." _Molecular Biology and Evolution_, 4(4), 406–425. |
| 210 | +* Fitch, W. M. (1971). "Toward defining the course of evolution." _Systematic Zoology_, 20(4), 406–416. |
| 211 | + |
| 212 | +=== Textbooks |
| 213 | + |
| 214 | +* Felsenstein, J. (2004). _Inferring Phylogenies_. Sinauer Associates. |
| 215 | +* Lemey, P. et al. (2009). _The Phylogenetic Handbook_. Cambridge University Press. |
| 216 | +* Hall, B. G. (2011). _Phylogenetic Trees Made Easy_. Sinauer Associates. |
| 217 | + |
| 218 | +=== Evolutionary Models |
| 219 | + |
| 220 | +* Jukes, T. H., & Cantor, C. R. (1969). "Evolution of protein molecules." _Mammalian Protein Metabolism_, pp. 21–132. |
| 221 | +* Kimura, M. (1980). "A simple method for estimating evolutionary rates." _Journal of Molecular Evolution_, 16(2), 111–120. |
| 222 | + |
| 223 | +== Citation |
| 224 | + |
| 225 | +[source,bibtex] |
| 226 | +---- |
| 227 | +@software{cladistics_jl, |
| 228 | + author = {Jewell, Jonathan D.A.}, |
| 229 | + title = {Cladistics.jl: Phylogenetic Analysis in Julia}, |
| 230 | + year = {2026}, |
| 231 | + url = {https://github.com/hyperpolymath/Cladistics.jl} |
| 232 | +} |
| 233 | +---- |
| 234 | + |
| 235 | +== Related Projects |
| 236 | + |
| 237 | +* https://github.com/BioJulia[BioJulia] — Broader bioinformatics ecosystem |
| 238 | +* https://github.com/crsl4/PhyloNetworks.jl[PhyloNetworks.jl] — Phylogenetic networks |
| 239 | +* https://github.com/richardreeve/Phylo.jl[Phylo.jl] — Alternative phylogenetics package |
| 240 | + |
| 241 | +== External Visualization Tools |
| 242 | + |
| 243 | +* http://tree.bio.ed.ac.uk/software/figtree/[FigTree] |
| 244 | +* https://itol.embl.de/[iTOL] |
| 245 | +* http://etetoolkit.org/[ETE Toolkit] |
| 246 | + |
| 247 | +== Contributing |
| 248 | + |
| 249 | +See link:CONTRIBUTING.md[CONTRIBUTING.md] for guidelines. |
| 250 | + |
| 251 | +Wondering how this works? See link:EXPLAINME.adoc[]. |
| 252 | + |
| 253 | +== License |
| 254 | + |
| 255 | +SPDX-License-Identifier: PMPL-1.0-or-later + |
| 256 | +See link:LICENSE[LICENSE]. |
0 commit comments