Skip to contents

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.