I need to add an internal node in a phylogenetic tree, along one of the trees branches.
For example, let's say that I have the following tree:
library(ape)
library(phytools)
tree <- rtree(4)
plot(tree,no.margin=T,label.offset=0.1)
nodelabels()
tiplabels()
edgelabels()
I would like to insert a new node along branch 5 at time (edge.length) t
, for example:
How could I do that?
You'll need to update the tree$edge
table and the tree$edge.length
vectors. You can find information on how these elements work in a "phylo"
object here.
Here's a quick function that should help you do do that on any tree:
#@param tree a phylo object
#@param element.IDs the numbers before and after the internal node
#@param edge.length the added edge length
add.internal.node <- function(tree, element.IDs, edge.length) {
## Find the edge in the edge table
tree_edge <- tree$edge
old_edge <- which(apply(tree_edge, 1, function(x, IDs) all(x == IDs), IDs = element.IDs))
if(length(old_edge) == 0) {
stop("edge not found")
}
## Add the new edge
tree_edge <- rbind(tree_edge[-old_edge, ], c(element.IDs[1], "new"), c("new", element.IDs[2]))
## Adding a new edge length
if(!is.null(tree$edge.length)) {
new_edge_lengths <- c(tree$edge.length, edge.length)
}
## Re-ordering node labels for the new edge table
## (element IDs need to be ordered in phylo objects)
#1- name the new node (should be ID+1)
tree_edge <- gsub("new", paste0("new", element.IDs[1]+1), tree_edge)
#2- update all the nodes after ID+1
update.nodes <- function(x, ID) {
if(!is.na(as.integer(x))) {
## Is not NA so it's a node (update the value if it's higher than the ID)
return(ifelse(as.integer(x) > ID, as.integer(x)+1, x))
} else {
## Is NA so it's the new node (leave it be for now)
return(x)
}
}
## Update all nodes
tree_edge <- apply(tree_edge, c(1,2), update.nodes, ID = element.IDs[1])
## Remove the "new" bit and make everything integers again
tree_edge <- gsub("new", "", tree_edge)
tree_edge <- apply(tree_edge, c(1,2), as.integer)
## Update the tree with the new node and the new edge.length
tree$edge <- tree_edge
if(!is.null(tree$edge.length)) {
tree$edge.length <- new_edge_lengths
}
tree$Nnode <- tree$Nnode+1
return(tree)
}
You can then add a node to your tree using the following pipeline:
## Generate a similar tree as in the example
set.seed(20)
tree <- rtree(4)
plot(tree)
axisPhylo(); nodelabels(); tiplabels(); edgelabels()
## Adding a node between elements 7 (node) and 4 (tip)
test <- add.internal.node(tree, element.IDs = c(7, 4), edge.length = 0.1)
plot(test)
axisPhylo(); nodelabels(); tiplabels(); edgelabels()