R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
164: Phylogenetic Tree Analysis in R
I've seen this happen to almost everyone starting with phylogenetics in R. You've spent hours running a Maximum Likelihood tree in another program, you've imported your Newick file perfectly, and you've got a beautiful metadata CSV. But the moment you try to map that data onto the tree, everything breaks or, worse, the plot looks correct but the colors are all wrong because the data didn't actually attach.
library(ape)
library(ggtree)
library(ggplot2)
# Load a simple tree
tree <- read.tree(text = "((Species_A:0.1, Species_B:0.2):0.5, Species_C:0.7);")
# Load metadata
metadata <- data.frame(
label = c("Species A", "Species B", "Species_C"),
trait = c("Red", "Blue", "Green")
)
# Try to plot and color by trait
ggtree(tree) %<+% metadata +
aes(color = trait) +
geom_tippoint()
The Ghost of Missing Metadata
If you run the code above, you'll notice something frustrating: Species_C might get colored, but Species_A and Species_B will be missing or default to a NA color. There isn't a crash, just a silent failure. The problem is that ggtree (and the %<+% operator) performs a join based on the label column. In the tree file, the tips are named Species_A (with an underscore), but in the CSV, they are Species A (with a space).
In the world of bioinformatics, different software packages have different rules for "legal" characters. Some tools force underscores; others preserve spaces. When you're bridging the gap between a tree file and a data frame, these tiny string mismatches are the primary source of bugs.
Sanitizing Labels for a Perfect Join
The fix isn't to manually edit your CSV—that's a recipe for human error. Instead, we handle it in R by ensuring both the tree labels and the metadata labels follow the same naming convention. I usually prefer to force everything to a "clean" version using gsub().
# Fix the metadata labels to match the tree's underscore convention
metadata$label <- gsub(" ", "_", metadata$label)
# Now the join will actually find the matches
ggtree(tree) %<+% metadata +
aes(color = trait) +
geom_tippoint() +
theme_tree2()
By normalizing the strings, we ensure the join key is identical. If you're dealing with a massive dataset, I'd recommend checking for mismatches explicitly using setdiff(tree$tip.label, metadata$label). If that returns anything, you know you've got a naming problem before you even try to plot.
Calculating Branch Lengths and Divergence
Once the visualization is sorted, you usually need to actually analyze the tree. A common task is determining the evolutionary distance between specific taxa. While you can eyeball the plot, you want the actual numbers for a paper or a report.
The ape package provides dist.phylo, which calculates the distance between all pairs of tips. I find it's often more useful to extract the path length between two specific nodes of interest.
# Get the distance matrix for all tips
dist_matrix <- dist.phylo(tree)
print(dist_matrix["Species_A", "Species_B"])
# To find the distance from the root to a specific tip:
# Sum the branch lengths from root to tip
node_path <- plot.phylo(tree)$node.depth # This is a shortcut via plotting
# Or more robustly using the tree structure:
sum(tree$edge.length[tree$edge[,2] == which(tree$tip.label == "Species_C")])
Refining the Tree Topology Visualization
Standard trees are often hard to read. I usually suggest using geom_tiplab() to ensure names are visible and geom_treescale() so the reader knows what the branch lengths actually represent. Without a scale bar, a phylogenetic tree is just a pretty drawing, not scientific data.
ggtree(tree) %<+% metadata +
aes(color = trait) +
geom_tippoint(size = 3) +
geom_tiplab(offset = 0.01) +
geom_treescale() +
labs(title = "Trait Distribution Across Taxa")
Notice how theme_tree2() adds the x-axis for branch length. When you're presenting this, always include that axis; it transforms the plot from a cladogram (which only shows relationship) into a phylogram (which shows evolutionary change).
📋 Practical Task
Exercise: Mapping Geographic Origins onto a Viral Phylogenetic Tree
You have been given a Newick string representing a small cluster of viral samples and a data frame containing the geographic origin of each sample. However, the sample IDs in the tree use dots (e.g., Sample.01), while the metadata uses dashes (e.g., Sample-01).
Your Task:
- Load the
apeandggtreelibraries. - Create the tree object from the following Newick string:
"((Sample.01:0.12, Sample.02:0.15):0.2, (Sample.03:0.1, Sample.04:0.11):0.2);" - Create a data frame named
geo_datawith two columns:label(containing"Sample-01", "Sample-02", "Sample-03", "Sample-04") andregion(containing"North", "North", "South", "South"). - Clean the
labelcolumn ingeo_dataso that it matches the tree's naming convention. - Plot the tree and color the tips based on the
regionusing the%<+%operator. - Add a scale bar to the plot using
geom_treescale().
There are no comments for now.