Here is my tree:
tree = data.frame(branchID = c(1,11,12,111,112,1121,1122), length = c(32, 21, 19, 5, 12, 6, 2))
> tree
branchID length
1 1
A version that supports trees with more than two branches. A bit work is required to convert to a data.tree structure, and to add the squirrels to it. But once you're there, the plotting is straight forward.
df <- data.frame(branchID = c(1,11,12,13, 14, 111,112,1121,1122), length = c(32, 21, 12, 8, 19, 5, 12, 6, 2))
squirrels <- data.frame(branchID = c(1,11,1121,11,111), PositionOnBranch = c(23, 12, 4, 2, 1), squirrel=c("FluffyTail", "Ginger", "NutCracker", "SuperSquirrel", "ChipnDale"), stringsAsFactors = FALSE)
library(magrittr)
#derive pathString from branchID, so we can convert it to data.tree structure
df$branchID %>%
as.character %>%
sapply(function(x) strsplit(x, split = "")) %>%
sapply(function(x) paste(x, collapse = "/")) ->
df$pathString
df$type <- "branch"
library(data.tree)
tree <- FromDataFrameTable(df)
#climb, little squirrels!
for (i in 1:nrow(squirrels)) {
squirrels[i, 'branchID'] %>%
as.character %>%
strsplit(split = "") %>%
extract2(1) %>%
extract(-1) -> path
if (length(path) > 0) branch <- tree$Climb(path)
else branch <- tree
#actually, we add the squirrels as branches to our tree
#What a symbiotic coexistence!
#advantage: Our SetCoordinates can be re-used as is
#disadvantage: may be confusing, and it requires us
#to do some filtering later
branch$AddChild(squirrels[i, 'squirrel'],
length = squirrels[i, 'PositionOnBranch'],
type = "squirrel")
}
SetCoordinates <- function(node, branch) {
if (branch$isRoot) {
node$x0 <- 0
node$y0 <- 0
} else {
node$x0 <- branch$parent$x1
node$y0 <- branch$parent$y1
}
#let's hope our squirrels didn't flunk in trigonometry ;-)
angle <- branch$position / (sum(Get(branch$siblings, "type") == "branch") + 2)
x <- - node$length * cospi(angle)
y <- sqrt(node$length^2 - x^2)
node$x1 <- node$x0 + x
node$y1 <- node$y0 + y
}
#let it grow!
tree$Do(function(node) {
SetCoordinates(node, node)
node$lwd <- 10 * (node$root$height - node$level + 1) / node$root$height
}, filterFun = function(node) node$type == "branch")
tree$Do(function(node) SetCoordinates(node, node$parent), filterFun = function(node) node$type == "squirrel")
Looking at the data:
print(tree, "type", "length", "x0", "y0", "x1", "y1")
This prints like so:
levelName type length x0 y0 x1 y1
1 1 branch 32 0.00000 0.00000 0.000000 32.00000
2 ¦--1 branch 21 0.00000 32.00000 -16.989357 44.34349
3 ¦ ¦--1 branch 5 -16.98936 44.34349 -19.489357 48.67362
4 ¦ ¦ °--ChipnDale squirrel 1 -16.98936 44.34349 -17.489357 45.20952
5 ¦ ¦--2 branch 12 -16.98936 44.34349 -10.989357 54.73580
6 ¦ ¦ ¦--1 branch 6 -10.98936 54.73580 -13.989357 59.93195
7 ¦ ¦ ¦ °--NutCracker squirrel 4 -10.98936 54.73580 -12.989357 58.19990
8 ¦ ¦ °--2 branch 2 -10.98936 54.73580 -9.989357 56.46785
9 ¦ ¦--Ginger squirrel 12 0.00000 32.00000 -9.708204 39.05342
10 ¦ °--SuperSquirrel squirrel 2 0.00000 32.00000 -1.618034 33.17557
11 ¦--2 branch 12 0.00000 32.00000 -3.708204 43.41268
12 ¦--3 branch 8 0.00000 32.00000 2.472136 39.60845
13 ¦--4 branch 19 0.00000 32.00000 15.371323 43.16792
14 °--FluffyTail squirrel 23 0.00000 0.00000 0.000000 23.00000
Once we're here, plotting is also easy:
plot(c(min(tree$Get("x0")), max(tree$Get("x1"))),
c(min(tree$Get("y0")), max(tree$Get("y1"))),
type='n', asp=1, axes=FALSE, xlab='', ylab='')
tree$Do(function(node) segments(node$x0, node$y0, node$x1, node$y1, lwd = node$lwd),
filterFun = function(node) node$type == "branch")
tree$Do(function(node) {
points(node$x1, node$y1, lwd = 8, col = "saddlebrown")
text(node$x1, node$y1, labels = node$name, pos = 2, cex = 0.7)
},
filterFun = function(node) node$type == "squirrel")