Skip to content

Commit

Permalink
v1.7.1: Patch edge length issues in AddTip() (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
ms609 committed Mar 25, 2022
1 parent 832d93a commit 0a9e1a4
Show file tree
Hide file tree
Showing 19 changed files with 393 additions and 372 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/R-CMD-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ jobs:
uses: r-lib/actions/setup-r-dependencies@v2
with:
needs: |
check
coverage
- name: Set up R dependencies (Non-Windows)
Expand All @@ -91,8 +90,6 @@ jobs:
with:
extra-packages: |
phangorn=?ignore-before-r=4.1.0
needs: |
check
- name: Set up pandoc
uses: r-lib/actions/setup-pandoc@v2
Expand Down
7 changes: 3 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: TreeTools
Title: Create, Modify and Analyse Phylogenetic Trees
Version: 1.7.0
Version: 1.7.1
Authors@R: c(
person("Martin R.", 'Smith', role = c("aut", "cre", "cph"),
email = "martin.smith@durham.ac.uk",
Expand Down Expand Up @@ -42,7 +42,7 @@ Imports:
fastmatch (>= 1.1.3),
methods,
R.cache,
Rdpack (>= 2.2),
Rdpack (>= 2.3),
Suggests:
spelling,
knitr,
Expand All @@ -52,9 +52,8 @@ Suggests:
shiny,
testthat (>= 3.0),
vdiffr (>= 1.0.0),
Config/Needs/check: rcmdcheck
Config/Needs/coverage: covr
Config/Needs/memcheck: devtools, rcmdcheck
Config/Needs/memcheck: devtools
Config/Needs/metadata: codemetar
Config/Needs/revdeps: revdepcheck
Config/Needs/website: pkgdown
Expand Down
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# TreeTools 1.7.1 #

- `AddTip()`: Fix bug when adding tip to root of weighted tree.


# TreeTools 1.7.0 #

## New methods and functions
Expand Down
201 changes: 201 additions & 0 deletions R/AddTip.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#' Add a tip to a phylogenetic tree
#'
#' `AddTip()` adds a tip to a phylogenetic tree at a specified location.
#'
#' `AddTip()` extends \code{\link{bind.tree}}, which cannot handle
#' single-taxon trees.
#'
#' @template treeParam
#' @param where The node or tip that should form the sister taxon to the new
#' node. To add a new tip at the root, use `where = 0`. By default, the
#' new tip is added to a random edge.
#' @param label Character string providing the label to apply to the new tip.
#' @param edgeLength Numeric specifying length of new edge
#' @param lengthBelow Numeric specifying length below neighbour at which to
#' graft new edge. Values greater than the length of the edge will result
#' in negative edge lengths. If `NULL`, the default, the new tip will be added
#' at the midpoint of the broken edge. If inserting at the root (`where = 0`),
#' a new edge of length `lengthBelow` will be inserted.
#' @param nTip,nNode,rootNode Optional integer vectors specifying number of tips and
#' nodes in `tree`, and index of root node.
#' Not checked for correctness: specifying values here trades code safety for a
#' nominal speed increase.
#'
#' @return `AddTip()` returns a tree of class `phylo` with an additional tip
#' at the desired location.
#'
#' @template MRS
#'
#' @seealso Add one tree to another: \code{\link{bind.tree}()}
#'
#' @examples
#' plot(tree <- BalancedTree(10))
#' ape::nodelabels()
#' ape::nodelabels(15, 15, bg='green')
#'
#' plot(AddTip(tree, 15, 'NEW_TIP'))
#'
#' @keywords tree
#' @family tree manipulation
#'
#' @export
AddTip <- function(tree,
where = sample.int(tree[["Nnode"]] * 2 + 2L, size = 1) - 1L,
label = "New tip",
edgeLength = 0,
lengthBelow = NULL,
nTip = NTip(tree),
nNode = tree[["Nnode"]],
rootNode = RootNode(tree)
) {
newTipNumber <- nTip + 1L
treeEdge <- tree[["edge"]]
edgeLengths <- tree[["edge.length"]]
lengths <- !is.null(edgeLengths)

if (is.character(where)) {
tmp <- match(where, TipLabels(tree))
if (is.na(tmp)) stop("No tip labelled '", where, "'")
where <- tmp
}
## find the row of 'where' before renumbering
if (where < 1L || where == rootNode) {
case <- 1L
} else {
insertionEdge <- which(treeEdge[, 2] == where)
case <- if (where <= nTip) 2L else 3L
}
# case = 1 -> y is bound on the root of x
# case = 2 -> y is bound on a tip of x
# case = 3 -> y is bound on a node of x

# Because in all situations internal nodes need to be
# renumbered, they are changed to negatives first, and
# nodes eventually added will be numbered sequentially
nodes <- treeEdge > nTip
treeEdge[nodes] <- nTip - treeEdge[nodes] # -1, ..., -nTip
nextNode <- -nNode - 1L
rootNode <- nTip - rootNode

switch(case, { # case = 1 -> y is bound on the root of x
treeEdge <- rbind(c(nextNode, treeEdge[1]), treeEdge, c(nextNode, newTipNumber))
if (lengths) {
if (is.null(lengthBelow)) {
lengthBelow <- 0
}
edgeLengths <- c(lengthBelow, edgeLengths, edgeLength)
}
rootNode <- nextNode
}, { # case = 2 -> y is bound on a tip of x
beforeInsertion <- seq_len(insertionEdge)
treeEdge[insertionEdge, 2] <- nextNode
treeEdge <- rbind(treeEdge[beforeInsertion, ],
c(nextNode, where),
c(nextNode, newTipNumber),
treeEdge[-beforeInsertion, ])
if (lengths) {
if (is.null(lengthBelow)) {
lengthBelow <- edgeLengths[insertionEdge] / 2L
}
edgeLengths <- c(edgeLengths[beforeInsertion[-insertionEdge]],
edgeLengths[insertionEdge] - lengthBelow,
lengthBelow,
edgeLength,
edgeLengths[-beforeInsertion])
}
}, { # case = 3 -> y is bound on a node of x
beforeInsertion <- seq_len(insertionEdge)

treeEdge <- rbind(treeEdge[beforeInsertion, ],
c(nextNode, newTipNumber),
c(nextNode, treeEdge[insertionEdge, 2]),
treeEdge[-beforeInsertion, ])
treeEdge[insertionEdge, 2] <- nextNode

if (lengths) {
if (is.null(lengthBelow)) {
lengthBelow <- edgeLengths[insertionEdge] / 2L
}
edgeLengths <- c(edgeLengths[beforeInsertion[-insertionEdge]],
edgeLengths[insertionEdge] - lengthBelow,
edgeLength,
lengthBelow,
edgeLengths[-beforeInsertion])
}

}
)
tree[["tip.label"]] <- c(tree[["tip.label"]], label)

nNode <- nNode + 1L
tree[["Nnode"]] <- nNode

## renumber nodes:
newNumbering <- integer(nNode)
newNumbering[-rootNode] <- newTipNumber + 1L
childNodes <- treeEdge[, 2] < 0L

## executed from right to left, so newNb is modified before x$edge:
treeEdge[childNodes, 2] <-
newNumbering[-treeEdge[childNodes, 2]] <-
newTipNumber + 2:nNode
treeEdge[, 1] <- newNumbering[-treeEdge[, 1]]

tree[["edge"]] <- treeEdge
if (lengths) {
tree[["edge.length"]] <- edgeLengths
}

# Return:
tree
}

#' @rdname AddTip
#'
#' @details `AddTipEverywhere()` adds a tip to each edge in turn.
#'
#' @param includeRoot Logical; if `TRUE`, each position adjacent
#' to the root edge is considered to represent distinct edges; if `FALSE`,
#' they are treated as a single edge.
#' @return `AddTipEverywhere()` returns a list of class `multiPhylo` containing
#' the trees produced by adding `label` to each edge of `tree` in turn.
#'
#' @examples
#' oldPar <- par(mfrow = c(2, 4), mar = rep(0.3, 4), cex = 0.9)
#'
#' backbone <- BalancedTree(4)
#' # Treating the position of the root as instructive:
#' additions <- AddTipEverywhere(backbone, includeRoot = TRUE)
#' xx <- lapply(additions, plot)
#'
#' par(mfrow=c(2, 3))
#' # Don't treat root edges as distinct:
#' additions <- AddTipEverywhere(backbone, includeRoot = FALSE)
#' xx <- lapply(additions, plot)
#'
#' par(oldPar)
#'
#' @importFrom ape is.rooted
#' @export
AddTipEverywhere <- function(tree, label = "New tip", includeRoot = FALSE) {
nTip <- NTip(tree)
if (nTip == 0L) return(list(SingleTaxonTree(label)))
if (nTip == 1L) return(list(StarTree(c(tree[["tip.label"]], label))))
whichNodes <- seq_len(nTip + tree[["Nnode"]])
edge <- tree[["edge"]]
root <- RootNode(edge)
if (!includeRoot) {
parent <- edge[, 1]
child <- edge[, 2]
rootChildren <- child[parent == root]

whichNodes <- if (length(rootChildren) == 2L && nTip > 2L) {
rootChildrenNodes <- rootChildren[rootChildren > nTip]
whichNodes[-c(root, rootChildrenNodes[1])]
} else {
whichNodes[-root]
}
}
lapply(whichNodes, AddTip, tree = tree, label = label, nTip = nTip,
rootNode = root)
}
42 changes: 21 additions & 21 deletions R/RoguePlot.R
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,31 @@
#' of the consensus tree.
#' @references \insertAllCited{}
#' @examples
#' trees <- list(read.tree(text = '(a, (b, (c, (rogue, (d, (e, f))))));'),
#' read.tree(text = '(a, (b, (c, (rogue, (d, (e, f))))));'),
#' read.tree(text = '(a, (b, (c, (rogue, (d, (e, f))))));'),
#' read.tree(text = '(a, (b, (c, (rogue, (d, (e, f))))));'),
#' read.tree(text = '(rogue, (a, (b, (c, (d, (e, f))))));'),
#' read.tree(text = '((rogue, a), (b, (c, (d, (e, f)))));'),
#' read.tree(text = '(a, (b, ((c, d), (rogue, (e, f)))));'),
#' read.tree(text = '(a, (b, ((c, (rogue, d)), (e, f))));'),
#' read.tree(text = '(a, (b, (c, (d, (rogue, (e, f))))));'))
#' RoguePlot(trees, 'rogue')
#' trees <- list(read.tree(text = "(a, (b, (c, (rogue, (d, (e, f))))));"),
#' read.tree(text = "(a, (b, (c, (rogue, (d, (e, f))))));"),
#' read.tree(text = "(a, (b, (c, (rogue, (d, (e, f))))));"),
#' read.tree(text = "(a, (b, (c, (rogue, (d, (e, f))))));"),
#' read.tree(text = "(rogue, (a, (b, (c, (d, (e, f))))));"),
#' read.tree(text = "((rogue, a), (b, (c, (d, (e, f)))));"),
#' read.tree(text = "(a, (b, ((c, d), (rogue, (e, f)))));"),
#' read.tree(text = "(a, (b, ((c, (rogue, d)), (e, f))));"),
#' read.tree(text = "(a, (b, (c, (d, (rogue, (e, f))))));"))
#' RoguePlot(trees, "rogue")
#' @template MRS
#' @importFrom fastmatch fmatch %fin%
#' @importFrom graphics par
#' @importFrom grDevices colorRamp colorRampPalette rgb
#' @family consensus tree functions
#' @export
RoguePlot <- function(trees, tip, p = 1, plot = TRUE,
Palette = colorRampPalette(c(par('fg'), '#009E73'),
space = 'Lab'),
nullCol = rgb(colorRamp(unlist(par(c('fg', 'bg'))),
space = 'Lab')(0.8) / 255),
edgeLength = NULL,
thin = par('lwd'), fat = thin + 1L,
outgroupTips,
...) {
Palette = colorRampPalette(c(par("fg"), "#009E73"),
space = "Lab"),
nullCol = rgb(colorRamp(unlist(par(c("fg", "bg"))),
space = "Lab")(0.8) / 255),
edgeLength = NULL,
thin = par("lwd"), fat = thin + 1L,
outgroupTips,
...) {
tipLabels <- TipLabels(trees[[1]])
nTip <- length(tipLabels)

Expand All @@ -69,12 +69,12 @@ RoguePlot <- function(trees, tip, p = 1, plot = TRUE,
attributes(trees) <- at

noRogue <- trees
attr(noRogue, 'TipLabel') <- NULL
attr(noRogue, "TipLabel") <- NULL
noRogue[] <- lapply(noRogue, DropTip, tip)
dummyRoot <- 'xxTREETOOLSxxDUMMYxxROOTxx'
dummyRoot <- "xxTREETOOLSxxDUMMYxxROOTxx"
# TODO replace with noRogue[] <- again
noRogue[] <- lapply(noRogue, AddTip, 0, dummyRoot)
class(noRogue) <- 'multiPhylo'
class(noRogue) <- "multiPhylo"
cons <- RootTree(Consensus(noRogue, p = p, check.labels = FALSE),
dummyRoot) # RootTree gives Preorder
consTip <- NTip(cons)
Expand Down
Loading

0 comments on commit 0a9e1a4

Please sign in to comment.