Thursday, May 18, 2023

Generic "combine" (c) method for "simmap" and "multiSimmap" object classes

This afternoon I spent an inordinate amount of time trying to create generic c (“combine”) method for the phytools "simmap" and "multiSimmap" object types.

I’m not sure how I hadn’t done this before, but perhaps it’s because the c.phylo and c.multiPhylo methods from ape work perfectly well on these object types – just producing "multiPhylo" rather than "multiSimmap" objects in return!

(Normally, this will probably be OK, but there are some circumstances in which it will cause problems.)

At first, I imagined writing the simple method as follows.

c.simmap<-function(...){
  object<-c.phylo(...)
  class(object)<-union("simmap",class(object))
  object
}

Unfortunately, c.phylo is not exported by the namespace of ape and replacing it with ape:::c.phylo is not allowed by CRAN policies!

Instead, I thought I would copy & (lightly) adapt the code of ape:::c.phylo as follows.

## adapted from c.phylo in ape (Paradis & Schliep 2019)
c.simmap<-function(...,recursive=TRUE){
  obj<-list(...)
  classes<-lapply(obj,class)
  isphylo<-sapply(classes,function(x) "simmap" %in% x)
  if(all(isphylo)){
    class(obj)<-c("multiSimmap","multiPhylo")
    return(obj)
  }
  if(!recursive) return(obj)
  ismulti<-sapply(classes, function(x) "multiSimmap" %in% x)
  if(all(isphylo|ismulti)){
    result<-list()
    j<-1
    for(i in 1:length(isphylo)){
      if(isphylo[i]){ 
        result[[j]]<-obj[[i]]
        j<-j+1
      } else {
        n<-length(obj[[i]])
        result[0:(n-1)+j]<-.uncompressTipLabel(obj[[i]])
        j<-j+n
      }
    }
    class(result)<-c("multiSimmap","multiPhylo")
    obj<-result
  } else {
    msg<-paste("some objects not of class \"simmap\" or",
      "\"multiSimmap\": argument recursive=TRUE ignored")
    warning(msg)
  }
  return(obj)
}

We can do a similar thing for the "multiSimmap" object.

## adapted from c.multiPhylo in ape (Paradis & Schliep 2019)
c.multiSimmap<-function(...,recursive=TRUE){
  obj<-list(...)
  if(!recursive) return(obj)
  classes<-lapply(obj,class)
  isphylo<-sapply(classes,function(x) "simmap" %in% x)
  ismulti<-sapply(classes, function(x) "multiSimmap" %in% x)
  if(all(isphylo|ismulti)){
    result<-list()
    j<-1
    for(i in 1:length(isphylo)){
      if(isphylo[i]){ 
        result[[j]]<-obj[[i]]
        j<-j+1
      } else {
        n<-length(obj[[i]])
        result[0:(n-1)+j]<-.uncompressTipLabel(obj[[i]])
        j<-j+n
      }
    }
    class(result)<-c("multiSimmap","multiPhylo")
    obj<-result
  } else {
    msg<-paste("some objects not of class \"simmap\" or",
      "\"multiSimmap\": argument recursive=TRUE ignored")
    warning(msg)
  }
  return(obj)
}

Let’s try it out.

library(phytools)
data(sunfish.tree)
data(sunfish.data)
feeding_mode<-setNames(sunfish.data$feeding.mode,
  rownames(sunfish.data))
ard_fit<-fitMk(sunfish.tree,feeding_mode,
  model="ARD",pi="fitzjohn")

First, combine "simmap" objects.

first_simmap<-simmap(ard_fit,nsim=1)
first_simmap
## 
## Phylogenetic tree with 28 tips and 27 internal nodes.
## 
## Tip labels:
## 	Acantharchus_pomotis, Lepomis_gibbosus, Lepomis_microlophus, Lepomis_punctatus, Lepomis_miniatus, Lepomis_auritus, ...
## 
## The tree includes a mapped, 2-state discrete character
## with states:
## 	non, pisc
## 
## Rooted; includes branch lengths.
second_simmap<-simmap(ard_fit,nsim=1)
second_simmap
## 
## Phylogenetic tree with 28 tips and 27 internal nodes.
## 
## Tip labels:
## 	Acantharchus_pomotis, Lepomis_gibbosus, Lepomis_microlophus, Lepomis_punctatus, Lepomis_miniatus, Lepomis_auritus, ...
## 
## The tree includes a mapped, 2-state discrete character
## with states:
## 	non, pisc
## 
## Rooted; includes branch lengths.
two_simmaps<-c(first_simmap,second_simmap)
two_simmaps
## 2 phylogenetic trees with mapped discrete characters

Now, let’s combine two "multiSimmap" objects. I imagine this is more combine because these could be, for example, results from two separate stochastic mapping analyses.

first_hundred<-simmap(ard_fit)
first_hundred
## 100 phylogenetic trees with mapped discrete characters
second_hundred<-simmap(ard_fit)
second_hundred
## 100 phylogenetic trees with mapped discrete characters
twohundred_simmap<-c(first_hundred,second_hundred)
twohundred_simmap
## 200 phylogenetic trees with mapped discrete characters

Neat.

Just as with ape c.phylo and c.multiPhylo, we should be able to combine objects of the two different classes.

abunchof_simmaps<-c(first_hundred,first_simmap,
  second_hundred,second_simmap)
abunchof_simmaps
## 202 phylogenetic trees with mapped discrete characters

Just for Twitter, let’s see what 202 phylogenies look like plotted.

layout(matrix(c(203,1:202,204),12,17))
par(bg="black")
cols<-setNames(viridisLite::viridis(n=2),
  levels(feeding_mode))
cols<-setNames(palette()[c(4,2)],levels(feeding_mode))
plot(abunchof_simmaps,cols,ftype="off",lwd=2)

plot of chunk unnamed-chunk-19

This will be in phytools soon.

No comments:

Post a Comment

Note: due to the very large amount of spam, all comments are now automatically submitted for moderation.