library(rgl) knitr::knit_hooks$set(webgl = hook_webgl)
library(treats) set.seed(123)
One other major feature of the treats
package is that it allows simulations to run with specified events that occur during the simulation.
These are typically events that can drastically change the course of the simulation.
For example, you might want simulate a mass extinction at some specific point in time.
However, these can also be more subtle, like the internal change of parameter values when reaching a specific trait value.
These are all handled by treats
with the events
object that you can create using make.events
and has the same overall logic as make.traits
and make.modifiers
.
events
require three main arguments:
* the target
which designates what the extinction should affect (e.g. the taxa, the speciation rate, etc...);
* the condition
which designates when to trigger the event;
* the modification
which designates what to modify when the event is triggered.
There are several more arguments that can be passed to make.events
but they are discussed later on.
First let's focus on these three main arguments:
condition, modification, add, test = TRUE, event.name, replications = 0, additional.args )
function | arguments | input | what does it do
---------|-----------|-------|----------------
make.events
| target | any of the following: "taxa", "bd.params", "traits", "modifiers" or "founding" | what aspect of the simulation should the event target
make.events
| condition | a function | a function that affects the condition of when to trigger the event (see below)
make.events
| modification* | a function | a function that applies the modification to the target if the condition is triggered (see below)
make.events
| add | a treats
object | another event generated by make.events
make.events
| test | logical | whether to test the validity of the event
make.events
| event.name | a character string | the name of the event
make.events
| replications | a number | if possible how many times is the event allowing to be repeated
make.events
| additional.args | ... | any additional arguments to be passed to the event
Here is a list of implemented conditions
(see ?events.conditions
):
function | arguments | input | what does it do
---------|-----------|-------|----------------
age.condition
, taxa.condition
, trait.condition
| x | the value to check the target against | e.g. time = x, taxa = x or trait = x (see below)
... | condition | a relational operator | e.g. ==
, >
, !=
, etc. for asking for example time == x
... | ... | any additional arguments to be passed to the event | |
Here is a list of implemented modifications
(see ?events.modifications
):
function | arguments | input | what does it do
---------|-----------|-------|----------------
random.extinction
| x | a numeric value | the proportion of species to make extinct
trait.extinction
| x | a numeric value | the trait value when to trigger the extinction
trait.extinction
| condition | a relational operator | the relation to the trait value (e.g. x = 1
and condition = >
makes species that have a trait value > 1 go extinct)
bd.params.update
| ... | any argument to be passed to make.bd.params
| |
traits.update
| ... | any argument to be passed to make.traits
| |
modifiers.update
| ... | any argument to be passed to make.modifiers
| |
* non-optional arguments
The target of the event is what the event is going to modify in the birth-death algorithm. You can only have one target per event (along with one condition and one modification) but you can create events that contain multiple events (i.e. multiple triplets of target/condition/modification). The targets that are currently available are:
"taxa"
to modify anything linked to the lineage
list. e.g. making half of the living taxa go extinct."bd.params"
to modify anything linked to the bd.params
object. For example you might want to change the distribution of one of the parameter after some conditions
. This is typically done by updating the object using the argument update
from make.bd.params
."traits"
to modify anything linked to the traits
object. For example you might want to change the trait process after some conditions
. This is typically done by updating the object using the argument update
from make.traits
."modifiers"
to modify anything linked to the modifiers
object. For example you might want to change the speciation rule after some conditions
. This is typically done by updating the object using the argument update
from make.modifiers
."founding"
this target is a bit more specialised and allows you run a nested treats
object in the simulation. It is covered in a specific section below.We will see some examples of these targets in the conditions
and modifications
described below.
condition
is a function that returns a logical value. When a specific condition is met, it should return TRUE
and trigger the event, else it should return FALSE
.
Currently there are three conditions functions implemented in treats
but you can easily come up with your own version of them.
All condition
functions in treats
take at least two arguments: x
for the variable of interest (e.g. time, number of taxa, trait value, etc.) and condition
, the relational operator to evaluate. A relational operator is the proper (fancy) term designating all the comparisons you're regularly using in R
like ==
(is equal?) <
(is smaller?) >=
(is bigger or equal?). You can get the full list in the R base manual (using ?Comparison
or any of the relational operator in a function form). For more details on how functions really work in R see the Advanced R
book!
age.condition
will trigger the event
once a certain time is reached in the simulations and is the simplest/most basic condition with no arguments other than the time required. For example age.condition(4, condition = `==`)
will trigger the event
once the simulations reaches 4 time units. Easy.taxa.condition
will trigger the event
once a certain number of taxa are reached. This can be considered including or excluding fossil species. For example taxa.condition(42, condition = `>=`, living = TRUE)
will trigger the event
if there are at least 42 living taxa.trait.condition
will trigger the event
once a certain trait value is reached. This function allows to say which trait(s) to target (by default, the first one using trait = 1
), what value of the trait to target (by default what = max
) and whether to use and absolute trait value or not (absolute = TRUE
). For example trait.condition(1/3, condition = `>`, trait = 1, what = sd)
will trigger the condition after the standard deviation of the first trait reaches 1/3.After defining the event
, target
and condition
, you also need to specify what it should modify.
These are functions that should modify a specific aspect of the target.
Depending on the target you can modify the following:
"taxa"
you can modify the internal lineage
list by removing living species using random.extinction
or trait.extinction
."taxa"
you can modify the lineage tracker by removing living species using random.extinction
or trait.extinction
."bd.params"
you can modify the bd.params
object using bd.params.update
."traits"
you can modify the traits
object using traits.update
."modifiers"
you can modify the modifiers
object using modifiers.update
."founding"
target see below)The most straightforward example is for modifications
on bd.params
, traits
or modifiers
objects because they use the same syntax as for their generic make.X
function.
For example, for make.traits
, you can update a trait
using the update
argument as follows:
## A BM trait in two dimensions (BM_2D <- make.traits(n = 2, process = BM.process)) ## Updating the 2D BM into a 2D OU (OU_2D <- make.traits(update = BM_2D, process = OU.process))
So the function update.X
applies the update to the object X
when the event happens.
The following event
updates the bd.params
object by setting the extinction parameter to 1/3 when reaching a 10 species:
make.events(target = "bd.params", condition = taxa.condition(10, condition = `>=`), modification = bd.params.update(extinction = 1/3))
Note that by default events are triggered only once across the whole simulation, so although the example above states that the condition is reaching at least 10 taxa, it will not trigger every time it reaches more than 10 taxa, only the first time. You can change the number of times the events can be triggered using the argument
replications
(by default it's set toreplications = 0
for triggering the event only once).
Here are some examples illustrating how to generate events. For a simple example, we can create a extinction event that will remove 80% of species after reaching time 4:
## 80% mass extinction at time 4 mass_extinction <- make.events( target = "taxa", condition = age.condition(4), modification = random.extinction(0.8)) ## Simulation parameters stop.rule <- list(max.time = 5) bd.params <- list(extinction = 0, speciation = 1) ## Running the simulations set.seed(123) results <- treats(bd.params = bd.params, stop.rule = stop.rule, events = mass_extinction) ## Plotting the results plot(results, show.tip.label = FALSE) axisPhylo()
Or for a slightly more complex example, we can can change the trait process from a BM to an OU when the trait values reaches an upper 95% quantile value above 2:
## The 95% upper quantile value of a distribution upper.95 <- function(x) { return(quantile(x, prob = 0.95)) } ## Create an event to change the trait process change_process <- make.events( target = "traits", ## condition is triggered if(upper.95(x) > 3) condition = trait.condition(3, condition = `>`, what = upper.95), modification = traits.update(process = OU.process)) ## Set the simulation parameters bd.params <- list(extinction = 0, speciation = 1) stop.rule <- list(max.time = 6) traits <- make.traits() ## Run the simulations set.seed(1) no_change <- treats(bd.params = bd.params, stop.rule = stop.rule, traits = traits) set.seed(1) process_change <- treats(bd.params = bd.params, stop.rule = stop.rule, traits = traits, events = change_process) ## Plot the results par(mfrow = c(1,2)) plot(no_change, ylim = c(-7, 7)) plot(process_change, ylim = c(-7, 7)) par(mfrow = c(1,1))
Founding events are a specific target
for events
that allows you to simulate a birth-death process within the current one!
It allows you to simulate a specific treats
process using the treats
function with its own traits
, modifiers
, bd.params
and events
(and yes, that's including events
that have their own founding
event).
This is basically the ultimate nested boss of modularity!
The founding event will run an internal treats
process (i.e. simulating a tree and, optionally, some data) resulting in a founding sub-tree.
It will then branch this sub-tree onto the rest of the simulation that continued normally in the mean time.
You can specify the founding event using the inbuilt founding.event
modification event.
This function takes the exact same arguments as treats
to simulate the sub-tree with its own parameters.
Additionally, we will use the additional.args
argument from make.events
to specify a prefix for the founding tree tips (to make them easier to distinguish).
## Set up parameters stop.rule <- list(max.time = 4) bd.params <- make.bd.params(speciation = 1, extinction = 0.3) ## Events that generate a new process (founding tree - with no extinction) founding_event <- make.events( target = "founding", condition = taxa.condition(10), modification = founding.event( bd.params = make.bd.params(speciation = 2, extinction = 0)), additional.args = list(prefix = "founding_")) ## Simulations set.seed(11) founding_tree <- treats(bd.params = bd.params, stop.rule = stop.rule, events = founding_event) plot(founding_tree, cex = 0.4)
Note that the nestedness here is potentially endless, for example, you can pass an event
argument to the founding.event
that will generate another founding event, etc.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.