#' Define tree searches
#'
#' \code{TreeAnalysis} is an R6 class that stores the configuration for a
#' tree analysis, including the character-taxon matrix, taxon activity,
#' character weighting and constraints on monophyly.
#' @importFrom ape write.tree .compressTipLabel
#' @importFrom checkmate asInt assert check_character check_class check_disjunct
#' check_int check_flag check_multi_class check_null check_number
#' check_numeric check_r6 check_subset check_false makeAssertCollection
#' reportAssertions test_class test_multi_class test_null test_true
#' @importFrom cli cli_abort cli_text col_grey
#' @importFrom dplyr if_else
#' @importFrom glue glue
#' @importFrom lubridate day hour minute second seconds_to_period
#' @importFrom magrittr %>% %$% and
#' @importFrom R6 R6Class
#' @importFrom treeio as.treedata
#' @importFrom TreeTools PhyDatToMatrix RenumberTips TntOrder
#' @importFrom stringr str_replace str_replace_all str_split_1 str_starts
#' str_to_sentence str_trim str_wrap
#' @export
TreeAnalysis <- R6Class("TreeAnalysis",
private = list(
.discrete_matrix = NULL,
.continuous_matrix = NULL,
.inactive_taxa = NULL,
.outgroup = NULL,
.zlb_rule = NULL,
.constraints = NULL,
.method = NULL,
.weighting = NULL,
.start_trees = NULL,
.hold = NULL,
.max_ram = NULL,
.timeout = NULL
),
active = list(
#' @field discrete_matrix A \code{"\link{DiscreteMatrix}"} object.
discrete_matrix = function (value) {
if (missing(value)) {
return(private$.discrete_matrix)
} else {
coll <- makeAssertCollection()
val_check <- assert(
check_null(value),
assert(
check_r6(value),
check_class(value, "DiscreteMatrix"),
combine = "and",
add = coll
),
add = coll
)
val_check <- coll$getMessages()
if (!coll$isEmpty()) {
cli_abort(c("{.arg discrete_matrix} must be a valid matrix object.",
"x" = val_check))
}
if (!test_null(value)) {
if (!test_null(self$continuous_matrix)) {
val_check <- check_subset(value$taxa, self$continuous_matrix$taxa)
if (!test_true(val_check)) {
cli_abort(c("{.arg discrete_matrix} must be compatible with {.arg continuous_matrix}.",
"x" = val_check))
}
}
}
private$.discrete_matrix <- value
}
},
#' @field continuous_matrix A \code{"\link{ContinuousMatrix}"} object.
continuous_matrix = function (value) {
if (missing(value)) {
return(private$.continuous_matrix)
} else {
coll <- makeAssertCollection()
assert(
check_null(value),
assert(
check_r6(value),
check_class(value, "ContinuousMatrix"),
combine = "and", add = coll
),
add = coll
)
val_check <- coll$getMessages()
if (!coll$isEmpty()) {
cli_abort(c("{.arg continuous_matrix} must be a valid matrix object.",
"x" = val_check))
}
if (!test_null(value)) {
if (!test_null(self$discrete_matrix)) {
val_check <- check_subset(value$taxa, self$discrete_matrix$taxa)
if (!test_true(val_check)) {
cli_abort(c("{.arg continuous_matrix} must be compatible with {.arg discrete_matrix}.",
"x" = val_check))
}
}
}
private$.continuous_matrix <- value
}
},
#' @field method An object that contains configuration options for a tree
#' analysis method.
method = function (value) {
if (missing(value)) {
return(private$.method)
} else {
val_check <- check_multi_class(value, c("BootstrapOptions", "BranchBreakingOptions", "BranchSwappingOptions", "ConstraintSectorialSearchOptions", "DrivenSearchOptions", "ImplicitEnumerationOptions", "JackknifeOptions", "RandomSectorialSearchOptions", "RatchetOptions", "SymmetricResamplingOptions", "BranchSupportOptions"))
if (!test_true(val_check)) {
cli_abort(c("{.arg method} must contain valid tree analysis options.",
"x" = val_check))
}
set_sel_size <- function (driven_class) {
sect_classes <- sapply(driven_class$sectorial_search, class)
if ("RandomSectorialSearchOptions" %in% sect_classes) {
def_size <- min(c(as.integer(ceiling(nrow(matrix) / 2)), 45L))
idx <- which(sect_classes == "RandomSectorialSearchOptions")
if (driven_class$sectorial_search[[idx]]$max_size == 0) {
driven_class$sectorial_search[[idx]]$max_size <- def_size
}
if (driven_class$sectorial_search[[idx]]$min_size == 0) {
driven_class$sectorial_search[[idx]]$min_size <- def_size
}
}
return(driven_class)
}
if (test_class(value, "DrivenSearchOptions")) {
value <- set_sel_size(value)
}
if (test_class(value, c("BranchBreakOptions"))) {
val_check <- check_class(private$.start_trees, "multiPhylo")
if (!test_true(val_check)) {
cli_abort(c("{.arg start_trees} must be a {.cls multiPhylo} object if a {.arg method} is a {.cls {class(value)}} object.",
"x" = val_check))
}
}
if (test_multi_class(value, c("ResampleBaseOptions", "BranchSupportOptions"))) {
if (test_class(value$search_method, "DrivenSearchOptions")) {
value$search_method <- set_sel_size(value$search_method)
}
val_check <- check_class(private$.start_trees, "phylo")
if (!test_true(val_check)) {
cli_abort(c("{.arg start_trees} must be a {.cls phylo} object if a {.arg method} is a resampling analysis.",
"x" = val_check))
}
valid_zlb_rule <- c("maximum", "identical_states", "minimum")
val_check <- check_subset(self$zlb_rule, valid_zlb_rule)
if (!test_true(val_check)) {
val_check <- str_replace_all(val_check, "([\\{\\}])", "\\1\\1")
cli_abort(c("{.arg zlb_rule} must be valid for a resampling {.arg method}.",
"x" = val_check,
"i" = "Set {.arg zlb_rule} to {valid_zlb_rule}."))
}
tree_taxa <- self$start_trees$tip.label
all_mtx <- c(continuous = self$continuous_matrix,
discrete = self$discrete_matrix)
all_taxa <- all_taxa <- sapply(all_mtx, `[[`, "taxa") %>%
as.vector() %>%
unique()
inactive_taxa <- all_taxa[!all_taxa %in% tree_taxa]
if (length(inactive_taxa) > 0) {
self$inactive_taxa <- inactive_taxa
}
}
private$.method <- value
}
},
#' @field inactive_taxa A character vector indicating the taxa to be
#' inactivated.
inactive_taxa = function (value) {
if (missing(value)) {
return(private$.inactive_taxa)
} else {
all_mtx <- c(continuous = self$continuous_matrix,
discrete = self$discrete_matrix)
all_taxa <- sapply(all_mtx, `[[`, "taxa") %>%
as.vector() %>%
unique()
coll <- makeAssertCollection()
assert(
check_null(value),
check_character(value, min.len = 1, max.len = length(all_taxa),
any.missing = FALSE, unique = TRUE),
add = coll
)
val_check <- coll$getMessages()
if (!coll$isEmpty()) {
cli_abort(c("{.arg inactive_taxa} must be either a character vector or {.val NULL}.",
"x" = val_check))
}
# if (test_multi_class(value, c("ResampleBaseOptions", "BranchSupportOptions"))) {
# cli_abort(c("{.arg inactive_taxa} can't be modified for resampling analyses."))
# }
if (test_null(value)) {
private$.inactive_taxa <- NULL
return()
}
val_check <- check_subset(value, all_taxa)
if (!test_true(val_check)) {
val_check <- str_replace_all(val_check, "(\\{|\\})", "\\1\\1")
cli_abort(c("{.arg inactive_taxa} must contain taxa present in {.arg matrix}.",
"x" = val_check))
}
all_constrained <- Reduce(or, lapply(private$.constraints, function (c) {
unique(c$fixed_otus, c$floating_otus)
}))
val_check <- check_disjunct(all_constrained, value)
if (!test_true(val_check)) {
cli_abort(c("{.arg inactive_taxa} must not contain taxa that are in constraint group(s)",
"x" = inact_constr))
}
private$.inactive_taxa <- value
}
},
#' @field outgroup A single character vector indicating the taxon to be
#' the outgroup.
outgroup = function (value) {
if (missing(value)) {
return(private$.outgroup)
} else {
all_mtx <- c(continuous = self$continuous_matrix,
discrete = self$discrete_matrix)
all_taxa <- sapply(all_mtx, `[[`, "taxa") %>%
as.vector() %>%
unique()
coll = makeAssertCollection()
assert(
check_null(value),
assert(
check_string(value, min.chars = 1),
check_choice(value, all_taxa),
combine = "and", add = coll
),
add = coll
)
val_check <- str_replace_all(coll$getMessages(), "(\\{|\\})", "\\1\\1")
if (!coll$isEmpty()) {
mtx_args <- paste(names(all_mtx), "_matrix", sep = "")
cli_abort(c("{.arg outgroup} must be a taxon present in {.arg {mtx_args}}.",
"x" = val_check))
}
if (test_null(value)) {
value <- all_taxa[1]
}
private$.outgroup <- value
}
},
#' @field zlb_rule A character vector indicating the rule for handling zero
#' length branches. The options are:
#' \itemize{
#' \item \code{maximum}: collapse an interior branch of the maximum possible
#' length of the branch is zero;
#' \item \code{identical_states}: only collapse zero length branches if ancestor and descendant
#' states are the same;
#' \item \code{minimum}: collapse an interior branch if the minimum possible
#' length of the branch is zero (default);
#' \item \code{discard_tree}: discard all trees that must contain a zero length
#' branch;
#' \item \code{spr}: collapse an interior branch using subtree pruning and reconnection (SPR) operations; and
#' \item \code{tbr}: collapse an interior branch using tree bisection and reconnection (TBR) operations.
#' }
zlb_rule = function (value) {
options = c("maximum", "identical_states", "minimum", "discard_tree", "spr", "tbr")
if (missing(value)) {
options[private$.zlb_rule]
} else {
value <- match.arg(value, options)
val_check <- check_choice(value, options)
if (!test_true(val_check)) {
cli_abort(c("{.arg collapse} choice must be valid.",
"x" = val_check,
"i" = "Set {.arg zlb_rule} to {options}."))
}
if (test_multi_class(self$method, "ResampleBaseOptions")) {
valid_zlb_rule <- c("maximum", "identical_states", "minimum")
val_check <- check_subset(value, valid_zlb_rule)
if (!test_true(val_check)) {
val_check <- str_replace_all(val_check, "([\\{\\}])", "\\1\\1")
cli_abort(c("{.arg zlb_rule} must be valid for a resampling {.arg method}.",
"x" = val_check,
"i" = "Set {.arg zlb_rule} to {valid_zlb_rule}."))
}
}
value <- which(value == options)
private$.zlb_rule <- value
}
},
#' @field constraints One or more \code{"\link{MonophylyConstraintOptions}"} objects.
constraints = function (...) {
obj <- list(...)
if (length(obj) == 0) {
return(private$.constraints)
} else {
coll <- makeAssertCollection()
assert(
check_null(unlist(obj)),
check_list(obj, types = c("MonophylyConstraintOptions", "BackboneConstraintOptions")),
add = coll
)
val_check <- coll$getMessages()
if (!test_true(coll$isEmpty())) {
cli_abort(c("{.arg constraints} must be one or more {.cls MonophylyConstraintOptions} or {.cls BackboneConstraintOptions} objects.",
"x" = val_check))
}
if (test_null(unlist(obj))) {
return()
}
all_mtx <- c(self$continuous_matrix, self$discrete_matrix)
all_taxa <- sapply(all_mtx, `[[`, "taxa") %>%
as.vector() %>%
unique()
for (constraint in obj) {
if (test_class(obj, "MonophylyConstraintOptions")) {
all_const <- c(constraint$fixed_otus, constraint$floating_otus)
val_check <- check_subset(all_const, all_taxa)
if (!test_true(val_check)) {
val_check <- str_replace_all(val_check, "([\\{\\}])", "\\1\\1")
cli_abort(c("{.arg constraints} must contain taxa that are present in the matrix.",
"x" = val_check))
}
# Check if any constrained taxa are presently inactive
val_check <- check_disjunct(constraint$fixed_otus, self$inactive_taxa)
if (!test_true(val_check)) {
val_check <- str_replace_all(val_check, "([\\{\\}])", "\\1\\1")
cli_abort("Fixed constraints must not contain inactive taxa.",
"x" = val_check)
}
val_check <- check_disjunct(constraint$floating_otus, all_taxa)
if (!test_true(val_check)) {
val_check <- str_replace_all(val_check, "([\\{\\}])", "\\1\\1")
cli_abort("Floating constraints must not contain inactive taxa.",
"x" = val_check)
}
}
if (test_class(obj, "BackboneConstraintOptions")) {
val_check <- check_subset(constraint$topology$tip.label, self$inactive_taxa)
if (!test_true(val_check)) {
val_check <- str_replace_all(val_check, "([\\{\\}])", "\\1\\1")
cli_abort("Backbone constraints must contain taxa that are present in the matrix.",
"x" = val_check)
}
}
}
private$.constraints <- obj
}
},
#' @field weighting An object containing configuration options for character
#' weighting.
weighting = function (value) {
if (missing(value)) {
return(private$.weighting)
} else {
coll <- makeAssertCollection()
assert(
check_null(value),
check_class(value, "ImpliedWeightingOptions"),
add = coll
)
val_check <- reportAssertions(coll)
if (!test_true(val_check)) {
cli_abort(c("{.arg weighting} must contain a valid weighting configuration.",
"x" = val_check))
}
private$.weighting <- value
}
},
#' @field start_trees A \code{phylo} or \code{multiPhylo} of trees to load
#' prior to starting the tree analysis.
start_trees = function (value) {
if (missing(value)) {
return(private$.start_trees)
} else {
coll <- makeAssertCollection()
assert(
check_null(value),
assert(
check_class(value, "phylo"),
check_class(value, "multiPhylo")
),
add = coll
)
val_check <- coll$getMessages()
if (!coll$isEmpty()) {
cli_abort(c("{.arg start_trees} must be either {.val NULL}, or a {.cls phylo} or {.cls multiPhylo} object.",
"x" = val_check))
}
if (test_null(value) & test_multi_class(self$method, c("ResampleBaseOptions", "BranchSupportOptions", "BranchBreakOptions"))) {
cli_abort(c("{.arg start_trees} can't be {.val null} if {.arg method} is a {.cls {class(self$method)}} object."))
}
if (test_class(value, "phylo")) {
value$node.label <- NULL
} else if (test_class(value, "multiPhylo")) {
value <- lapply(value, function (x) {
x$node.label <- NULL
return(x)
}) %>%
.compressTipLabel()
}
private$.start_trees <- value
}
},
#' @field hold An integer indicating the number of trees to hold in TNT's
#' tree buffer.
hold = function (value) {
if (missing(value)) {
return(private$.hold)
} else {
val_check <- check_int(value, lower = 1)
if (!test_true(val_check)) {
cli_abort(c("{.arg hold} must be a valid numeric value.",
"x" = val_check))
}
value <- asInt(value)
private$.hold <- value
}
},
#' @field max_ram A numeric indicating the number of (binary) megabytes to
#' allocate for use by TNT.
max_ram = function (value) {
if (missing(value)) {
return(private$.max_ram)
} else {
val_check <- check_number(value, lower = 0)
if (!test_true(val_check)) {
cli_abort(c("{.max_ram} must be a valid numeric value.",
"x" = val_check))
}
private$.max_ram <- value
}
},
#' @field timeout A positive integer indicating the number of seconds to
#' allow a search to run for before terminating.
timeout = function (value) {
if (missing(value)) {
return(private$.timeout)
} else {
coll <- makeAssertCollection()
assert(
check_null(value),
check_int(value, lower = 1)
)
val_check <- reportAssertions(coll)
if (!test_true(val_check)) {
cli_abort(c("{.arg timeout} must be a valid numeric value.",
"x" = val_check))
}
private$.timeout <- value
}
}
),
public = list(
#' @param method An object that contains configuration options for the tree
#' analysis method.
#' @param discrete_matrix A \code{"\link{DiscreteMatrix}"} object.
#' @param continuous_matrix A \code{"\link{ContinuousMatrix}"} object.
#' @param inactive_taxa A character vector indicating the taxa to be
#' inactivated.
#' @param outgroup A single character vector indicating the taxon to be the
#' outgroup.
#' @param zlb_rule An integer indicating the rule for collapsing of zero
#' length branches. The options are:
#' \itemize{
#' \item \code{maximum}: collapse an interior branch of the maximum
#' possible length of the branch is zero;
#' \item \code{identical_states}: only collapse zero length branches if
#' ancestor and descendant states are the same;
#' \item \code{minimum}: collapse an interior branch if the minimum
#' possible length of the branch is zero (the default); and
#' \item \code{discard_tree}: discard all trees that must contain a zero
#' length branch.
#' \item \code{spr}: collapse an interior branch using subtree pruning and
#' reconnection (SPR) operations; and
#' \item \code{tbr}: collapse an interior branch using tree bisection and
#' reconnection (TBR) operations.
#' }
#' @param constraints One or more \code{"\link{MonophylyConstraintOptions}"} objects.
#' @param weighting An object containing configuration options for character
#' weighting.
#' @param start_trees A \code{phylo} or \code{multiPhylo} of trees to load
#' prior to starting the tree analysis.
#' @param hold An integer indicating the number of trees to hold in TNTs tree
#' buffer.
#' @param max_ram A numeric indicating the number of (binary) megabytes to
#' allocate for use by TNT.
#' @param timeout A positive integer indicating the number of seconds to
#' allow a search to run for before terminating.
initialize = function (method, discrete_matrix = NULL,
continuous_matrix = NULL, inactive_taxa = NULL,
outgroup = NULL, zlb_rule = "minimum",
constraints = NULL, weighting = NULL,
start_trees = NULL, hold = 100, max_ram = 16,
timeout = NULL) {
a <- as.list(environment(), all = TRUE)
# Check that at least one matrix type has been passed
coll <- makeAssertCollection()
val_check <- assert(
check_false(test_null(discrete_matrix)),
check_false(test_null(continuous_matrix)),
add = coll
)
val_check <- coll$getMessages()
if (!coll$isEmpty()) {
val_check <- str_split_1(val_check, "\n") %>% str_trim()
is_ul <- str_starts(val_check, "\\*")
val_check <- str_replace(val_check, "\\* ", "")
names(val_check) <- if_else(is_ul, "*", "x")
cli_abort(c("At least one matrix object must be provided.",
val_check))
}
# Check that resampling analyses have a start tree
if (test_multi_class(method, c("ResampleBaseOptions", "BranchSupportOptions"))) {
val_check <- check_class(start_trees, "phylo")
if (!test_true(val_check)) {
cli_abort(c("{.arg start_trees} must be a {.cls phylo} object when a {.arg method} is a resampling analysis.",
"x" = val_check))
}
self$start_trees <- start_trees
a$start_trees <- NULL
}
for (n in names(a)) {
self[[n]] <- a[[n]]
}
},
#' @param ... Ignored.
print = function (...) {
cli_text("{col_grey(\"# A TNT tree analysis\")}")
config <- c()
all_mtx <- c(continuous = self$continuous_matrix,
discrete = self$discrete_matrix)
which_mtx <- (!sapply(all_mtx, test_null)) %>%
{glue("{sum(.)} ({paste(names(all_mtx)[.], collapse = \", \")})")}
inactive_taxa <- self$inactive_taxa %>%
{ifelse(!test_null(.), as.character(length(.)), "None")}
n_constraints <- 0
if(!test_null(private$.constraints)) {
n_constraints <- length(private$.constraints)
}
weighting <- "Equal"
if (test_class(self$weighting, "ImpliedWeightingOptions")) {
weighting <- "Implied"
}
ta_method <- class(self$method)[1] %>%
str_replace_all(c("Options" = "", "([a-z])([A-Z])" = "\\1 \\2")) %>%
str_to_sentence()
config <- c("Character matrices:" = which_mtx,
"Inactive taxa:" = inactive_taxa,
"Outgroup:" = self$outgroup,
"Tree analysis method:" = ta_method,
"Zero-length branch rule:" = str_to_sentence(self$zlb_rule))
if (n_constraints > 0) {
config <- c(config,
"Constraints:" = n_constraints)
}
config <- c(config,
"Weighting:" = weighting) %>%
data.frame()
names(config) <- NULL
print(config)
},
#' @param ... Ignored.
queue = function (...) {
queue <- CommandQueue$new()
queue$add("echo", "=")
queue$add("screen", "25x10000")
queue$add("log", "stdout")
queue$add("silent", "=all")
queue$add("silent", "-console")
queue$add("collapse", private$.zlb_rule)
weight_queue <- NULL
if (test_class(self$weighting, c("ImpliedWeightingOptions", "R6"))) {
weight_queue <- self$weighting$queue()
weight_cmd <- weight_queue$read_next()
queue$add(weight_cmd$name, weight_cmd$arguments)
}
all_mtx <- c(
continuous = self$continuous_matrix,
discrete = self$discrete_matrix
)
all_taxa <- sapply(all_mtx, `[[`, "taxa") %>%
as.vector() %>%
unique()
queue$add("taxname", glue("+{nchar(all_taxa) %>% max() + 1}"))
mtx_chars <- sapply(all_mtx, `[[`, "n_characters")
xread <- glue("{n_char} {n_tax}",
n_char = sum(mtx_chars),
n_tax = length(all_taxa))
ccode <- c()
n <- 0
for (n_mtx in seq(all_mtx)) {
mtx <- all_mtx[[n_mtx]]
is_discrete <- test_class(mtx, c("DiscreteMatrix", "R6"))
is_continuous <- test_class(mtx, c("ContinuousMatrix", "R6"))
if (is_continuous) {
queue$add("nstates", "cont")
}
xread <- c(xread, mtx$queue()$read_next()$arguments)
if (is_discrete) {
if (!test_null(mtx$ordered)) {
ccode <- c(ccode, glue("+ {paste(mtx$ordered + n - 1, collapse = \" \")}"))
}
}
if (!test_null(mtx$inactive)) {
ccode <- c(ccode, glue("] {paste(mtx$inactive + n - 1, collapse = \" \")}"))
}
n <- n + mtx$n_characters
}
queue$add("xread", xread)
if (length(ccode) > 0) {
queue$add("ccode", paste(ccode, collapse = " "))
}
if (!test_null(private$.inactive_taxa)) {
queue$add("taxcode", glue("-{taxa}", taxa = paste(private$.inactive_taxa, collapse = " ")))
}
queue$add("hold", private$.hold)
queue$add("outgroup", private$.outgroup)
if (!test_null(weight_queue)) {
if (weight_queue$length() > 0) {
queue <- c(queue, weight_queue)
}
}
if (!test_null(private$.constraints)) {
all_args <- sapply(private$.constraints, function (x) x$queue()$read_next()$arguments)
queue$add("force", all_args)
queue$add("constrain", "=")
}
# if (!test_null(private$.timeout)) {
# td <- seconds_to_period(private$.timeout)
# tstr <- sprintf("%d:%02d:%02d", day(td) * 24 + hour(td), minute(td), second(td))
# tnt_cmds <- c(tnt_cmds, paste("timeout ", tstr, ";", collapse = ""))
# }
start_trees <- NULL
if (!test_null(self$start_trees)) {
start_trees <- write.tree(self$start_trees) %>%
paste(collapse = " ") %>%
str_replace_all(c("; " = "*;", ";$" = "", "\\),\\(" = "\\)\\(", "(,[^\\)]+)" = "\\1,", "\\)," = "\\)", "," = " ")) %>%
str_split_1(";")
queue$add("tread", start_trees)
}
if (test_multi_class(private$.method, c("ResampleBaseOptions", "BranchSupportOptions"))) {
queue$add("ttags", "=")
queue <- c(queue, private$.method$queue())
queue$add("ttags", ")")
if (test_multi_class(private$.method, "ResampleBaseOptions")) {
queue$add("unique")
}
queue$add("ttags", "/")
} else {
queue <- c(queue, private$.method$queue())
queue$add("condense")
queue$add("tplot", "*")
}
queue$add("length")
if (test_class(self$weighting, c("ImpliedWeightingOptions", "R6"))) {
queue$add("score")
}
queue$add("minmax", "-<")
queue$add("minmax", "->")
queue$add("zzz")
return(queue)
},
#' @param .envir The environment that TNT has been attached to.
run = function (.envir = parent.frame()) {
val_check <- check_environment(.envir)
if (!test_true(val_check)) {
cli_abort(c("{.arg .envir} must be an environment.",
"x" = val_check))
}
output <- execute_analysis(self, .envir)
output$queue <- self$queue()
all_taxa <- c(continuous = self$continuous_matrix,
discrete = self$discrete_matrix) %>%
sapply(`[[`, "taxa") %>%
as.vector() %>%
unique()
if (!test_null(output$tags)) {
output$phy <- output$tags$phy
output$tags <- output$tags$tags
output$tags$node <- output$tags$node + 1
}
output$phy <- lapply(output$phy, function (x) {
tip_order <- x$tip.label %>%
as.numeric() + 1
x$tip.label <- all_taxa[tip_order]
x <- RenumberTips(x, all_taxa[sort(tip_order)]) %>%
TntOrder()
return(x)
}) %>%
.compressTipLabel()
if (!test_null(output$legend)) {
# weight_legend <- ifelse(test_class(self$weighting, "ImpliedWeightingOptions"), "adjusted homoplasy score", "steps")
output$legend <- mutate(
output$legend,
# weight = weight_legend,
summary = if (test_class(self$method, "BranchSupportOptions")) ta$method$index_type else summary,
legend = glue("{type} ({summary})") %>% str_to_sentence())
names(output$tags)[-1] <- output$legend$legend
output$legend <- NULL
}
res <- do.call(TreeAnalysisResults$new, output)
return(res)
}
)
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.