Here is an R function that can merge the bootstrap values from the consensus tree with the actual branch lengths from another tree. It uses the ape package, which also includes some tree plotting functions.
The comments in the code have most of the details. I usually write my comments in roxygen format - I hope it isn't too difficult to read. It makes it easier to wrap the functions into a package later.
AFAIK, this function works correctly, but I have not tested it extensively, so you should probably manually check the output. I would be interested in hearing about any bugs you find.
mergeTrees <- function(branchTree, bootTree, edge=TRUE) {
require(ape)
.splitCode <- function(tree) {
pp <- prop.part(tree)
tipLabels <- attr(pp, "labels")
ordTipLabels <- order(tipLabels)
nTip <- Ntip(tree)
out <- sapply(pp, function(x, ntip) {
ret <- numeric(ntip)
ret[ordTipLabels[x]] <- 1
as.logical(ret)
}, nTip)
out <- apply(out, 2, function(x) {
s <- sum(x)
l <- length(x) / 2
if(s > l)
!x
else if(s == l && x[1])
!x
else
x
})
nFillerBits <- ceiling(nrow(out) / 8) * 8 - nrow(out)
out <- rbind(out, matrix(nrow=nFillerBits, ncol=ncol(out), data=FALSE))
out <- apply(out, 2, function(x) paste(packBits(x), collapse=""))
attr(out, "labels") <- tipLabels[ordTipLabels]
cbind(1:length(pp) + Ntip(tree), out)
}
brSplits <- .splitCode(branchTree)
boSplits <- .splitCode(bootTree)
brSplits <- brSplits[brSplits[, 2] %in% boSplits[, 2], ]
boSplits <- boSplits[boSplits[, 2] %in% brSplits[, 2], ]
nodeNums <- cbind(brSplits[match(boSplits[, 2], brSplits[, 2]), 1],
boSplits[, 1])
storage.mode(nodeNums) <- "numeric"
if(edge)
bootstrap <- character(length(branchTree$edge.length))
else
bootstrap <- character(Nnode(branchTree))
for(i in seq_along(bootstrap)) {
if(edge)
brNode <- branchTree$edge[i, 2]
else
brNode <- i + Ntip(branchTree)
boNode <- nodeNums[nodeNums[, 1] == brNode, 2]
if(!length(boNode))
next
bootVal <- as.character(bootTree$edge.length[bootTree$edge[, 2] == boNode])
if(length(bootVal))
bootstrap[i] <- bootVal
}
bootstrap
}
And here are some examples of how to use it:
library(ape)
br <- read.tree("/path/to/tree/with/branch_lengths")
bo <- read.tree("/path/to/consensus/tree/with/bootstraps")
bootstraps <- mergeTrees(br, bo, TRUE)
plot(br)
edgelabels(bootstraps, frame="none", adj=c(0.5, 0))
plot(br, type="unrooted")
edgelabels(bootstraps, frame="none")
bootstraps <- mergeTrees(br, bo, FALSE)
br$node.names <- bootstraps
write.tree(br, file="mytree.phy")
mytree <- read.tree("mytree.phy")
bootstraps <- node2Edge(mytree)
plot(mytree); edgelabels(bootstraps)
node2Edge <- function(tree) {
nodeLabs <- tree$node.label
childNodes <- tree$edge[, 2]
edgeLabs <- character(length(childNodes))
for(i in seq_along(edgeLabs)) {
if(tree$edge[i, 2] <= Ntip(tree))
next
edgeLabs[i] <- nodeLabs[tree$edge[i, 2] - Ntip(tree)]
}
edgeLabs
}
Hey Panos, you've really answered your own question. It's fine to add support values with illustrator/inkscape but the topologies may differ (which is why consense doesn't give branch lengths)
You can also plot node support for the consensus in TreeView or APE. I HATE doing it by hand!