Skip to content

Commit c05ba8e

Browse files
committed
Merge branch 'master' of https://github.com/ArtPoon/ggfree
2 parents 5d34266 + 6bd45d2 commit c05ba8e

28 files changed

+495
-46
lines changed

R/tree.R

+66-41
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ as.phyloData <- function(phy, unscaled=FALSE) {
2121
if (is.null(phy$node.label)) {
2222
phy$node.label <- paste("Node", 1:Nnode(phy), sep='')
2323
}
24+
if (is.null(phy$edge.length)) {
25+
unscaled <- TRUE
26+
phy$edge.length <- rep(NA, nrow(phy$edge))
27+
}
2428

2529
# convert edge attributes into data frame
2630
edges <- data.frame(
@@ -526,7 +530,7 @@ lines.phyloLayout <- function(obj, col='grey50', shade=TRUE, ...) {
526530
#' @param ...: additional graphical parameters passed to `text`
527531
#'
528532
#' @export
529-
text.phyloLayout <- function(obj, label='t', align=FALSE, cex.lab=1, ...) {
533+
text.phyloLayout <- function(obj, label='t', align=FALSE, cex.lab=1, offset=0, ...) {
530534

531535
# filter node data frame
532536
tips <- obj$nodes[obj$nodes$n.tips==0, ]
@@ -538,8 +542,8 @@ text.phyloLayout <- function(obj, label='t', align=FALSE, cex.lab=1, ...) {
538542
if (obj$layout=='rectangular' | obj$layout=='slanted') {
539543
if (is.element(label, c('t', 'b'))) {
540544
# draw tip labels
541-
x <- tips$x
542-
if (align) { x <- max(tips$x) }
545+
x <- tips$x + offset
546+
if (align) { x <- max(tips$x) + offset }
543547
text(x=x, y=tips$y, labels=paste0(' ', tips$label),
544548
adj=0, cex=cex.lab, ...)
545549
}
@@ -557,10 +561,11 @@ text.phyloLayout <- function(obj, label='t', align=FALSE, cex.lab=1, ...) {
557561

558562
# equal angle layout draws zero-angle straight up
559563
if (obj$layout == 'equal.angle') tip$angle <- pi/2-tip$angle
560-
tip <- .rotate.label(tip, cex.lab)
564+
tip <- .rotate.label(tip, offset)
561565

562566
text(x=tip$x, y=tip$y, labels=tip$label,
563-
srt=tip$angle/pi*180, adj=0, cex=cex.lab, ...)
567+
srt=tip$angle/pi*180, adj=as.integer(tip$rotated),
568+
cex=cex.lab, ...)
564569
}
565570
}
566571
if (is.element(label, c('i', 'b'))) {
@@ -569,10 +574,11 @@ text.phyloLayout <- function(obj, label='t', align=FALSE, cex.lab=1, ...) {
569574
node <- internals[i, ]
570575

571576
if (obj$layout == 'equal.angle') node$angle <- pi/2-node$angle
572-
node <- .rotate.label(node, cex.lab)
577+
node <- .rotate.label(node, offset)
573578

574579
text(x=node$x, y=node$y, labels=node$label,
575-
srt=node$angle/pi*180, adj=0, cex=cex.lab, ...)
580+
srt=node$angle/pi*180, adj=as.integer(node$rotated),
581+
cex=cex.lab, ...)
576582
}
577583
}
578584
}
@@ -588,26 +594,28 @@ text.phyloLayout <- function(obj, label='t', align=FALSE, cex.lab=1, ...) {
588594
#'
589595
#' @param node: named vector, a row from the nodes data frame of a
590596
#' `phyloLayout` object.
597+
#' @param offset: amount to push label outward from origin
598+
#'
591599
#' @return a named vector with updated `x`, `y`, `angle` and `label` values
592600
#'
593601
#' @keywords internal
594-
.rotate.label <- function(node, cex.lab) {
602+
.rotate.label <- function(node, offset) {
603+
# slide label outward
604+
node$x <- node$x + offset*cos(node$angle)
605+
node$y <- node$y + offset*sin(node$angle)
606+
595607
h <- node$angle %% (2*pi)
596608
if (h>0.5*pi && h<(1.5*pi)) {
597-
# slide label outward
598-
w <- strwidth(node$label, units='user') * cex.lab
599-
node$x <- node$x + w*cos(node$angle)
600-
node$y <- node$y + w*sin(node$angle)
601-
602609
# invert the label
603610
node$angle <- node$angle+pi
604-
605611
# pad the label on the right
606612
node$label <- paste0(node$label, ' ')
613+
node$rotated <- TRUE
607614
}
608615
else {
609616
# pad the label on the left
610617
node$label <- paste0(' ', node$label)
618+
node$rotated <- FALSE
611619
}
612620
node
613621
}
@@ -745,11 +753,9 @@ draw.guidelines <- function(obj, lty=3, ...) {
745753
#' Generic function for drawing a grid of coloured or grey-scale
746754
#' rectangles corresponding to values in a matrix `z`. Rows in
747755
#' the matrix are assumed to correspond to tips of the tree.
748-
#' Only rectangular or slanted tree layouts are supported. If
749-
#' you want to annotate a circular (radial) layout, use `ringplot`.
750756
#'
751757
#' @param obj: an S3 object of class `phyloLayout`
752-
#' @param z: matrix, data to annotate
758+
#' @param z: matrix, data to annotate tips in order of *nodes*
753759
#' @param xlim: horizontal range of grid relative to current plot device.
754760
#' Note this function will call `xpd=NA` to permit drawing
755761
#' in margins.
@@ -768,41 +774,60 @@ image.phyloLayout <- function(obj, z, xlim, col=NA, border='white', xaxt='y', ..
768774
col <- colorRampPalette(c('firebrick', 'dodgerblue'))(max(z, na.rm=T))
769775
}
770776

771-
tips <- obj$edges[obj$edges$isTip, ]
777+
#tips <- obj$edges[obj$edges$isTip, ]
778+
tips <- obj$nodes[obj$nodes$n.tips==0, ]
772779

773780
# check that z has the expected dimensions
774781
if (nrow(z) != nrow(tips)) {
775782
stop("Number of rows in matrix `z` does not match number of tips ",
776783
"in `phyloLayout` object: ", nrow(z), "!=", nrow(tips))
777784
}
778-
y <- tips$y1
779-
x <- seq(xlim[1], xlim[2], length.out=ncol(z)+1)
780-
dx <- (x[2]-x[1])/2
781785

786+
# draw the image
782787
par(xpd=NA)
783-
offset <- max(tips$x1) + max(strwidth(obj$nodes$label))
784-
785-
for (j in 1:ncol(z)) {
786-
for (i in 1:nrow(z)) {
787-
val <- z[i,j]
788-
if (is.na(val)) next
789-
rect(xleft = x[j], xright = x[j+1],
790-
ybottom = y[i]-0.5, ytop = y[i]+0.5,
791-
border = border, col = col[val])
788+
789+
if (obj$layout == 'rectangular' | obj$layout == 'slanted') {
790+
y <- tips$y
791+
x <- seq(xlim[1], xlim[2], length.out=ncol(z)+1)
792+
dx <- (x[2]-x[1])/2
793+
794+
for (j in 1:ncol(z)) {
795+
for (i in 1:nrow(z)) {
796+
val <- z[i,j]
797+
if (is.na(val)) next
798+
rect(xleft = x[j], xright = x[j+1],
799+
ybottom = y[i]-0.5, ytop = y[i]+0.5,
800+
border = border, col = col[val])
801+
}
802+
}
803+
804+
# draw axis (override masking of labels)
805+
if (xaxt != 'n') {
806+
odds <- seq(1, ncol(z), 2)
807+
evens <- odds+1
808+
suppressWarnings({
809+
axis(side=1, at=x[1:ncol(z)]+dx, labels=NA, ...)
810+
axis(side=1, at=x[odds]+dx, labels=names(geno)[odds], lwd=0, ...)
811+
axis(side=1, at=x[evens]+dx, labels=names(geno)[evens], lwd=0, ...)
812+
})
792813
}
793814
}
794-
795-
# draw axis (override masking of labels)
796-
if (xaxt != 'n') {
797-
odds <- seq(1, ncol(z), 2)
798-
evens <- odds+1
799-
suppressWarnings({
800-
axis(side=1, at=x[1:ncol(z)]+dx, labels=NA, ...)
801-
axis(side=1, at=x[odds]+dx, labels=names(geno)[odds], lwd=0, ...)
802-
axis(side=1, at=x[evens]+dx, labels=names(geno)[evens], lwd=0, ...)
803-
})
815+
else if (obj$layout == 'radial') {
816+
angles <- tips$angle
817+
d.theta <- pi/nrow(tips)
818+
r <- seq(xlim[1], xlim[2], length.out=ncol(z)+1)
819+
dr <- (r[2]-r[1])/2
820+
821+
for (j in 1:ncol(z)) {
822+
for (i in 1:nrow(z)) {
823+
val <- z[i,j]
824+
if (is.na(val)) next
825+
draw.arc(x = 0, y = 0, theta0 = angles[i]-d.theta,
826+
theta1 = angles[i]+d.theta, r0 = r[j], r1 = r[j+1],
827+
col = col[val], border = border)
828+
}
829+
}
804830
}
805-
806831
par(xpd=FALSE)
807832
}
808833

README.Rmd

+29-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ par(mfrow=c(1,1), cex=1)
203203
```
204204

205205

206-
### Annotating trees
206+
### Flu example
207207

208208
Here is the source code to reproduce one of the example figures from the `ggtree` [application note](https://doi.org/10.1111/2041-210X.12628):
209209
```{r fig.width=10, fig.height=10, out.width="75%"}
@@ -245,7 +245,35 @@ image(L, geno[index, ], xlim=c(30, 37), col=col, cex.axis=0.75, line=-2)
245245
Note that most of the functions being used here are generic S3 methods in base R (namely, `plot`, `text`, `lines`, `points` and `image`).
246246

247247

248+
### Birds example
248249

250+
Here is code for decorating a phylogeny of bird families with the numbers of species:
251+
```{r fig.width=10, fig.height=10, out.width="80%"}
252+
data(bird.families)
253+
254+
# taxonomic info from BirdLife International
255+
path <- system.file("extdata/birdlife.csv", package='ggfree')
256+
birds <- read.csv(path, row.names=1)
257+
258+
# some entries are missing
259+
missing <- data.frame(
260+
Family=c("Dendrocygnidae", "Bucorvidae", "Rhinopomastidae", "Dacelonidae", "Cerylidae", "Centropidae", "Coccyzidae", "Crotophagidae", "Neomorphidae", "Batrachostomidae", "Eurostopodidae", "Chionididae", "Eopsaltriidae"),
261+
Count=c(8, 2, 3, 70, 9, 10, 13, 4, 6, 5, 3, 2, 44)
262+
)
263+
birds <- rbind(birds, missing)
264+
265+
# map information to tree
266+
index <- match(bird.families$tip.label, birds$Family)
267+
268+
require(RColorBrewer)
269+
pal <- brewer.pal(9, 'Blues')[2:9]
270+
bins <- as.integer(cut(log(birds$Count[index]), breaks=8))
271+
272+
# draw the tree, offsetting the labels for our image
273+
L <- tree.layout(bird.families, type='o')
274+
plot(L, cex.lab=0.7, offset=2, mar=rep(5,4), col='chocolate')
275+
image(L, z=as.matrix(bins), xlim=c(28.5,30), col=pal)
276+
```
249277

250278

251279
## Other works

README.md

+37-2
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,10 @@ approach to that taken by the plot functions in the `ape` package, which
247247
otherwise yields the same basic plots shown above.
248248

249249
So what’s the point? Now that we have the layout data, we have the
250-
freedom to make any customizations we want to the tree visualization.
250+
freedom to add any customization we can think of to the tree
251+
visualization.
251252

252-
### Annotating trees
253+
### Flu example
253254

254255
Here is the source code to reproduce one of the example figures from the
255256
`ggtree` [application note](https://doi.org/10.1111/2041-210X.12628):
@@ -295,6 +296,40 @@ image(L, geno[index, ], xlim=c(30, 37), col=col, cex.axis=0.75, line=-2)
295296
Note that most of the functions being used here are generic S3 methods
296297
in base R (namely, `plot`, `text`, `lines`, `points` and `image`).
297298

299+
### Birds example
300+
301+
Here is code for decorating a phylogeny of bird families with the
302+
numbers of species:
303+
304+
``` r
305+
data(bird.families)
306+
307+
# taxonomic info from BirdLife International
308+
path <- system.file("extdata/birdlife.csv", package='ggfree')
309+
birds <- read.csv(path, row.names=1)
310+
311+
# some entries are missing
312+
missing <- data.frame(
313+
Family=c("Dendrocygnidae", "Bucorvidae", "Rhinopomastidae", "Dacelonidae", "Cerylidae", "Centropidae", "Coccyzidae", "Crotophagidae", "Neomorphidae", "Batrachostomidae", "Eurostopodidae", "Chionididae", "Eopsaltriidae"),
314+
Count=c(8, 2, 3, 70, 9, 10, 13, 4, 6, 5, 3, 2, 44)
315+
)
316+
birds <- rbind(birds, missing)
317+
318+
# map information to tree
319+
index <- match(bird.families$tip.label, birds$Family)
320+
321+
require(RColorBrewer)
322+
pal <- brewer.pal(9, 'Blues')[2:9]
323+
bins <- as.integer(cut(log(birds$Count[index]), breaks=8))
324+
325+
# draw the tree, offsetting the labels for our image
326+
L <- tree.layout(bird.families, type='o')
327+
plot(L, cex.lab=0.7, offset=2, mar=rep(5,4), col='chocolate')
328+
image(L, z=as.matrix(bins), xlim=c(28.5,30), col=pal)
329+
```
330+
331+
<img src="man/figures/README-unnamed-chunk-15-1.png" width="80%" style="display: block; margin: auto;" />
332+
298333
## Other works
299334

300335
- @aronekuld’s [beeswarm](https://github.com/aroneklund/beeswarm)

0 commit comments

Comments
 (0)