##' @importFrom ape as.phylo
##' @export
ape::as.phylo
##' @method as.phylo tbl_df
##' @importFrom tibble as_tibble
##' @importFrom dplyr mutate_if
##' @export
as.phylo.tbl_df <- function(x, branch.length, label, ...) {
x <- data.frame(x) %>% mutate_if(is.factor, as.character)
branch.length <- rlang::enquo(branch.length)
label <- rlang::enquo(label)
length_var <- root.edge <- edge.length <- NULL
tip.label <- node.label <- labels <- NULL
if (nrow(unique(x[, 1, drop=FALSE])) > nrow(unique(x[, 2, drop=FALSE]))){
x %<>% dplyr::select(rev(seq_len(2)), seq_len(ncol(x)))
}
if (!rlang::quo_is_missing(branch.length)){
edge.length <- as.numeric(x %>% dplyr::pull(!!branch.length))
length_var <- rlang::as_name(branch.length)
}
if (!rlang::quo_is_missing(label)){
labels <- x %>% dplyr::pull(!!label) %>% as.character()
}else{
labels <- x %>% dplyr::pull(2) %>% as.character()
}
edge <- check_edgelist(x)
indx <- attr(edge, "indx")
if (is.null(indx)){
indx <- c(FALSE, rep(TRUE, nrow(edge)))
labels <- c(unique(edge[,1][!edge[,1] %in% edge[,2]]), labels)
if (!is.null(edge.length)){
edge.length <- c(0, edge.length)
}
}
isTip <- !edge[,2] %in% edge[,1]
index <- rep(NA, length(isTip))
index[isTip] <- seq_len(sum(isTip))
index[!isTip] <- seq(sum(isTip)+2, length(isTip)+1)
mapping <- data.frame(node=index, labelnames=edge[,2], isTip)
parent <- mapping[match(edge[,1], mapping$labelnames), "node"]
child <- mapping[match(edge[,2], mapping$labelnames), 'node']
edge <- as.matrix(cbind(parent, child))
colnames(edge) <- NULL
edge[is.na(edge)] <- sum(isTip) + 1
if (!is.null(edge.length)){
root.edge <- edge.length[!indx]
if (length(root.edge)==0 || is.na(root.edge)){
root.edge <- NULL
}
edge.length <- edge.length[indx]
}
if (!is.null(labels)){
root.label <- labels[!indx]
labels <- labels[indx]
tip.label <- labels[isTip]
node.label <- c(root.label, labels[!isTip])
if (all(is.na(node.label))){
node.label <- NULL
}
}
Nnode <- length(unique(as.vector(edge))) - sum(isTip)
phylo <- list(
edge = edge,
Nnode = Nnode,
tip.label = tip.label,
edge.length = edge.length
)
class(phylo) <- 'phylo'
phylo$root.edge <- root.edge
phylo$node.label <- node.label
attr(phylo, "length_var") <- length_var
return(phylo)
}
##' @method as.phylo phylo4
##' @export
as.phylo.phylo4 <- function(x, ...) {
edge <- x@edge
edge.filter <- edge[,1] != 0
edge <- edge[edge.filter, ]
edge.length <- x@edge.length
edge.length <- edge.length[edge.filter]
tip.id <- sort(setdiff(edge[,2], edge[,1]))
tip.label <- x@label[tip.id]
phylo <- list(edge = edge,
edge.length = edge.length,
tip.label = tip.label)
node.id <- sort(unique(edge[,1]))
node.id <- node.id[node.id != 0]
node.label <- x@label[node.id]
if (!all(is.na(node.label))) {
phylo$node.label <- node.label
}
phylo$Nnode <- length(node.id)
class(phylo) <- "phylo"
return(phylo)
}
##' @method as.phylo data.frame
##' @export
as.phylo.data.frame <- as.phylo.tbl_df
##' @method as.phylo matrix
##' @export
as.phylo.matrix <- as.phylo.tbl_df
##' @method as.phylo pvclust
##' @export
as.phylo.pvclust <- function(x, ...) {
as.phylo_hclust_node(x$hclust, ...)
}
##' @method as.phylo ggtree
##' @export
as.phylo.ggtree <- function(x, ...) {
d <- as_tibble(x$data)
class(d) <- c("tbl_tree", "tbl_df", "tbl", "data.frame")
as.phylo(d)
}
##' @method as.phylo igraph
##' @importFrom rlang check_installed
##' @export
as.phylo.igraph <- function(x, branch.length, ...) {
branch.length <- rlang::enquo(branch.length)
check_installed('igraph', 'for `as.phylo()` with igraph.')
edge <- igraph::as_data_frame(x)
if (!igraph::is_tree(x)){
cli::cli_abort("The graph is not a root graph.")
}
trash <- try(
silent = TRUE,
expr = {
x <- as.phylo(edge, branch.length=!!branch.length)
}
)
if (inherits(trash, 'try-error')){
trash <- try(
silent = TRUE,
expr = {
x <- .as.phylo.rev.edges(edge, branch.length = !!branch.length)
}
)
}
if (inherits(trash, 'try-error')){
cli::cli_abort("The igraph is a network not a tree graph.")
}else{
return(x)
}
}
.as.phylo.rev.edges <- function(x, branch.length){
branch.length <- rlang::enquo(branch.length)
edge <- as.matrix(x[,c(1,2)])
col.nm <- colnames(x)
keep.ind <- !duplicated(edge)
x <- x[keep.ind,]
edge <- edge[keep.ind,]
if (any(duplicated(x[,2]))){
edge <- .adjust.tree.network.edge(edge)
}
x <- cbind(edge, x[,-c(1,2)])
colnames(x) <- col.nm
x <- as.phylo(x, branch.length = !!branch.length)
return(x)
}
##' @method as.phylo list
##' @export
as.phylo.list <- function(x, ...){
max.depth <- purrr::vec_depth(x)
while(max.depth > 1){
trash = try(silent = TRUE,
expr = {
x <- purrr::map_depth(x, max.depth - 1, paste_nested_list)
}
)
max.depth <- max.depth - 1
}
x <- lapply(x, .paste0_)
x <- .paste1_(x)
x <- paste0(x, collapse=',')
x <- paste0('(', x, ');')
x <- ape::read.tree(text = x)
return(x)
}
##' @method as.phylo dendro
##' @export
as.phylo.dendro <- function(x, ...){
seg.da <- x$segments
lab.da <- x$labels
inode.da <- seg.da[,c(1, 2)] |>
dplyr::bind_rows(
seg.da[,c(3, 4)] |>
dplyr::rename(x="xend", y="yend")
)
root.index <- apply(inode.da, 1,
function(x)!any(x[[1]] == seg.da$xend & x[[2]] == seg.da$yend),
simplify=F) |>
unlist(use.names=F)
root.da <- unique(inode.da[root.index, ])
inode.da <- inode.da[apply(inode.da, 1,
function(x)sum(x[[1]]==inode.da[,1] & x[[2]]==inode.da[,2])) != 2,]
dt <- unique(rbind(lab.da[,c(1, 2)], root.da, inode.da))
rownames(dt) <- seq_len(nrow(dt))
parent.id <- do.call('rbind',
apply(dt, 1, function(x)check.inode(x=x, y=dt, z=seg.da, root.id=root.da), simplify=FALSE)
)
edge <- cbind(as.numeric(parent.id), as.numeric(rownames(dt)))
edge <- edge[!edge[,1]==edge[,2],]
tr <- structure(
.Data = list(
edge = edge,
edge.length = abs(dt[edge[,2],2] - dt[edge[,1],2]),
tip.label = lab.da$label,
Nnode = nrow(dt) - nrow(lab.da)
),
class = 'phylo'
)
return(tr)
}
check_edgelist <- function(edgelist) {
if (dim(edgelist)[2] < 2)
stop("input should be a matrix of edge list that holds the relationships in the first two columns")
if (length(unique(edgelist[,1,drop=TRUE])) > length(unique(edgelist[,2,drop=TRUE]))) {
children <- edgelist[,1,drop=TRUE]
parents <- edgelist[,2,drop=TRUE]
} else {
children <- edgelist[,2,drop = TRUE]
parents <- edgelist[,1,drop = TRUE]
}
root1 <- unique(parents[!(parents %in% children)])
root2 <- unique(parents[parents == children])
if ((length(root1) != 1 && length(root2) != 1 )|| any(duplicated(children)))
stop("Cannot find root. network is not a tree!")
if (length(root1) != 1 && length(root2) == 1){
indx <- parents != children
parents <- parents[indx]
children <- children[indx]
edge <- matrix(c(parents, children), ncol=2)
edge <- .adjust.tree.network.edge(edge)
attr(edge, "indx") <- indx
}else{
edge <- matrix(c(parents, children), ncol=2)
edge <- .adjust.tree.network.edge(edge)
}
return (edge)
}
.paste1_ <- function(x){
index <- grepl('\\),', x)
if (any(index)){
x[index] <- paste0("(", x[index],")", names(x[index]))
x[!index] <- paste0(x[!index], names(x[!index]))
}else{
x <- paste0(x, names(x))
}
return(x)
}
check.inode <- function(x, y, z, root.id){
check.root <- x[[1]] == root.id[[1]] && x[[2]] == root.id[[2]]
if (check.root){
nodeID <- rownames(y[x[[1]]==y[,1] & x[[2]]==y[,2], ])
return(nodeID)
}
tmp <- z[x[[1]]==z[,3] & x[[2]] == z[,4],]
x <- tmp[,c(1,2)]
nodeID <- rownames(y[x[[1]] == y[,1] & x[[2]] == y[,2],])
check.root <- x[[1]] == root.id[[1]] && x[[2]] == root.id[[2]]
if (length(nodeID)>0 || check.root){
return(nodeID)
}else{
check.inode(x=x, y=y, z=z, root.id=root.id)
}
}
.paste0_ <- function(x){
flag <- grepl("^\\(\\(", x) && grepl("\\)\\)$", x)
if (flag){
x <- gsub("^\\(\\(", "\\(", x)
x <- gsub('\\)\\)$', "\\)", x)
}else{
x <- paste0('(', x, ')', names(x), collapse=',')
}
return(x)
}
paste_nested_list <- function(x){
if (length(x)>1){
if (length(names(x))!=0){
x <- paste0(paste0(x, names(x)), collapse=',')
}else{
x <- paste0("(", paste0(x, collapse=','), ")")
}
}else{
if (!(grepl("^\\(", x) && grepl("\\)$", x))){
x <- paste0('(', x, ')', names(x))
}else{
x <- paste0(x, names(x))
}
}
return(x)
}
.adjust.tree.network.edge <- function(x){
tip.nodes <- names(which(table(x) == 1))
if (.check.no.tree.network(x, tip.nodes)){
cli::cli_abort("The edge is not a edge of tree.")
}
x <- .rev.edge(x, tip.nodes, index = 1)
internal.nodes <- names(which(table(x[,2])>1))
while(length(internal.nodes) > 0){
x <- .rev.edge(x, internal.nodes, index = 2)
internal.nodes <- names(which(table(x[,2])>1))
}
return(x)
}
##' access phylo slot
##'
##'
##' @title get.tree
##' @param x tree object
##' @param ... additional parameters
##' @return phylo object
##' @export
##' @author Guangchuang Yu
get.tree <- function(x, ...) {
as.phylo(x, ...)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.