from collections import defaultdict
from IPython.display import HTML
from itables import to_html_datatable
import json
import os
import pandas as pdLearning objectives:
- Know the advantages of using external parameter files in simulation workflows
- Learn how to create a parameter file and data dictionary.
- Understand methods for importing parameters.
Relevant reproducibility guidelines:
- STARS Reproducibility Recommendations: Avoid hard-coded parameters.
- NHS Levels of RAP (🥈): Data is handled and output in a Tidy data format.
Required packages:
These should be available from environment setup on the Structuring as a package page.
library(jsonlite)
library(kableExtra)Why use external parameter files?
External parameter files can offer some advantages over storing parameters directly in scripts:
- Easier for non-programmers to modify parameters.
- Can easily share your simulation with different parameters versions (e.g. real parameters for internal use, synthetic parameters for external sharing, as discussed in input data management) without editing the code itself.
- Less hard coding as parameters are completely separate.
In many cases though, storing parameters within a script is commonplace - particularly for simpler models - due to its simplicity (everything in one place, fewer files).
Create parameter file
There are several file types you can use to store parameters, with CSV and JSON being two good options. Both have advantages over Excel files:
- Open and non-proprietary format.
- Work well with version control (as they are plain text).
- Supported by many tools: spreadsheets, text editors, programming languages.
- Easier to import and parse programmatically than Excel.
We will make a folder called inputs to store our parameter files within.
data_path = "inputs"As per the standard structure for R packages, we create a folder inst/extdata/ to store our parameter files within.
data_path <- file.path("inst", "extdata")CSV example
For CSV files, use a simple tabular structure and follow tidy data principles - each row is an observation, each column a variable. Avoid merged cells or stacked headers, as these make data processing difficult.
# Import and preview parameter file
csv_content = pd.read_csv(os.path.join(data_path, "example_parameters.csv"))
HTML(to_html_datatable(csv_content))| Loading ITables v2.5.2 from the internet... (need help?) |
You can use either:
- Long format (one parameter per row) - works well when you have a single parameter set with grouped parameters.
- Wide format (one parameter per column) - useful for when you have multiple parameter sets or scenarios in one file. It can also work directly with
purrr::pmap()(see “Direct import to model functions” below).
Long format
# Import and view the parameter table
csv_content <- read.csv(file.path(data_path, "example_parameters.csv"))
kable(csv_content)| patient | metric | value |
|---|---|---|
| adult | interarrival | 5.0 |
| adult | consultation | 20.0 |
| adult | transfer | 0.3 |
| child | interarrival | 7.0 |
| child | consultation | 15.0 |
| child | transfer | 0.2 |
| elderly | interarrival | 10.0 |
| elderly | consultation | 30.0 |
| elderly | transfer | 0.5 |
Wide format
# Import and view the parameter table
csv_content <- read.csv(file.path(data_path, "example_parameters_wide.csv"))
kable(csv_content)| adult_interarrival | adult_consultation | adult_transfer | child_interarrival | child_consultation | child_transfer | elderly_interarrival | elderly_consultation | elderly_transfer |
|---|---|---|---|---|---|---|---|---|
| 5 | 20 | 0.3 | 7 | 15 | 0.2 | 10 | 30 | 0.5 |
JSON example
For JSON files, data are stored in hierachical / nested structures. Keep the structure clear and consistent.
# Import parameters
# pylint:disable=invalid-name
json_file = os.path.join(data_path, "example_parameters.json")
with open(json_file, "r", encoding="utf-8") as f:
json_content = json.load(f)
# View the dictionary
# (Use json.dumps() as makes dict easier to read when print)
print(json.dumps(json_content, indent=3)){
"simulation_parameters": {
"adult_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 5.0
}
},
"adult_consultation": {
"class_name": "Exponential",
"params": {
"mean": 20.0
}
},
"adult_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.3
}
},
"child_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 7.0
}
},
"child_consultation": {
"class_name": "Exponential",
"params": {
"mean": 15.0
}
},
"child_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.2
}
},
"elderly_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 10.0
}
},
"elderly_consultation": {
"class_name": "Exponential",
"params": {
"mean": 30.0
}
},
"elderly_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.5
}
}
}
}
json_file <- fromJSON(file.path(data_path, "example_parameters.json"))
str(json_file)List of 1
$ simulation_parameters:List of 9
..$ adult_interarrival :List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 5
..$ adult_consultation :List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 20
..$ adult_transfer :List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 0.3
..$ child_interarrival :List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 7
..$ child_consultation :List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 15
..$ child_transfer :List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 0.2
..$ elderly_interarrival:List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 10
..$ elderly_consultation:List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 30
..$ elderly_transfer :List of 2
.. ..$ class_name: chr "Exponential"
.. ..$ params :List of 1
.. .. ..$ mean: num 0.5
Storing the parameter file
If you have structured your research as a package, a good location for the parameter file/s is in inst/extdata. This is the conventional location for example data in packages, with everything in inst copied upon installation. It means the directory becomes <pkg>/extdata/ and can be accessed using system.file("extdata", "filename", package = "yourpackage"),
Packages can also have a data/ folder, but this is intended only for .rda files. These are documented datasets that can be loaded using data(). You use a data-raw/ directory to store scripts that read, process and save raw data as .rda files. These scripts are not included in the final package but provide a clear record of how datasets are generated.
You could make custom locations (e.g., an inputs/ directory) if it suits your workflow, though it’s worth noting that this isn’t not part of the standard R package structure. If you do so, just make sure to document clearly for users how to find and access your files, as package tools and conventions may not automatically support these locations.
You can also share data separately from your package (such as for collaborative projects or frequency updated datasets). Check out the pins package as one of the possible tools for doing this.
Create data dictionary
As mentioned in the checklist for managing parameter data on the input data management page, you should include a data dictionary or documentation describing each parameter. This documentation should explain:
- The parameter’s meaning
- The parameter’s units
- Any abbreviations or codes used.
The examples below were created using markdown (converted to PDF using pandoc), but you can use any suitable format (e.g. CSV, YAML, etc.) - as long as it is clear, consistent and accessible.
CSV data dictionary example
This example is for the wide format CSV data.
JSON data dictionary example
Methods for importing parameters
As explained in the parameters from script page, there are two key principles for managing parameters effectively in simulation models:
- Group parameters into a dedicated object.
- Pass this object explicitly to your model.
If you haven’t already, see the parameters from script page for more detail on why this explicit grouping and passing is important.
Implementation options for file-based parameters
You still have two main choices: a function or a class.
Direct dictionary import (loading the file straight into a dictionary) is rarely practical.
- ❌ It only works if the file structure exactly matches your model and no extra parameters are needed.
Function (returns a dictionary)
✅ Handles any processing needed to go from file to parameter structure.
✅ Lets you merge file-based and script-based parameters into one object.
✅ Keeps all import/processing logic together for easy reuse and debugging.
❌ Dictionaries can be modified after creation, so validation must be performed in the model functions/classes (ensuring the final set of parameters is being checked). This means that errors are only caught later, rather than immediately at the point of import/change.
Class
✅ Includes all function benefits: processing, merging, in one place.
✅ Supports validation at creation or update, catching errors earlier.
When importing parameters from a file, you have two main approaches:
| Parameter function | Direct import to model | |
|---|---|---|
| Description | A function loads parameters from the file and returns them as a named list. This list is then passed to your model. | Import parameters directly as individual arguments to your model function. |
| Organisation | ✅ Functions have fewer inputs (easier to read) | ❌ All parameters in model function signature (harder to read with many) |
| Complex nested structures | ✅ Useful for nested parameters | ❌ Less flexible for nesting |
| Multiple parameter sets | ❌ Requires extra processing steps | ✅ Natural integration with purrr::pmap(), and furrr::future_pmap() for parallelisation |
| Risk of accidental modification (see validation page) | ❌ Named list can be modified after creation | ✅ Function signature prevents undefined parameters |
Why wasn’t direct import to model included an option on the parameters from script page?
Direct import to model functions works best when parameters are in a file or dataframe, particularly when you have multiple scenarios.
With script-based parameters, you typically define a single parameter set with defaults. Direct import becomes overkill - you’d end up recreating what a parameter function already does, just with a messier function signature.
With file-based parameters, you naturally have multiple rows (scenarios) in a dataframe. Direct import leverages this structure: column names map directly to function arguments, and
purrr::pmap()runs each row through your model efficiently.For script-based parameters, a parameter function (as shown on the Parameters from script page) remains the cleaner approach.
Using a function
Nested or flat dictionary
A function loads parameters and returns them as a dictionary. You can choose between a nested or flat structure, depending on complexity.
A nested dictionary is good for grouped parameters, like distributions with multiple values (mean, sd, etc.), or if you want to organise by category like patient type.
params = {
"adult": {"interarrival": 5, "service_time": 10},
"child": {"interarrival": 3, "service_time": 7},
"distribution": {"mean": 0, "sd": 1}
}
print(params["adult"]["interarrival"])5
A flat dictionary can be simpler to use if everything fits in one layer.
params = {
"adult_interarrival": 5,
"adult_service_time": 10,
"child_interarrival": 3,
"child_service_time": 7
}
print(params["adult_interarrival"])5
Parameter function function
Choice of outputs: nested or flat list
A function loads parameters and returns them as a named list. You can choose between a nested or flat structure, depending on complexity.
A nested list is good for grouped parameters, like distributions with multiple values (mean, sd, etc.), or if you want to organise by category like patient type.
params <- list(
adult = list(interarrival = 5L, service_time = 10L),
child = list(interarrival = 3L, service_time = 7L),
distribution = list(mean = 0L, sd = 1L)
)
params$adult$interarrival[1] 5
A flat list can be simpler to use if everything fits in one layer.
params <- list(
adult_interarrival = 5L,
adult_service_time = 10L,
child_interarrival = 3L,
child_service_time = 7L
)
params$adult_interarrival[1] 5
JSON example
This uses a nested format, as the JSON was already in that format:
def create_parameters(json_file, number_of_runs=10):
"""
Create parameter dictionary by combining parameters from JSON with those
defined in this function.
Parameters
----------
json_file : str
Path to JSON file containing simulation parameters.
number_of_runs : int, optional
Number of simulation runs (default is 10).
Returns
-------
param_dict : dict
Nested dictionary of simulation parameters.
"""
with open(json_file, "r", encoding="utf-8") as f:
json_content = json.load(f)
param_dict = json_content["simulation_parameters"]
param_dict["number_of_runs"] = number_of_runs
return param_dict
param = create_parameters(os.path.join(data_path, "example_parameters.json"))
print(json.dumps(param, indent=3)){
"adult_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 5.0
}
},
"adult_consultation": {
"class_name": "Exponential",
"params": {
"mean": 20.0
}
},
"adult_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.3
}
},
"child_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 7.0
}
},
"child_consultation": {
"class_name": "Exponential",
"params": {
"mean": 15.0
}
},
"child_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.2
}
},
"elderly_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 10.0
}
},
"elderly_consultation": {
"class_name": "Exponential",
"params": {
"mean": 30.0
}
},
"elderly_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.5
}
},
"number_of_runs": 10
}
CSV example (flat)
def create_parameters(csv_file, number_of_runs=10):
"""
Create flat parameter dictionary by combining parameters from CSV with
those defined in this function.
Parameters
----------
csv_file : str
Path to CSV file containing simulation parameters.
number_of_runs : int, optional
Number of simulation runs (default is 10).
Returns
-------
param_dict : dict
Flat dictionary of simulation parameters.
"""
csv_content = pd.read_csv(csv_file)
param_dict = {
f"{row.patient}_{row.metric}": float(row.value)
for row in csv_content.itertuples(index=False)
}
param_dict["number_of_runs"] = number_of_runs
return param_dict
param = create_parameters(os.path.join(data_path, "example_parameters.csv"))
print(json.dumps(param, indent=3)){
"adult_interarrival": 5.0,
"adult_consultation": 20.0,
"adult_transfer": 0.3,
"child_interarrival": 7.0,
"child_consultation": 15.0,
"child_transfer": 0.2,
"elderly_interarrival": 10.0,
"elderly_consultation": 30.0,
"elderly_transfer": 0.5,
"number_of_runs": 10
}
CSV example (nested)
def create_parameters(csv_file, number_of_runs=10):
"""
Create nested parameter dictionary by combining parameters from CSV with
those defined in this function.
Parameters
----------
csv_file : str
Path to CSV file containing simulation parameters.
number_of_runs : int, optional
Number of simulation runs (default is 10).
Returns
-------
dict
Nested dictionary of simulation parameters.
"""
csv_content = pd.read_csv(csv_file)
param_dict = defaultdict(dict)
for row in csv_content.itertuples(index=False):
param_dict[row.patient][row.metric] = float(row.value)
param_dict["number_of_runs"] = number_of_runs
return dict(param_dict)
param = create_parameters(os.path.join(data_path, "example_parameters.csv"))
print(json.dumps(param, indent=3)){
"adult": {
"interarrival": 5.0,
"consultation": 20.0,
"transfer": 0.3
},
"child": {
"interarrival": 7.0,
"consultation": 15.0,
"transfer": 0.2
},
"elderly": {
"interarrival": 10.0,
"consultation": 30.0,
"transfer": 0.5
},
"number_of_runs": 10
}
Importing from JSON
This uses a nested format, as the JSON was already in that format:
#' Create parameter list by combining parameters from JSON with those
#' defined in this function.
#'
#' @param json_file Character, path to JSON file containing simulation
#' parameters.
#' @param number_of_runs Integer, number of simulation runs (default is 10).
#'
#' @return Named list of simulation parameters (nested).
#' @export
create_parameters <- function(json_file, number_of_runs = 10L) {
json_content <- jsonlite::fromJSON(json_file)
param_list <- json_content$simulation_parameters
param_list$number_of_runs <- number_of_runs
param_list
}
# Example usage:
param <- create_parameters(file.path(data_path, "example_parameters.json"))
print(param[1L:4L])$adult_interarrival
$adult_interarrival$class_name
[1] "Exponential"
$adult_interarrival$params
$adult_interarrival$params$mean
[1] 5
$adult_consultation
$adult_consultation$class_name
[1] "Exponential"
$adult_consultation$params
$adult_consultation$params$mean
[1] 20
$adult_transfer
$adult_transfer$class_name
[1] "Exponential"
$adult_transfer$params
$adult_transfer$params$mean
[1] 0.3
$child_interarrival
$child_interarrival$class_name
[1] "Exponential"
$child_interarrival$params
$child_interarrival$params$mean
[1] 7
Importing from CSV
With flat output:
#' Create flat parameter list by combining parameters from CSV with those
#' defined in this function.
#'
#' @param csv_file Character, path to CSV file containing simulation
#' parameters.
#' @param number_of_runs Integer, number of simulation runs (default is 10).
#'
#' @return Named list of simulation parameters (flat).
#' @export
create_parameters <- function(csv_file, number_of_runs = 10L) {
csv_content <- read.csv(csv_file)
lst <- as.list(csv_content$value)
names(lst) <- paste(csv_content$patient, csv_content$metric, sep = "_")
lst$number_of_runs <- number_of_runs
lst
}
# Example usage:
param <- create_parameters(file.path(data_path, "example_parameters.csv"))
print(param[1L:4L])$adult_interarrival
[1] 5
$adult_consultation
[1] 20
$adult_transfer
[1] 0.3
$child_interarrival
[1] 7
With nested output:
#' Create nested parameter list by combining parameters from CSV with those
#' defined in this function.
#'
#' @param csv_file Character, path to CSV file containing simulation
#' parameters.
#' @param number_of_runs Integer, number of simulation runs (default is 10).
#'
#' @return Named list of simulation parameters (nested).
#' @export
create_parameters <- function(csv_file, number_of_runs = 10L) {
csv_content <- read.csv(csv_file)
lst <- lapply(split(csv_content, csv_content$patient), function(subset) {
setNames(as.list(subset$value), subset$metric)
})
lst$number_of_runs <- number_of_runs
lst
}
# Example usage:
param <- create_parameters(file.path(data_path, "example_parameters.csv"))
print(param[1L:4L])$adult
$adult$interarrival
[1] 5
$adult$consultation
[1] 20
$adult$transfer
[1] 0.3
$child
$child$interarrival
[1] 7
$child$consultation
[1] 15
$child$transfer
[1] 0.2
$elderly
$elderly$interarrival
[1] 10
$elderly$consultation
[1] 30
$elderly$transfer
[1] 0.5
$number_of_runs
[1] 10
Using a class
In the class, parameters can again be nested or flat, but this time they are attributes of the class instance. Those attributes can themselves them be simple values or dictionaries.
As described above, classes also allow you to “lock down” the allowed parameters and add validation checks during initialisation and any changes. For examples of how to add validation when using a class, see the parameter validation page.
JSON example
class CreateParameters:
"""
Store simulation parameters, combining those loaded from a JSON file with
values defined in the class.
"""
def __init__(self, json_file, number_of_runs=10):
"""
Parameters
----------
json_file : str
Path to JSON file containing simulation parameters.
number_of_runs : int, optional
Number of simulation runs (default is 10).
"""
# Import parameters from JSON file
with open(json_file, "r", encoding="utf-8") as f:
json_content = json.load(f)
params = json_content["simulation_parameters"]
# Assign file-based parameters as attributes
for key, value in params.items():
setattr(self, key, value)
# Add class-defined parameters
self.number_of_runs = number_of_runs
param = CreateParameters(os.path.join(data_path, "example_parameters.json"))
print(json.dumps(param.__dict__, indent=3)){
"adult_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 5.0
}
},
"adult_consultation": {
"class_name": "Exponential",
"params": {
"mean": 20.0
}
},
"adult_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.3
}
},
"child_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 7.0
}
},
"child_consultation": {
"class_name": "Exponential",
"params": {
"mean": 15.0
}
},
"child_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.2
}
},
"elderly_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 10.0
}
},
"elderly_consultation": {
"class_name": "Exponential",
"params": {
"mean": 30.0
}
},
"elderly_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.5
}
},
"number_of_runs": 10
}
CSV example (flat)
class CreateParameters:
"""
Store simulation parameters, combining those loaded from a CSV file with
values defined in the class.
"""
def __init__(self, csv_file, number_of_runs=10):
"""
Parameters
----------
csv_file : str
Path to CSV file containing simulation parameters.
number_of_runs : int, optional
Number of simulation runs (default is 10).
"""
# Import parameters from CSV file
csv_content = pd.read_csv(csv_file)
# Assign CSV parameters as attributes (flat style)
for row in csv_content.itertuples(index=False):
param_key = f"{row.patient}_{row.metric}"
setattr(self, param_key, float(row.value))
# Add class-defined parameters
self.number_of_runs = number_of_runs
param = CreateParameters(os.path.join(data_path, "example_parameters.csv"))
print(json.dumps(param.__dict__, indent=3)){
"adult_interarrival": 5.0,
"adult_consultation": 20.0,
"adult_transfer": 0.3,
"child_interarrival": 7.0,
"child_consultation": 15.0,
"child_transfer": 0.2,
"elderly_interarrival": 10.0,
"elderly_consultation": 30.0,
"elderly_transfer": 0.5,
"number_of_runs": 10
}
CSV example (nested)
class CreateParameters:
"""
Store simulation parameters, combining those loaded from a CSV file with
values defined in the class.
"""
def __init__(self, csv_file, number_of_runs=10):
"""
Parameters
----------
csv_file : str
Path to CSV file containing simulation parameters.
number_of_runs : int, optional
Number of simulation runs (default is 10).
"""
# Import parameters from CSV file and structure as nested dictionary
csv_content = pd.read_csv(csv_file)
param_dict = defaultdict(dict)
for row in csv_content.itertuples(index=False):
param_dict[row.patient][row.metric] = float(row.value)
param_dict["number_of_runs"] = number_of_runs
# Assign file-based parameters as attributes
for key, value in param_dict.items():
setattr(self, key, value)
# Add class-defined parameters
self.number_of_runs = number_of_runs
param = CreateParameters(os.path.join(data_path, "example_parameters.csv"))
print(json.dumps(param.__dict__, indent=3)){
"adult": {
"interarrival": 5.0,
"consultation": 20.0,
"transfer": 0.3
},
"child": {
"interarrival": 7.0,
"consultation": 15.0,
"transfer": 0.2
},
"elderly": {
"interarrival": 10.0,
"consultation": 30.0,
"transfer": 0.5
},
"number_of_runs": 10
}
Alternative set-up: class with explicit attributes
The previous examples dynamically set attributes based on whatever is present in your input file. Here, we show another approach - explicitly defining each attribute. This provides clarity and structure, although is more verbose.
You’ll notice that this class is similar to those in parameters from script - except that it has no default values.
class Parameters:
"""
Container for simulation parameters across patient demographics.
"""
def __init__(
self,
adult_interarrival, adult_consultation, adult_transfer,
child_interarrival, child_consultation, child_transfer,
elderly_interarrival, elderly_consultation, elderly_transfer
):
"""
Initialise Parameters instance.
Parameters
----------
adult_interarrival : float
Time between adult patient arrivals (e.g., in minutes or hours).
adult_consultation : float
Duration of consultation time for adult patients.
adult_transfer : float
Time required to transfer adult patients between stages.
child_interarrival : float
Time between child patient arrivals (e.g., in minutes or hours).
child_consultation : float
Duration of consultation time for child patients.
child_transfer : float
Time required to transfer child patients between stages.
elderly_interarrival : float
Time between elderly patient arrivals (e.g., in minutes or hours).
elderly_consultation : float
Duration of consultation time for elderly patients.
elderly_transfer : float
Time required to transfer elderly patients between stages.
"""
# Adult parameters
self.adult_interarrival = adult_interarrival
self.adult_consultation = adult_consultation
self.adult_transfer = adult_transfer
# Child parameters
self.child_interarrival = child_interarrival
self.child_consultation = child_consultation
self.child_transfer = child_transfer
# Elderly parameters
self.elderly_interarrival = elderly_interarrival
self.elderly_consultation = elderly_consultation
self.elderly_transfer = elderly_transferUse a helper function that loads the data and matches it to the class attributes. For example, from a CSV file:
def setup_param_from_csv(csv_path):
"""
Create a Parameters instance using parameter values loaded from a CSV file.
Parameters
----------
csv_path : str
Path to CSV file containing the parameters. Should have columns
"patient", "metric", and "value".
Returns
-------
Parameters
Instance of Parameters initialised with parameters from the CSV file.
"""
# Import the parameters
param_file = pd.read_csv(csv_path)
# Create name-value mappings
values = {
f"{row.patient}_{row.metric}": row.value
for row in param_file.itertuples()
}
# Pass these values to the Parameters class
return Parameters(**values)
# Import parameters
params = setup_param_from_csv(
os.path.join(data_path, "example_parameters.csv")
)
# View object
print(json.dumps(params.__dict__, indent=3)){
"adult_interarrival": 5.0,
"adult_consultation": 20.0,
"adult_transfer": 0.3,
"child_interarrival": 7.0,
"child_consultation": 15.0,
"child_transfer": 0.2,
"elderly_interarrival": 10.0,
"elderly_consultation": 30.0,
"elderly_transfer": 0.5
}
Direct import to model
With the wide format, parameters can be imported directly into your model function using purrr::pmap().
We haven’t built the full model yet, but let’s create a simple example to demonstrate the pattern. This model function accepts parameters directly as individual arguments:
#' Simple model example with explicit parameters
#'
#' @param adult_interarrival Numeric. Time between adult patient arrivals
#' (minutes).
#' @param adult_consultation Numeric. Duration of consultation time for adult
#' patients (minutes).
#' @param adult_transfer Numeric. Probability of transferring adult patients
#' between stages.
#' @param child_interarrival Numeric. Time between child patient arrivals
#' (minutes).
#' @param child_consultation Numeric. Duration of consultation time for child
#' patients (minutes).
#' @param child_transfer Numeric. Probability of transferring child patients
#' between stages.
#' @param elderly_interarrival Numeric. Time between elderly patient arrivals
#' (minutes).
#' @param elderly_consultation Numeric. Duration of consultation time for
#' elderly patients (minutes).
#' @param elderly_transfer Numeric. Probability of transferring elderly
#' patients between stages.
#'
#' @return Model results
#' @export
model <- function(
adult_interarrival, adult_consultation, adult_transfer,
child_interarrival, child_consultation, child_transfer,
elderly_interarrival, elderly_consultation, elderly_transfer
) {
# Model code would go here
# For now, let's just demonstrate it imported successfully by printing...
print(paste("Parameters imported! For example:"))
print(paste("Adult interarrival time:", adult_interarrival, "minutes"))
print(paste("Child consultation time:", child_consultation, "minutes"))
print(paste("Elderly transfer probability:", elderly_transfer))
}Import the wide-format parameters:
params <- readr::read_csv(
file.path(data_path, "example_parameters_wide.csv"), show_col_types = FALSE
)For a single parameter set, we can use do.call():
do.call(model, params[1, ])[1] "Parameters imported! For example:"
[1] "Adult interarrival time: 5 minutes"
[1] "Child consultation time: 15 minutes"
[1] "Elderly transfer probability: 0.5"
For multiple scenarios (if we had multiple rows), we can use purrr::pmap():
params %>% purrr::pmap(model)[1] "Parameters imported! For example:"
[1] "Adult interarrival time: 5 minutes"
[1] "Child consultation time: 15 minutes"
[1] "Elderly transfer probability: 0.5"
[[1]]
[1] "Elderly transfer probability: 0.5"
The column names in the CSV must match the function argument names exactly - this provides automatic validation against typos (see parameter validation page).
Explore the example models
Nurse visit simulation
The nurse visit model doesn’t import parameters from a file. It instead stores them within a script, as covered on the parameters from script page.
Stroke pathway simulation
Click to visit pydesrap_stroke repository
| Key files | simulation/parameters.pyinputs/parameters.jsoninputs/data_dictionary.md |
| What to look for? | See how model parameters are stored in a structured JSON file and documented with a data dictionary. The Python code imports these parameters and stores them as an attribute in the Param class, while a few parameters (like warm-up period) remain defined in the script. |
| Why it matters? | This model has 50+ parameters across multiple patient types and care settings. By keeping these in a separate file rather than in the script, it significantly reduces the amount of code for this project. |
Click to visit rdesrap_stroke repository
| Key file | R/parameters.Rinst/extdata/parameters.jsoninst/extdata/data_dictionary.md |
| What to look for? | See how model parameters are stored in a structured JSON file and documented with a data dictionary. The R code imports these parameters and stores them within the named list returned by the function. A few parameters (like warm-up period) remain defined in the script. |
| Why it matters? | This model has 50+ parameters across multiple patient types and care settings. By keeping these in a separate file rather than in the script, it significantly reduces the amount of code for this project. |
Test yourself
If you haven’t already, now’s the time to practice working with external parameter files.
Task:
- Download the example CSV and JSON parameter files from this page - for example, saving them into a data folder in your repository.
Create a new Jupyter notebook (e.g. in a
notebooks/directory).In your notebook, try out at least two different parameter loading approaches from the examples above (e.g. flat vs nested structure, function vs class, CSV v.s. JSON).
Create a new Rmarkdown file (e.g. in a
rmarkdown/directory).In your Rmarkdown file, try out at least two different parameter loading approaches from the examples above (e.g. flat vs nested structure, CSV v.s. JSON).
- Print or inspect the loaded parameters for each approach, and briefly note any differences or advantages you notice.
- If you encounter errors, check the file path, the folder structure, and the filename extensions.