Introduction to R6Sim
Pedro Nascimento de Lima
introduction.RmdThis 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 14Once 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 52The 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] 10Instead 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 47Once 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.