... | ... |
@@ -1,11 +1,14 @@ |
1 | 1 |
##' drawing phylogenetic trees from list of phylo objects |
2 |
+##' |
|
3 |
+##' The trees plotted by 'ggdensitree()' will be stacked on top of each other and the |
|
4 |
+##' structures of the trees will be rotated to ensure the consistency of the tip order. |
|
2 | 5 |
##' |
3 | 6 |
##' @title ggdensitree |
4 | 7 |
##' @param data a list of phylo objects or any object with an as.phylo and fortify method |
5 | 8 |
##' @param mapping aesthetic mapping |
6 | 9 |
##' @param layout one of 'slanted', 'rectangluar', 'fan', 'circular' or 'radial' (default: 'slanted') |
7 | 10 |
##' @param tip.order the order of the tips by a character vector of taxa names; or an integer, N, to order the tips by the order of the tips in the Nth tree; 'mode' to order the tips by the most common order; 'mds' to order the tips based on MDS of the path length between the tips; or 'mds_dist' to order the tips based on MDS of the distance between the tips (default: 'mode') |
8 |
-##' @param align.tips TRUE to align trees by their tips and FALSE to align trees by their root (default: TRUE) |
|
11 |
+##' @param align.tips TRUE (default) to align trees by their tips and FALSE to align trees by their root |
|
9 | 12 |
##' @param jitter deviation to jitter tips |
10 | 13 |
##' @param ... additional parameters passed to fortify, ggtree and geom_tree |
11 | 14 |
##' @return tree layer |
... | ... |
@@ -13,6 +16,10 @@ |
13 | 16 |
##' @importFrom magrittr add |
14 | 17 |
##' @export |
15 | 18 |
##' @author Yu Guangchuang, Bradley R. Jones |
19 |
+##' @references |
|
20 |
+##' For more detailed demonstration of this function, please refer to chapter 4.4.2 of |
|
21 |
+##' *Data Integration, Manipulation and Visualization of Phylogenetic Trees* |
|
22 |
+##' <http://yulab-smu.top/treedata-book/index.html> by Guangchuang Yu. |
|
16 | 23 |
##' @examples |
17 | 24 |
##' require(ape) |
18 | 25 |
##' require(dplyr) |
... | ... |
@@ -119,11 +119,10 @@ ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mo |
119 | 119 |
|
120 | 120 |
## reorder tips (and shift x id align tips) |
121 | 121 |
max.x <- vapply(trees.f, function(x) max(x$x, na.rm = TRUE), numeric(1)) |
122 |
- farthest <- max(max.x) |
|
123 | 122 |
trees.f <- lapply(1:length(trees), function(i) { |
124 | 123 |
trees.f[[i]]$y <- getYcoord(trees[[i]], tip.order = tip.order) |
125 | 124 |
if (align.tips) { |
126 |
- trees.f[[i]]$x <- trees.f[[i]]$x + (farthest - max.x[i]) |
|
125 |
+ trees.f[[i]]$x <- trees.f[[i]]$x - max.x[i] |
|
127 | 126 |
} |
128 | 127 |
if (i > 1 && jitter > 0) { |
129 | 128 |
trees.f[[i]]$y[1:n.tips] %<>% add(stats::rnorm(n.tips, mean=0, sd=jitter)) |
... | ... |
@@ -62,15 +62,15 @@ ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mo |
62 | 62 |
if (length(tip.order) == 1) { |
63 | 63 |
if (tip.order == 'mode') { |
64 | 64 |
first.label <- trees.f[[1]] %>% |
65 |
- dplyr::filter(isTip) %>% |
|
66 |
- dplyr::arrange(y) %>% |
|
67 |
- dplyr::pull(label) |
|
65 |
+ dplyr::filter(.data$isTip) %>% |
|
66 |
+ dplyr::arrange(.data$y) %>% |
|
67 |
+ dplyr::pull(.data$label) |
|
68 | 68 |
|
69 | 69 |
res <- lapply( |
70 | 70 |
trees.f, |
71 | 71 |
. %>% |
72 |
- dplyr::filter(isTip) %>% |
|
73 |
- dplyr::arrange(y) %>% |
|
72 |
+ dplyr::filter(.data$isTip) %>% |
|
73 |
+ dplyr::arrange(.data$y) %>% |
|
74 | 74 |
dplyr::pull("label") %>% |
75 | 75 |
match(first.label) |
76 | 76 |
) %>% |
... | ... |
@@ -78,8 +78,8 @@ ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mo |
78 | 78 |
as.data.frame() %>% |
79 | 79 |
dplyr::group_by_all() %>% |
80 | 80 |
dplyr::summarize(count = dplyr::n(), .groups = 'drop') %>% |
81 |
- dplyr::filter(count == max(count)) %>% |
|
82 |
- dplyr::select(-count) %>% |
|
81 |
+ dplyr::filter(.data$count == max(.data$count)) %>% |
|
82 |
+ dplyr::select(-.data$count) %>% |
|
83 | 83 |
dplyr::slice(1) %>% |
84 | 84 |
unlist() |
85 | 85 |
|
... | ... |
@@ -144,4 +144,4 @@ cophenetic.phylo.check.length <- function(tree, method) { |
144 | 144 |
tree$edge.length <- rep(1, nrow(tree$edge)) |
145 | 145 |
|
146 | 146 |
stats::cophenetic(tree) |
147 |
-} |
|
148 | 147 |
\ No newline at end of file |
148 |
+} |
... | ... |
@@ -4,7 +4,7 @@ |
4 | 4 |
##' @param data a list of phylo objects or any object with an as.phylo and fortify method |
5 | 5 |
##' @param mapping aesthetic mapping |
6 | 6 |
##' @param layout one of 'slanted', 'rectangluar', 'fan', 'circular' or 'radial' (default: 'slanted') |
7 |
-##' @param tip.order the order of the tips by a character vector of taxa names; or an integer, N, to order the tips by the order of the tips in the Nth tree; 'mds' to order the tips based on MDS of the path length between the tips; or 'mds_dist' to order the tips based on MDS of the distance between the tips (default: 'mds_dist') |
|
7 |
+##' @param tip.order the order of the tips by a character vector of taxa names; or an integer, N, to order the tips by the order of the tips in the Nth tree; 'mode' to order the tips by the most common order; 'mds' to order the tips based on MDS of the path length between the tips; or 'mds_dist' to order the tips based on MDS of the distance between the tips (default: 'mode') |
|
8 | 8 |
##' @param align.tips TRUE to align trees by their tips and FALSE to align trees by their root (default: TRUE) |
9 | 9 |
##' @param jitter deviation to jitter tips |
10 | 10 |
##' @param ... additional parameters passed to fortify, ggtree and geom_tree |
... | ... |
@@ -16,41 +16,42 @@ |
16 | 16 |
##' @examples |
17 | 17 |
##' require(ape) |
18 | 18 |
##' require(dplyr) |
19 |
+##' require(tidyr) |
|
19 | 20 |
##' |
20 |
-##' # Plot mutliple trees with aligned tips |
|
21 |
+##' # Plot multiple trees with aligned tips |
|
21 | 22 |
##' trees <- list(read.tree(text="((a:1,b:1):1.5,c:2.5);"), read.tree(text="((a:1,c:1):1,b:2);")); |
22 | 23 |
##' ggdensitree(trees) + geom_tiplab() |
23 | 24 |
##' |
24 |
-##' # Plot multiple trees with aligmned tips with tip labls and separate tree colors |
|
25 |
+##' # Plot multiple trees with aligned tips with tip labels and separate tree colors |
|
25 | 26 |
##' trees.fort <- list(trees[[1]] %>% fortify %>% mutate(tree="a"), trees[[2]] %>% fortify %>% mutate(tree="b")); |
26 | 27 |
##' ggdensitree(trees.fort, aes(colour=tree)) + geom_tiplab(colour='black') |
27 | 28 |
##' |
28 | 29 |
##' |
29 | 30 |
##' # Generate example data |
30 | 31 |
##' set.seed(1) |
31 |
-##' trees <- rmtree(5, 10) |
|
32 |
-##' time.trees <- lapply(1:length(trees), function(i) { |
|
33 |
-##' tree <- trees[[i]] |
|
32 |
+##' random.trees <- rmtree(5, 10) |
|
33 |
+##' time.trees <- lapply(seq_along(random.trees), function(i) { |
|
34 |
+##' tree <- random.trees[[i]] |
|
34 | 35 |
##' tree$tip.label <- paste0("t", 1:10) |
35 | 36 |
##' dates <- estimate.dates(tree, 1:10, mu=1, nsteps=1) |
36 | 37 |
##' tree$edge.length <- dates[tree$edge[, 2]] - dates[tree$edge[, 1]] |
37 | 38 |
##' fortify(tree) %>% mutate(tree=factor(i, levels=as.character(1:10))) |
38 | 39 |
##' }) |
39 | 40 |
##' |
40 |
-##' # Plot multiple trees with aligned tips from muliple time points |
|
41 |
+##' # Plot multiple trees with aligned tips from multiple time points |
|
41 | 42 |
##' ggdensitree(time.trees, aes(colour=tree), tip.order=paste0("t", 1:10)) + geom_tiplab(colour='black') |
42 | 43 |
##' |
43 | 44 |
##' |
44 | 45 |
##' # Read example data |
45 |
-##' trees <- read.tree(system.file("examples", "ggdensitree_example.tree", package="ggtree")) |
|
46 |
+##' example.trees <- read.tree(system.file("examples", "ggdensitree_example.tree", package="ggtree")) |
|
46 | 47 |
##' |
47 | 48 |
##' # Compute OTU |
48 | 49 |
##' grp <- list(A = c("a.t1", "a.t2", "a.t3", "a.t4"), B = c("b.t1", "b.t2", "b.t3", "b.t4"), C = c("c.t1", "c.t2", "c.t3", "c.t4")) |
49 |
-##' trees <- lapply(trees, groupOTU, grp) |
|
50 |
+##' otu.trees <- lapply(example.trees, groupOTU, grp) |
|
50 | 51 |
##' |
51 | 52 |
##' # Plot multiple trees colored by OTU |
52 |
-##' ggdensitree(trees, aes(colour=group), alpha=1/6) + scale_colour_manual(values=c("black", "red", "green", "blue")) |
|
53 |
-ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mds_dist', |
|
53 |
+##' ggdensitree(otu.trees, aes(colour=group), alpha=1/6, tip.order='mds') + scale_colour_manual(values=c("black", "red", "green", "blue")) |
|
54 |
+ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mode', |
|
54 | 55 |
align.tips=TRUE, jitter=0, ...) { |
55 | 56 |
## factorize to simplify code |
56 | 57 |
trees <- lapply(data, as.phylo) |
... | ... |
@@ -59,7 +60,31 @@ ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='md |
59 | 60 |
|
60 | 61 |
## determine tip order |
61 | 62 |
if (length(tip.order) == 1) { |
62 |
- if (grepl('mds', tip.order)) { |
|
63 |
+ if (tip.order == 'mode') { |
|
64 |
+ first.label <- trees.f[[1]] %>% |
|
65 |
+ dplyr::filter(isTip) %>% |
|
66 |
+ dplyr::arrange(y) %>% |
|
67 |
+ dplyr::pull(label) |
|
68 |
+ |
|
69 |
+ res <- lapply( |
|
70 |
+ trees.f, |
|
71 |
+ . %>% |
|
72 |
+ dplyr::filter(isTip) %>% |
|
73 |
+ dplyr::arrange(y) %>% |
|
74 |
+ `$`("label") %>% |
|
75 |
+ match(first.label) |
|
76 |
+ ) %>% |
|
77 |
+ do.call(rbind, .) %>% |
|
78 |
+ as.data.frame() %>% |
|
79 |
+ dplyr::group_by_all() %>% |
|
80 |
+ dplyr::summarize(count = dplyr::n(), .groups = 'drop') %>% |
|
81 |
+ dplyr::filter(count == max(count)) %>% |
|
82 |
+ dplyr::select(-count) %>% |
|
83 |
+ dplyr::slice(1) %>% |
|
84 |
+ unlist() |
|
85 |
+ |
|
86 |
+ tip.order <- first.label[res] |
|
87 |
+ } else if (grepl('mds', tip.order)) { |
|
63 | 88 |
method <- tip.order |
64 | 89 |
|
65 | 90 |
first.label <- trees.f[[1]] %>% |
... | ... |
@@ -4,7 +4,7 @@ |
4 | 4 |
##' @param data a list of phylo objects or any object with an as.phylo and fortify method |
5 | 5 |
##' @param mapping aesthetic mapping |
6 | 6 |
##' @param layout one of 'slanted', 'rectangluar', 'fan', 'circular' or 'radial' (default: 'slanted') |
7 |
-##' @param tip.order the order of the tips by a character vector of taxa names; or an integer, N, to order the tips by the order of the tips in the Nth tree; or 'mds' to order the tips based on MDS of the distance between the tips (default: 'mds') |
|
7 |
+##' @param tip.order the order of the tips by a character vector of taxa names; or an integer, N, to order the tips by the order of the tips in the Nth tree; 'mds' to order the tips based on MDS of the path length between the tips; or 'mds_dist' to order the tips based on MDS of the distance between the tips (default: 'mds') |
|
8 | 8 |
##' @param align.tips TRUE to align trees by their tips and FALSE to align trees by their root (default: TRUE) |
9 | 9 |
##' @param jitter deviation to jitter tips |
10 | 10 |
##' @param ... additional parameters passed to fortify, ggtree and geom_tree |
... | ... |
@@ -17,12 +17,16 @@ |
17 | 17 |
##' require(ape) |
18 | 18 |
##' require(dplyr) |
19 | 19 |
##' |
20 |
+##' # Plot mutliple trees with aligned tips |
|
20 | 21 |
##' trees <- list(read.tree(text="((a:1,b:1):1.5,c:2.5);"), read.tree(text="((a:1,c:1):1,b:2);")); |
21 | 22 |
##' ggdensitree(trees) + geom_tiplab() |
22 | 23 |
##' |
24 |
+##' # Plot multiple trees with aligmned tips with tip labls and separate tree colors |
|
23 | 25 |
##' trees.fort <- list(trees[[1]] %>% fortify %>% mutate(tree="a"), trees[[2]] %>% fortify %>% mutate(tree="b")); |
24 | 26 |
##' ggdensitree(trees.fort, aes(colour=tree)) + geom_tiplab(colour='black') |
25 | 27 |
##' |
28 |
+##' |
|
29 |
+##' # Generate example data |
|
26 | 30 |
##' set.seed(1) |
27 | 31 |
##' trees <- rmtree(5, 10) |
28 | 32 |
##' time.trees <- lapply(1:length(trees), function(i) { |
... | ... |
@@ -31,9 +35,22 @@ |
31 | 35 |
##' dates <- estimate.dates(tree, 1:10, mu=1, nsteps=1) |
32 | 36 |
##' tree$edge.length <- dates[tree$edge[, 2]] - dates[tree$edge[, 1]] |
33 | 37 |
##' fortify(tree) %>% mutate(tree=factor(i, levels=as.character(1:10))) |
34 |
-##' }) |
|
38 |
+##' }) |
|
39 |
+##' |
|
40 |
+##' # Plot multiple trees with aligned tips from muliple time points |
|
35 | 41 |
##' ggdensitree(time.trees, aes(colour=tree), tip.order=paste0("t", 1:10)) + geom_tiplab(colour='black') |
36 |
-ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mds', |
|
42 |
+##' |
|
43 |
+##' |
|
44 |
+##' # Read example data |
|
45 |
+##' trees <- read.tree(system.file("ggtree", "inst/examples/ggdensitree_example.tree")) |
|
46 |
+##' |
|
47 |
+##' # Compute OTU |
|
48 |
+##' grp <- list(A = c("a.t1", "a.t2", "a.t3", "a.t4"), B = c("b.t1", "b.t2", "b.t3", "b.t4"), C = c("c.t1", "c.t2", "c.t3", "c.t4")) |
|
49 |
+##' trees <- lapply(trees, groupOTU, grp) |
|
50 |
+##' |
|
51 |
+##' # Plot multiple trees colored by OTU |
|
52 |
+##' ggdensitree(trees, aes(colour=group), alpha=1/6) + scale_colour_manual(values=c("black", "red", "green", "blue")) |
|
53 |
+ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mds_dist', |
|
37 | 54 |
align.tips=TRUE, jitter=0, ...) { |
38 | 55 |
## factorize to simplify code |
39 | 56 |
trees <- lapply(data, as.phylo) |
... | ... |
@@ -42,7 +59,7 @@ ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='md |
42 | 59 |
|
43 | 60 |
## determine tip order |
44 | 61 |
if (length(tip.order) == 1) { |
45 |
- if (tip.order == 'mds') { |
|
62 |
+ if (grepl('mds', tip.order)) { |
|
46 | 63 |
first.label <- trees.f[[1]] %>% |
47 | 64 |
dplyr::filter(.data$isTip) %>% |
48 | 65 |
dplyr::pull(.data$label) |
... | ... |
@@ -96,7 +113,7 @@ ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='md |
96 | 113 |
|
97 | 114 |
## wrapper for cohpenetic to ensure that branch lengths exist |
98 | 115 |
cophenetic.phylo.check.length <- function(tree) { |
99 |
- if (is.null(tree$edge.length)) |
|
116 |
+ if (method != 'mds_dist' || is.null(tree$edge.length)) |
|
100 | 117 |
tree$edge.length <- rep(1, nrow(tree$edge)) |
101 | 118 |
|
102 | 119 |
stats::cophenetic(tree) |
... | ... |
@@ -4,9 +4,8 @@ |
4 | 4 |
##' @param data a list of phylo objects or any object with an as.phylo and fortify method |
5 | 5 |
##' @param mapping aesthetic mapping |
6 | 6 |
##' @param layout one of 'slanted', 'rectangluar', 'fan', 'circular' or 'radial' (default: 'slanted') |
7 |
-##' @param branch.length variable to be used to scale branch length. Setting to 'branch.length' will use the branch lengths of the tree objects. Default is 'none' to discard branch length and only plot cladogram (more reasonable for densitree). |
|
8 | 7 |
##' @param tip.order the order of the tips by a character vector of taxa names; or an integer, N, to order the tips by the order of the tips in the Nth tree; or 'mds' to order the tips based on MDS of the distance between the tips (default: 'mds') |
9 |
-##' @param dangle TRUE to align trees by their tips and FALSE to align treesby their root (default: TRUE) |
|
8 |
+##' @param align.tips TRUE to align trees by their tips and FALSE to align trees by their root (default: TRUE) |
|
10 | 9 |
##' @param jitter deviation to jitter tips |
11 | 10 |
##' @param ... additional parameters passed to fortify, ggtree and geom_tree |
12 | 11 |
##' @return tree layer |
... | ... |
@@ -17,78 +16,88 @@ |
17 | 16 |
##' @examples |
18 | 17 |
##' require(ape) |
19 | 18 |
##' require(dplyr) |
20 |
-##' library(ape) |
|
21 | 19 |
##' |
22 |
-##' trees <- list(read.tree(text="((a,b),c);"), read.tree(text="((a,c),b);")); |
|
20 |
+##' trees <- list(read.tree(text="((a:1,b:1):1.5,c:2.5);"), read.tree(text="((a:1,c:1):1,b:2);")); |
|
23 | 21 |
##' ggdensitree(trees) + geom_tiplab() |
24 | 22 |
##' |
25 | 23 |
##' trees.fort <- list(trees[[1]] %>% fortify %>% mutate(tree="a"), trees[[2]] %>% fortify %>% mutate(tree="b")); |
26 | 24 |
##' ggdensitree(trees.fort, aes(colour=tree)) + geom_tiplab(colour='black') |
27 |
-ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", branch.length = "none", |
|
28 |
- tip.order='mds', dangle=TRUE, jitter=0, ...) { |
|
29 |
- ## factorize to simplify code |
|
30 |
- trees <- lapply(data, as.phylo) |
|
31 |
- trees.f <- lapply(data, fortify, layout=layout, branch.length = branch.length, ...) |
|
32 |
- n.tips <- sum(trees.f[[1]]$isTip) |
|
33 |
- |
|
34 |
- ## determine tip order |
|
35 |
- if (length(tip.order) == 1) { |
|
36 |
- if (tip.order == 'mds') { |
|
37 |
- first.label <- trees.f[[1]] %>% |
|
38 |
- dplyr::filter(.data$isTip) %>% |
|
39 |
- dplyr::pull(.data$label) |
|
40 |
- |
|
41 |
- tip.order <- lapply(trees, function(x) { |
|
42 |
- match(x$tip.label, first.label) |
|
43 |
- }) |
|
44 |
- |
|
45 |
- tip.2.tip <- lapply(trees, cophenetic.phylo.check.length) |
|
46 |
- tip.2.tip <- lapply(1:length(trees), function(i) { |
|
47 |
- tip.2.tip[[i]][tip.order[[i]], tip.order[[i]]] |
|
48 |
- }) |
|
49 |
- |
|
50 |
- all.tab <- do.call(rbind, tip.2.tip) |
|
51 |
- rownames(all.tab) <- NULL |
|
52 |
- |
|
53 |
- distances <- stats::dist(t(all.tab)) |
|
54 |
- |
|
55 |
- res <- stats::cmdscale(distances, k=1) |
|
25 |
+##' |
|
26 |
+##' set.seed(1) |
|
27 |
+##' trees <- rmtree(5, 10) |
|
28 |
+##' time.trees <- lapply(1:length(trees), function(i) { |
|
29 |
+##' tree <- trees[[i]] |
|
30 |
+##' tree$tip.label <- paste0("t", 1:10) |
|
31 |
+##' dates <- estimate.dates(tree, 1:10, mu=1, nsteps=1) |
|
32 |
+##' tree$edge.length <- dates[tree$edge[, 2]] - dates[tree$edge[, 1]] |
|
33 |
+##' fortify(tree) %>% mutate(tree=factor(i, levels=as.character(1:10))) |
|
34 |
+##' }) |
|
35 |
+##' ggdensitree(time.trees, aes(colour=tree), tip.order=paste0("t", 1:10)) + geom_tiplab(colour='black') |
|
36 |
+ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mds', |
|
37 |
+ align.tips=TRUE, jitter=0, ...) { |
|
38 |
+ ## factorize to simplify code |
|
39 |
+ trees <- lapply(data, as.phylo) |
|
40 |
+ trees.f <- lapply(data, fortify, layout=layout, ...) |
|
41 |
+ n.tips <- sum(trees.f[[1]]$isTip) |
|
42 |
+ |
|
43 |
+ ## determine tip order |
|
44 |
+ if (length(tip.order) == 1) { |
|
45 |
+ if (tip.order == 'mds') { |
|
46 |
+ first.label <- trees.f[[1]] %>% |
|
47 |
+ dplyr::filter(.data$isTip) %>% |
|
48 |
+ dplyr::pull(.data$label) |
|
49 |
+ |
|
50 |
+ tip.order <- lapply(trees, function(x) { |
|
51 |
+ match(x$tip.label, first.label) |
|
52 |
+ }) |
|
53 |
+ |
|
54 |
+ tip.2.tip <- lapply(trees, cophenetic.phylo.check.length) |
|
55 |
+ tip.2.tip <- lapply(1:length(trees), function(i) { |
|
56 |
+ tip.2.tip[[i]][tip.order[[i]], tip.order[[i]]] |
|
57 |
+ }) |
|
58 |
+ |
|
59 |
+ all.tab <- do.call(rbind, tip.2.tip) |
|
60 |
+ rownames(all.tab) <- NULL |
|
56 | 61 |
|
57 |
- tip.order <- first.label[order(res[,1])] |
|
58 |
- } else if (as.numeric(tip.order) && tip.order <= length(trees)) { |
|
59 |
- labels <- trees.f[[tip.order]] %>% |
|
60 |
- dplyr::filter(.data$isTip) %>% |
|
61 |
- dplyr::pull(.data$label) |
|
62 |
- |
|
63 |
- tip.order <- labels[as.integer(trees.f[[tip.order]]$y)] |
|
64 |
- } |
|
65 |
- } |
|
62 |
+ distances <- stats::dist(t(all.tab)) |
|
63 |
+ |
|
64 |
+ res <- stats::cmdscale(distances, k=1) |
|
65 |
+ |
|
66 |
+ tip.order <- first.label[order(res[,1])] |
|
67 |
+ } else if (as.numeric(tip.order) && tip.order <= length(trees)) { |
|
68 |
+ labels <- trees.f[[tip.order]] %>% |
|
69 |
+ dplyr::filter(.data$isTip) %>% |
|
70 |
+ dplyr::pull(.data$label) |
|
71 |
+ |
|
72 |
+ tip.order <- labels[as.integer(trees.f[[tip.order]]$y)] |
|
73 |
+ } |
|
74 |
+ } |
|
66 | 75 |
|
67 |
- ## reorder tips (and shift x id dangling) |
|
68 |
- max.x <- vapply(trees.f, function(x) max(x$x, na.rm = TRUE), numeric(1)) |
|
69 |
- farthest <- max(max.x) |
|
70 |
- trees.f <- lapply(1:length(trees), function(i) { |
|
71 |
- trees.f[[i]]$y <- getYcoord(trees[[i]], tip.order = tip.order) |
|
72 |
- if (dangle) { |
|
73 |
- trees.f[[i]]$x <- trees.f[[i]]$x + (farthest - max.x[i]) |
|
74 |
- } |
|
75 |
- if (i > 1 && jitter > 0) { |
|
76 |
- trees.f[[i]]$y[1:n.tips] %<>% add(stats::rnorm(n.tips, mean=0, sd=jitter)) |
|
77 |
- } |
|
78 |
- trees.f[[i]] |
|
79 |
- }) |
|
80 |
- |
|
81 |
- ## plot all trees together |
|
82 |
- p <- ggtree(tr=trees.f[[1]], mapping=mapping, layout=layout, ...) |
|
83 |
- for (x in trees.f[-1]) |
|
84 |
- p <- p + geom_tree(mapping=mapping, data=x, layout=layout, ...) |
|
85 |
- p |
|
76 |
+ ## reorder tips (and shift x id align tips) |
|
77 |
+ max.x <- vapply(trees.f, function(x) max(x$x, na.rm = TRUE), numeric(1)) |
|
78 |
+ farthest <- max(max.x) |
|
79 |
+ trees.f <- lapply(1:length(trees), function(i) { |
|
80 |
+ trees.f[[i]]$y <- getYcoord(trees[[i]], tip.order = tip.order) |
|
81 |
+ if (align.tips) { |
|
82 |
+ trees.f[[i]]$x <- trees.f[[i]]$x + (farthest - max.x[i]) |
|
83 |
+ } |
|
84 |
+ if (i > 1 && jitter > 0) { |
|
85 |
+ trees.f[[i]]$y[1:n.tips] %<>% add(stats::rnorm(n.tips, mean=0, sd=jitter)) |
|
86 |
+ } |
|
87 |
+ trees.f[[i]] |
|
88 |
+ }) |
|
89 |
+ |
|
90 |
+ ## plot all trees together |
|
91 |
+ p <- ggtree(tr=trees.f[[1]], mapping=mapping, layout=layout, ...) |
|
92 |
+ for (x in trees.f[-1]) |
|
93 |
+ p <- p + geom_tree(mapping=mapping, data=x, layout=layout, ...) |
|
94 |
+ p |
|
86 | 95 |
} |
87 | 96 |
|
88 | 97 |
## wrapper for cohpenetic to ensure that branch lengths exist |
89 | 98 |
cophenetic.phylo.check.length <- function(tree) { |
90 |
- if (is.null(tree$edge.length)) |
|
91 |
- tree$edge.length <- rep(1, nrow(tree$edge)) |
|
92 |
- |
|
93 |
- stats::cophenetic(tree) |
|
94 |
-} |
|
99 |
+ if (is.null(tree$edge.length)) |
|
100 |
+ tree$edge.length <- rep(1, nrow(tree$edge)) |
|
101 |
+ |
|
102 |
+ stats::cophenetic(tree) |
|
103 |
+} |
|
95 | 104 |
\ No newline at end of file |
... | ... |
@@ -4,6 +4,7 @@ |
4 | 4 |
##' @param data a list of phylo objects or any object with an as.phylo and fortify method |
5 | 5 |
##' @param mapping aesthetic mapping |
6 | 6 |
##' @param layout one of 'slanted', 'rectangluar', 'fan', 'circular' or 'radial' (default: 'slanted') |
7 |
+##' @param branch.length variable to be used to scale branch length. Setting to 'branch.length' will use the branch lengths of the tree objects. Default is 'none' to discard branch length and only plot cladogram (more reasonable for densitree). |
|
7 | 8 |
##' @param tip.order the order of the tips by a character vector of taxa names; or an integer, N, to order the tips by the order of the tips in the Nth tree; or 'mds' to order the tips based on MDS of the distance between the tips (default: 'mds') |
8 | 9 |
##' @param dangle TRUE to align trees by their tips and FALSE to align treesby their root (default: TRUE) |
9 | 10 |
##' @param jitter deviation to jitter tips |
... | ... |
@@ -17,106 +18,77 @@ |
17 | 18 |
##' require(ape) |
18 | 19 |
##' require(dplyr) |
19 | 20 |
##' library(ape) |
20 |
-##' library(tidyverse) |
|
21 | 21 |
##' |
22 | 22 |
##' trees <- list(read.tree(text="((a,b),c);"), read.tree(text="((a,c),b);")); |
23 | 23 |
##' ggdensitree(trees) + geom_tiplab() |
24 | 24 |
##' |
25 | 25 |
##' trees.fort <- list(trees[[1]] %>% fortify %>% mutate(tree="a"), trees[[2]] %>% fortify %>% mutate(tree="b")); |
26 | 26 |
##' ggdensitree(trees.fort, aes(colour=tree)) + geom_tiplab(colour='black') |
27 |
-ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mds', dangle=TRUE, jitter=0, ...) { |
|
28 |
- # factorize to simplify code |
|
29 |
- trees <- lapply(data, as.phylo) |
|
30 |
- trees.f <- lapply(data, fortify, layout=layout, ...) |
|
31 |
- n.tips <- sum(trees.f[[1]]$isTip) |
|
32 |
- |
|
33 |
- # determine tip order |
|
34 |
- if (length(tip.order) == 1) { |
|
35 |
- if (tip.order == 'mds') { |
|
36 |
- first.label <- subset(trees.f[[1]], isTip)$label |
|
37 |
- |
|
38 |
- tip.order <- lapply(trees, . %$% tip.label %>% match(first.label)) |
|
39 |
- |
|
40 |
- tip.2.tip <- lapply(trees, cophenetic.phylo.check.length) |
|
41 |
- tip.2.tip <- lapply(1:length(trees), function(i) tip.2.tip[[i]][tip.order[[i]], tip.order[[i]]]) |
|
42 |
- |
|
43 |
- all.tab <- do.call(rbind, tip.2.tip) |
|
44 |
- rownames(all.tab) <- NULL |
|
45 |
- |
|
46 |
- distances <- dist(t(all.tab)) |
|
47 |
- |
|
48 |
- res <- cmdscale(distances, k=1) |
|
49 |
- |
|
50 |
- tip.order <- first.label[order(res[,1])] |
|
51 |
- } else if (as.numeric(tip.order) && tip.order <= length(trees)) { |
|
52 |
- labels <- subset(trees.f[[tip.order]], isTip)$label |
|
27 |
+ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", branch.length = "none", |
|
28 |
+ tip.order='mds', dangle=TRUE, jitter=0, ...) { |
|
29 |
+ ## factorize to simplify code |
|
30 |
+ trees <- lapply(data, as.phylo) |
|
31 |
+ trees.f <- lapply(data, fortify, layout=layout, branch.length = branch.length, ...) |
|
32 |
+ n.tips <- sum(trees.f[[1]]$isTip) |
|
33 |
+ |
|
34 |
+ ## determine tip order |
|
35 |
+ if (length(tip.order) == 1) { |
|
36 |
+ if (tip.order == 'mds') { |
|
37 |
+ first.label <- trees.f[[1]] %>% |
|
38 |
+ dplyr::filter(.data$isTip) %>% |
|
39 |
+ dplyr::pull(.data$label) |
|
40 |
+ |
|
41 |
+ tip.order <- lapply(trees, function(x) { |
|
42 |
+ match(x$tip.label, first.label) |
|
43 |
+ }) |
|
44 |
+ |
|
45 |
+ tip.2.tip <- lapply(trees, cophenetic.phylo.check.length) |
|
46 |
+ tip.2.tip <- lapply(1:length(trees), function(i) { |
|
47 |
+ tip.2.tip[[i]][tip.order[[i]], tip.order[[i]]] |
|
48 |
+ }) |
|
49 |
+ |
|
50 |
+ all.tab <- do.call(rbind, tip.2.tip) |
|
51 |
+ rownames(all.tab) <- NULL |
|
52 |
+ |
|
53 |
+ distances <- stats::dist(t(all.tab)) |
|
54 |
+ |
|
55 |
+ res <- stats::cmdscale(distances, k=1) |
|
53 | 56 |
|
54 |
- tip.order <- labels[as.integer(trees.f[[tip.order]]$y)] |
|
55 |
- } |
|
56 |
- } |
|
57 |
+ tip.order <- first.label[order(res[,1])] |
|
58 |
+ } else if (as.numeric(tip.order) && tip.order <= length(trees)) { |
|
59 |
+ labels <- trees.f[[tip.order]] %>% |
|
60 |
+ dplyr::filter(.data$isTip) %>% |
|
61 |
+ dplyr::pull(.data$label) |
|
62 |
+ |
|
63 |
+ tip.order <- labels[as.integer(trees.f[[tip.order]]$y)] |
|
64 |
+ } |
|
65 |
+ } |
|
57 | 66 |
|
58 |
- # reorder tips (and shift x id dangling) |
|
67 |
+ ## reorder tips (and shift x id dangling) |
|
68 |
+ max.x <- vapply(trees.f, function(x) max(x$x, na.rm = TRUE), numeric(1)) |
|
69 |
+ farthest <- max(max.x) |
|
59 | 70 |
trees.f <- lapply(1:length(trees), function(i) { |
60 |
- trees.f[[i]]$y <- getYcoord_order(trees[[i]], tip.order) |
|
61 |
- if (i > 1) { |
|
62 |
- trees.f[[i]]$y[1:n.tips] %<>% add(rnorm(n.tips, mean=0, sd=jitter)) |
|
63 |
- if (dangle) |
|
64 |
- trees.f[[i]]$x <- trees.f[[1]]$x[1] - trees.f[[i]]$x[match(trees.f[[1]]$label, trees.f[[i]]$label)][1] + trees.f[[i]]$x |
|
65 |
- } |
|
66 |
- trees.f[[i]] |
|
71 |
+ trees.f[[i]]$y <- getYcoord(trees[[i]], tip.order = tip.order) |
|
72 |
+ if (dangle) { |
|
73 |
+ trees.f[[i]]$x <- trees.f[[i]]$x + (farthest - max.x[i]) |
|
74 |
+ } |
|
75 |
+ if (i > 1 && jitter > 0) { |
|
76 |
+ trees.f[[i]]$y[1:n.tips] %<>% add(stats::rnorm(n.tips, mean=0, sd=jitter)) |
|
77 |
+ } |
|
78 |
+ trees.f[[i]] |
|
67 | 79 |
}) |
68 |
- |
|
69 |
- # plot all trees together |
|
70 |
- p <- ggtree(tr=trees.f[[1]], mapping=mapping, layout=layout, ...) |
|
71 |
- for (x in trees.f[-1]) |
|
72 |
- p <- p + geom_tree(mapping=mapping, data=x, layout=layout, ...) |
|
73 |
- p |
|
80 |
+ |
|
81 |
+ ## plot all trees together |
|
82 |
+ p <- ggtree(tr=trees.f[[1]], mapping=mapping, layout=layout, ...) |
|
83 |
+ for (x in trees.f[-1]) |
|
84 |
+ p <- p + geom_tree(mapping=mapping, data=x, layout=layout, ...) |
|
85 |
+ p |
|
74 | 86 |
} |
75 | 87 |
|
76 | 88 |
## wrapper for cohpenetic to ensure that branch lengths exist |
77 | 89 |
cophenetic.phylo.check.length <- function(tree) { |
78 |
- if (is.null(tree$edge.length)) |
|
79 |
- tree$edge.length <- rep(1, nrow(tree$edge)) |
|
80 |
- |
|
81 |
- cophenetic(tree) |
|
82 |
-} |
|
83 |
- |
|
84 |
-## this is an adaptation of old code in ggtree |
|
85 |
-## |
|
86 |
-##' @importFrom magrittr %>% |
|
87 |
-##' @importFrom magrittr equals |
|
88 |
-getYcoord_order <- function(tr, tip.order) { |
|
89 |
- Ntip <- length(tr[["tip.label"]]) |
|
90 |
- N <- getNodeNum(tr) |
|
91 |
- |
|
92 |
- edge <- tr[["edge"]] |
|
93 |
- parent <- edge[,1] |
|
94 |
- child <- edge[,2] |
|
95 |
- |
|
96 |
- cl <- split(child, parent) |
|
97 |
- child_list <- list() |
|
98 |
- child_list[as.numeric(names(cl))] <- cl |
|
99 |
- |
|
100 |
- y <- numeric(N) |
|
101 |
- y[1:Ntip] <- match(tr$tip.label, tip.order) |
|
102 |
- y[-(1:Ntip)] <- NA |
|
103 |
- |
|
104 |
- pvec <- integer(max(tr$edge)) |
|
105 |
- pvec[child] = parent |
|
106 |
- |
|
107 |
- currentNode <- 1:Ntip |
|
108 |
- while(anyNA(y)) { |
|
109 |
- pNode <- unique(pvec[currentNode]) |
|
110 |
- |
|
111 |
- idx <- sapply(pNode, function(i) all(child_list[[i]] %in% currentNode)) |
|
112 |
- newNode <- pNode[idx] |
|
113 |
- |
|
114 |
- y[newNode] <- sapply(newNode, function(i) { |
|
115 |
- mean(y[child_list[[i]]], na.rm=TRUE) |
|
116 |
- }) |
|
117 |
- |
|
118 |
- currentNode <- c(currentNode[!currentNode %in% unlist(child_list[newNode])], newNode) |
|
119 |
- } |
|
120 |
- |
|
121 |
- return(y) |
|
90 |
+ if (is.null(tree$edge.length)) |
|
91 |
+ tree$edge.length <- rep(1, nrow(tree$edge)) |
|
92 |
+ |
|
93 |
+ stats::cophenetic(tree) |
|
122 | 94 |
} |
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,122 @@ |
1 |
+##' drawing phylogenetic trees from list of phylo objects |
|
2 |
+##' |
|
3 |
+##' @title ggdensitree |
|
4 |
+##' @param data a list of phylo objects or any object with an as.phylo and fortify method |
|
5 |
+##' @param mapping aesthetic mapping |
|
6 |
+##' @param layout one of 'slanted', 'rectangluar', 'fan', 'circular' or 'radial' (default: 'slanted') |
|
7 |
+##' @param tip.order the order of the tips by a character vector of taxa names; or an integer, N, to order the tips by the order of the tips in the Nth tree; or 'mds' to order the tips based on MDS of the distance between the tips (default: 'mds') |
|
8 |
+##' @param dangle TRUE to align trees by their tips and FALSE to align treesby their root (default: TRUE) |
|
9 |
+##' @param jitter deviation to jitter tips |
|
10 |
+##' @param ... additional parameters passed to fortify, ggtree and geom_tree |
|
11 |
+##' @return tree layer |
|
12 |
+##' @importFrom magrittr %<>% |
|
13 |
+##' @importFrom magrittr add |
|
14 |
+##' @export |
|
15 |
+##' @author Yu Guangchuang, Bradley R. Jones |
|
16 |
+##' @examples |
|
17 |
+##' require(ape) |
|
18 |
+##' require(dplyr) |
|
19 |
+##' library(ape) |
|
20 |
+##' library(tidyverse) |
|
21 |
+##' |
|
22 |
+##' trees <- list(read.tree(text="((a,b),c);"), read.tree(text="((a,c),b);")); |
|
23 |
+##' ggdensitree(trees) + geom_tiplab() |
|
24 |
+##' |
|
25 |
+##' trees.fort <- list(trees[[1]] %>% fortify %>% mutate(tree="a"), trees[[2]] %>% fortify %>% mutate(tree="b")); |
|
26 |
+##' ggdensitree(trees.fort, aes(colour=tree)) + geom_tiplab(colour='black') |
|
27 |
+ggdensitree <- function(data=NULL, mapping=NULL, layout="slanted", tip.order='mds', dangle=TRUE, jitter=0, ...) { |
|
28 |
+ # factorize to simplify code |
|
29 |
+ trees <- lapply(data, as.phylo) |
|
30 |
+ trees.f <- lapply(data, fortify, layout=layout, ...) |
|
31 |
+ n.tips <- sum(trees.f[[1]]$isTip) |
|
32 |
+ |
|
33 |
+ # determine tip order |
|
34 |
+ if (length(tip.order) == 1) { |
|
35 |
+ if (tip.order == 'mds') { |
|
36 |
+ first.label <- subset(trees.f[[1]], isTip)$label |
|
37 |
+ |
|
38 |
+ tip.order <- lapply(trees, . %$% tip.label %>% match(first.label)) |
|
39 |
+ |
|
40 |
+ tip.2.tip <- lapply(trees, cophenetic.phylo.check.length) |
|
41 |
+ tip.2.tip <- lapply(1:length(trees), function(i) tip.2.tip[[i]][tip.order[[i]], tip.order[[i]]]) |
|
42 |
+ |
|
43 |
+ all.tab <- do.call(rbind, tip.2.tip) |
|
44 |
+ rownames(all.tab) <- NULL |
|
45 |
+ |
|
46 |
+ distances <- dist(t(all.tab)) |
|
47 |
+ |
|
48 |
+ res <- cmdscale(distances, k=1) |
|
49 |
+ |
|
50 |
+ tip.order <- first.label[order(res[,1])] |
|
51 |
+ } else if (as.numeric(tip.order) && tip.order <= length(trees)) { |
|
52 |
+ labels <- subset(trees.f[[tip.order]], isTip)$label |
|
53 |
+ |
|
54 |
+ tip.order <- labels[as.integer(trees.f[[tip.order]]$y)] |
|
55 |
+ } |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ # reorder tips (and shift x id dangling) |
|
59 |
+ trees.f <- lapply(1:length(trees), function(i) { |
|
60 |
+ trees.f[[i]]$y <- getYcoord_order(trees[[i]], tip.order) |
|
61 |
+ if (i > 1) { |
|
62 |
+ trees.f[[i]]$y[1:n.tips] %<>% add(rnorm(n.tips, mean=0, sd=jitter)) |
|
63 |
+ if (dangle) |
|
64 |
+ trees.f[[i]]$x <- trees.f[[1]]$x[1] - trees.f[[i]]$x[match(trees.f[[1]]$label, trees.f[[i]]$label)][1] + trees.f[[i]]$x |
|
65 |
+ } |
|
66 |
+ trees.f[[i]] |
|
67 |
+ }) |
|
68 |
+ |
|
69 |
+ # plot all trees together |
|
70 |
+ p <- ggtree(tr=trees.f[[1]], mapping=mapping, layout=layout, ...) |
|
71 |
+ for (x in trees.f[-1]) |
|
72 |
+ p <- p + geom_tree(mapping=mapping, data=x, layout=layout, ...) |
|
73 |
+ p |
|
74 |
+} |
|
75 |
+ |
|
76 |
+## wrapper for cohpenetic to ensure that branch lengths exist |
|
77 |
+cophenetic.phylo.check.length <- function(tree) { |
|
78 |
+ if (is.null(tree$edge.length)) |
|
79 |
+ tree$edge.length <- rep(1, nrow(tree$edge)) |
|
80 |
+ |
|
81 |
+ cophenetic(tree) |
|
82 |
+} |
|
83 |
+ |
|
84 |
+## this is an adaptation of old code in ggtree |
|
85 |
+## |
|
86 |
+##' @importFrom magrittr %>% |
|
87 |
+##' @importFrom magrittr equals |
|
88 |
+getYcoord_order <- function(tr, tip.order) { |
|
89 |
+ Ntip <- length(tr[["tip.label"]]) |
|
90 |
+ N <- getNodeNum(tr) |
|
91 |
+ |
|
92 |
+ edge <- tr[["edge"]] |
|
93 |
+ parent <- edge[,1] |
|
94 |
+ child <- edge[,2] |
|
95 |
+ |
|
96 |
+ cl <- split(child, parent) |
|
97 |
+ child_list <- list() |
|
98 |
+ child_list[as.numeric(names(cl))] <- cl |
|
99 |
+ |
|
100 |
+ y <- numeric(N) |
|
101 |
+ y[1:Ntip] <- match(tr$tip.label, tip.order) |
|
102 |
+ y[-(1:Ntip)] <- NA |
|
103 |
+ |
|
104 |
+ pvec <- integer(max(tr$edge)) |
|
105 |
+ pvec[child] = parent |
|
106 |
+ |
|
107 |
+ currentNode <- 1:Ntip |
|
108 |
+ while(anyNA(y)) { |
|
109 |
+ pNode <- unique(pvec[currentNode]) |
|
110 |
+ |
|
111 |
+ idx <- sapply(pNode, function(i) all(child_list[[i]] %in% currentNode)) |
|
112 |
+ newNode <- pNode[idx] |
|
113 |
+ |
|
114 |
+ y[newNode] <- sapply(newNode, function(i) { |
|
115 |
+ mean(y[child_list[[i]]], na.rm=TRUE) |
|
116 |
+ }) |
|
117 |
+ |
|
118 |
+ currentNode <- c(currentNode[!currentNode %in% unlist(child_list[newNode])], newNode) |
|
119 |
+ } |
|
120 |
+ |
|
121 |
+ return(y) |
|
122 |
+} |