emmanuelparadis / ape

analysis of phylogenetics and evolution

Home Page:http://ape-package.ird.fr/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ape::drop.tip() unrooting tree

HedvigS opened this issue · comments

Hello! I've got a question about how underlyingly ape::drop.tip() (and keep.tip()) deals with roots. I've got a tree which seems to have an odd way of being rooted, and when I prune it of tips using drop.tip() the returned tree is unrooted.

Here is the tree in question: glottolog_tree_newick_GB_pruned.txt

I tried three different ways of re-rooting the treem despite it already being rooted.

  1. placing a new node in the middle of the root edge with castor::root_in_edge()
  2. re-rooting with a known outgroup with ape::root()
  3. re-rooting via midpoint rooting with phytools::midpoint.root()

Out of these, (1) and (3) give me back a rooted tree after passing through ape::drop.tip(). (2) does not.

> library(ape)
> library(phytools)
> library(castor)
> library(tidyverse)
>         
> glottolog_tree <- ape::read.tree("glottolog_tree_newick_GB_pruned.txt")
> 
> glottolog_tree %>% ape::is.rooted()
[1] TRUE
> glottolog_tree %>% ape::drop.tip("louu1245") %>% ape::is.rooted()
[1] FALSE
> 
> #re-root in the middle of root edge
> root_edge <- glottolog_tree$root.edge
> glottolog_tree_rerooted_in_edge <- castor::root_in_edge(glottolog_tree, root_edge)
> 
> glottolog_tree_rerooted_in_edge %>% ape::is.rooted()
[1] TRUE
> glottolog_tree_rerooted_in_edge %>% ape::drop.tip("louu1245") %>% ape::is.rooted()
[1] TRUE
> 
> #reroot with known outgroup
> glottolog_tree_rerooted_outgroup <- ape::root(glottolog_tree, "muss1246")
> 
> glottolog_tree_rerooted_outgroup %>% ape::is.rooted()
[1] TRUE
> glottolog_tree_rerooted_outgroup %>% ape::drop.tip("louu1245") %>% ape::is.rooted()
[1] FALSE
> 
> #reroot with midpoint rooting
> glottolog_tree_rerooted_midpoint <- phytools::midpoint.root(glottolog_tree)
> 
> glottolog_tree_rerooted_midpoint %>% ape::is.rooted()
[1] TRUE
> glottolog_tree_rerooted_midpoint%>% ape::drop.tip("louu1245") %>% ape::is.rooted()
[1] TRUE

Any ideas about what might be going on :)?

There's a bug in ape: drop.tip() "forgets" the root edge of the tree, so it becomes unrooted.

R> tr <- read.tree("glottolog_tree_newick_GB_pruned.txt")
R> tr$root.edge
[1] 1
R> drop.tip(tr, "louu1245")$root.edge
NULL

I'll fix that and will update here.
Here's a (very) brief explanation: as rightly pointed out by @xrotwang, trees in Newick format are basically rooted, but with a few additional conventions, unrooted trees can be distinguished. A few basic examples to show how it works:

(a,b);     rooted
(a,b,c);   unrooted
(a,b,c):0; rooted

The last two trees are graphically identical. Your tree is similar (in structure) to the last one.

Aha, excellent. Thank you very much!!

In the meantime, I'll use approach (1) with castor::root_in_edge() for the scripts.

Fixed and pushed here. The help page has been clarified.

I also had this problem, that is, it return a unrooted tree after drop.tip some species. Is there a way to fix this bug so far? In addition to the above method: castor::root_in_edge()
Many thanks!

Did you try the version from here?

Thanks for your kindly reply.
I copy the code from https://github.com/emmanuelparadis/ape/blob/master/R/drop.tip.R
Then I used this version of function (drop.tip), but still got an error.
I am not sure if the code from this link is the version you mentioned.

This was fixed in ape 5.6, so the version on CRAN (and here too) has the fix.
Did you check that your tree has a root edge, eg, with is.null(tree$root.edge)?

The ape version I used is 5.6-2. The results of is.null(tree$root.edge) and is.rooted(tree) are TRUE.
Here is my data. I save the tree and the dropped-tip list to the Rdata.
data.for.drop.tip.Rdata.zip

so it's not the same situation than reported above. Maybe your tree is something like:

((a,b,c),d);

which is indeed rooted, but if d is dropped, it becomes unrooted.

It's likely because of that. Then, do you have any suggestions about how I should deal with this? Should I add a root to the pruned tree? Thanks!

Yes that's a possibility. Something like tree$root.edge <- 0 and the tree should be always rooted.

Got it. Thank you very much!!!

Can I ask a follow up re tree$root.edge <- 0 ? What does that actually... do? Sorry if it's a silly question. I understand that it adds a branch length to the root edge,but in cases like the ones above, what does that entail for where the root is?

Take a random unrooted tree:

R> library(ape)
R> tr <- rtree(10, rooted = FALSE)

All (internal) nodes are of degree 3 since the tree unrooted:

R> degree(tr)
  Degree  N
1      1 10
3      3  8

When representing the tree, one of these 8 nodes is (arbitrarily) the "root". For instance, you can print the Newick string of this tree which will show something like this:

(...... , ....... , ......);

The last right parenthesis is the "root" (even if the tree is unrooted). This is where the root edge is placed by the operation you mention. You can see that with a small function printing the last 4 characters of the Newick string:

f <- function(tr) {
    x <- write.tree(tr)
    n <- nchar(x)
    cat("......", substr(x, n - 3, n), "\n")
}
R> is.rooted(tr)
[1] FALSE
R> f(tr)
...... 19); 
R> tr$root.edge <- 0
R> is.rooted(tr)
[1] TRUE
R> f(tr)
...... ):0; 

Note that the branch lengths stored in $edge.length are not changed by the operation.