Sitemap
Maooz Abdullah

Maooz Abdullah

turning raw data into pieces of cake

A Simple Guide to Building Phylogenetic Trees and Heatmaps in R

3 min readMay 21, 2026

--

In this quick tutorial, we will build a simple pipeline to map out the evolutionary history of the Canidae family (dogs, wolves, and foxes), using a domestic cat as an outgroup. We will fetch real DNA data directly from a public database, align the sequences, and generate both a phylogenetic tree and a genetic distance heatmap.

Get Maooz Abdullah’s stories in your inbox

Join Medium for free to get updates from this writer.

Here is how you can do it in just a few steps.

1. Setting Up and Fetching Real Data

First, we need to load our core bioinformatics packages. Instead of downloading files manually, we will use the rentrez package to pull the real mitochondrial Cytochrome b gene sequences directly from the NCBI database using their unique Accession Numbers.

library(rentrez)
library(Biostrings)
library(msa)
library(ape)
library(ggtree)
library(ggplot2)
setwd(tempdir())
# Fetch real data from NCBI (Cytochrome B)
accession_numbers <- c(
"AB004237", "AM181037", "KM224347", "AJ441334","MN990717","DQ480509", "MG490401","AY291430")
# Fetch the raw FASTA data
fasta_data <- entrez_fetch(db="nucleotide", id=accession_numbers, rettype="fasta")
# Create a secure temporary file and read it into Biostrings
temp_fasta <- tempfile(fileext = ".fasta")
write(fasta_data, temp_fasta)
dna_seqs <- readDNAStringSet(temp_fasta)
# Rename the sequences to match our species
names(dna_seqs) <- c(
"Felis_catus",
"Vulpes_vulpes",
"Vulpes_lagopus",
"Canis_lupus",
"Canis_familiaris",
"Canis_latrans",
"Lycaon_pictus",
"Cuon_alpinus"
)

2. Aligning and Calculating Genetic Distance

DNA sequences naturally have mutations, so before we can compare them, we have to line them up perfectly. We use the msa package to run a ClustalW alignment. Once aligned, we use the ape package to calculate a mathematical distance matrix—essentially scoring how many mutations separate each species.

# Run the ClustalW alignment algorithm
alignment <- msa(dna_seqs, method="ClustalW")
# Convert to standard format and calculate genetic distance
dna_bin <- as.DNAbin(alignment)
distance_matrix <- dist.dna(dna_bin, model="K80")

3. Drawing the Phylogenetic Tree

With our math done, visualizing the tree is incredibly straightforward. We use the Neighbor-Joining (nj) method to build the structure, and R's base plot function to draw it.

# Build the tree using the Neighbor-Joining method
tree <- nj(distance_matrix)
# Draw the tree
plot(tree,
main="Evolutionary Divergence of Canidae (Cytochrome b)",
cex=1.1,
edge.width=2,
type="phylogram",
align.tip.label=FALSE)

When you run this, you will clearly see the evolutionary splits: the foxes branch off first, while the domestic dog and gray wolf sit closely together on their own branch, far away from the feline outgroup.

Press enter or click to view image in full size

4. Creating a Distance Heatmap

While trees are great for showing timelines, heatmaps are fantastic for showing raw genetic differences in a color-coded grid. We can pass the exact same distance matrix into the pheatmap package to get a completely different perspective on our data.

if (!requireNamespace("pheatmap", quietly = TRUE)) install.packages("pheatmap")
library(pheatmap)
dist_mat <- as.matrix(distance_matrix)
pheatmap(dist_mat,
main = "Canidae Genetic Distance Matrix",
color = colorRampPalette(c("#007f5f", "#aacc00", "#ffff3f"))(50),
display_numbers = TRUE,
fontsize_number = 10,
fontsize_row = 8,
fontsize_col = 8,
angle_col = 0)

The custom green-to-yellow color palette highlights similarity. Deep green squares instantly show us which species are genetically nearly identical, while the bright yellow highlights the massive evolutionary gap between the canine group and the cat.

Press enter or click to view image in full size

--

--

Maooz Abdullah
Maooz Abdullah

Written by Maooz Abdullah

turning raw data into pieces of cake