Introduction to R6Sim
Pedro Nascimento de Lima
introduction.Rmd
This package contains two R6
classes that can be
inherited in your analysis: R6Sim
and
R6Experiment
. Here I provide a brief introduction of how
they work. A more complete example of their use can be seen in
repositories that build models and analyses based on R6Sim
,
such as the value of
environmental surveillance project.
The R6Sim
model class
The R6Sim
class is meant to represent an R6 simulation
model. An R6Sim
model should be self-contained, and once
instantiated, should immediately know how to simulate itself with the
model$simulate()
function. Here is an example
R6Sim
model:
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(R6Sim)
Mymodel <- R6::R6Class(
classname = "Mymodel",
inherit = R6Sim,
public = list(
sim_res = NULL,
# Custom Initialize function
initialize = function(name) {
super$initialize(name = name)
self$set_input("pop.size", 100)$
set_input("risk.mean", 0.01)$
set_input("risk.sd", 0.001)$
set_input(name = "trials",value = 10)
# Can also add a parameter distribution
self$set_param_dist(params_list = list(param_dist_a = data.frame(sample_param = 1, weights = 1)), use_average = T, param_dist_weights = "weights")
},
# Sample Simulate function
simulate = function() {
# Create a sample population with some health events:
self$sim_res = data.frame(p.id = 1:self$inputs$pop.size,
risk = rnorm(n = self$inputs$pop.size, mean = self$inputs$risk.mean, sd = self$inputs$risk.sd)) %>%
mutate(probability.event = 1-exp(-risk)) %>%
mutate(n.events = rbinom(n = 1:self$inputs$pop.size, size = self$inputs$trials, prob = probability.event))
# Let's return the number of events as the key model input
return(data.frame(events = sum(self$sim_res$n.events)))
}
)
)
model <- Mymodel$new("test model")
model$simulate()
#> events
#> 1 14
Once your model is created, you can set inputs and run the model again:
# you can chain methods
model$set_input("risk.mean", 0.05)$
simulate()
#> events
#> 1 52
The model$inputs
list contains all model inputs, and can
be inspected.
model$inputs
#> $pop.size
#> [1] 100
#>
#> $risk.mean
#> [1] 0.05
#>
#> $risk.sd
#> [1] 0.001
#>
#> $trials
#> [1] 10
Instead of passing these inputs around as parameters in your
functions, you can always access the model inputs whithin the model with
the self$inputs$input_name
.
The R6Experiment
class
An R6Experiment
can contain multiple R6Sim
models. That class can be used to define experiments and run them in
parallel. Your experiment can have multiple models.
# Now, the other model will be slightly different
my_other_model <- model$clone(deep = T)
my_other_model$
set_input("risk.sd", 0.002)
my_other_model$name <- "high dispersion"
# Note that my_other_model could be an arbitraryly different class, as long as it also inherits R6Sim
# here, we could change some structural parameter:
experiment = R6Experiment$new(model, my_other_model)
experiment$
set_parameter(parameter_name = "risk.mean",experimental_design = "grid",values = seq.default(from = 0.01, to = 0.05, by = 0.01))$
set_design()
results <- experiment$run(parallel = F, model_from_cluster_eval = F)
# model.id =1 is the first model passed to experiment, and so on:
results %>%
select(model.id, risk.mean, events)
#> model.id risk.mean events
#> <int> <num> <int>
#> 1: 1 0.01 11
#> 2: 1 0.02 21
#> 3: 1 0.03 26
#> 4: 1 0.04 36
#> 5: 1 0.05 48
#> 6: 2 0.01 10
#> 7: 2 0.02 21
#> 8: 2 0.03 26
#> 9: 2 0.04 36
#> 10: 2 0.05 47
Once you experiment runs, now you can iterate and add new inputs, or new parameters to your experiment, or add an arbitrary number of models to your experiment. To see a real use case with parallel runs, see the value-of-surveillance project.