import pandas as pd
import json
from collections import defaultdict
Parameters from file
Learning 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:
This should be available from environment setup in the “🧪 Test yourself” section of Environments.
library(jsonlite)
library(R6)
The print_dict
function is just a small helper used on this page to make dictionary outputs easier to read.
import json
import itertools
def print_dict(dictionary, max_items_per_level):
"""
Pretty print the first n items of a dictionary in JSON format.
Parameters
----------
dictionary : dict
Dictionary to print.
max_items_per_level: int or list of int.
If int, applies limit to all levels. If list, limit for
[level1, level2, ...]. If the nesting is deeper, the last value is
used for deeper levels.
"""
# Ensure max_items_per_level is a list of limits per level
if isinstance(max_items_per_level, int):
= [max_items_per_level]
levels else:
= list(max_items_per_level)
levels
def _truncate(obj, level=0):
"""
Recursively copy the dictionary, trimming to the allowed number
of items at each level.
Parameters
----------
obj : dict or other
The object (dict or non-dict) to be truncated.
level : int
Current depth in nested structure (0 = top/root).
"""
# Choose the current level's limit, or last limit if deeper
= levels[min(level, len(levels)-1)]
max_items
# If obj is a dict, slice its items to the limit
if isinstance(obj, dict):
# Use islice for efficiency; only take max_items items
return {
+ 1)
k: _truncate(v, level for k, v in itertools.islice(obj.items(), max_items)
}# If obj is not a dict, return as is
return obj
# Create a truncated copy and print as pretty JSON
= _truncate(dictionary)
trimmed_dict print(json.dumps(trimmed_dict, indent=4, sort_keys=True))
❓ 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.
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
= pd.read_csv("parameters_file_resources/example_parameters.csv")
csv_content csv_content.head()
patient metric value
0 adult interarrival 5.0
1 adult consultation 20.0
2 adult transfer 0.3
3 child interarrival 7.0
4 child consultation 15.0
# Import and view the parameter table
read.csv(file.path("parameters_file_resources", "example_parameters.csv"))
patient metric value
1 adult interarrival 5.0
2 adult consultation 20.0
3 adult transfer 0.3
4 child interarrival 7.0
5 child consultation 15.0
6 child transfer 0.2
7 elderly interarrival 10.0
8 elderly consultation 30.0
9 elderly transfer 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
= "parameters_file_resources/example_parameters.json"
json_file with open(json_file, "r", encoding="utf-8") as f:
= json.load(f)
json_content
# Preview the dictionary
1, 3]) print_dict(json_content, [
{
"simulation_parameters": {
"adult_consultation": {
"class_name": "Exponential",
"params": {
"mean": 20.0
}
},
"adult_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 5.0
}
},
"adult_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.3
}
}
}
}
<- fromJSON(
json_file file.path("parameters_file_resources", "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
📖 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.
#| echo: false
import subprocess
subprocess.run(["pandoc",
"parameters_file_resources/_csv_data_dictionary.md",
"-o", "parameters_file_resources/_csv_data_dictionary.pdf"
=False)
], check
subprocess.run(["pandoc",
"parameters_file_resources/_json_data_dictionary.md",
"-o", "parameters_file_resources/_json_data_dictionary.pdf"
=False) ], check
CSV data dictionary example
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.
On that page, we discussed three implementation options: dictionary, function, and class. When defining parameters within scripts, we recommended using a function or class. But what about when you’re importing parameters from a file?
On that page, we discussed three implementation options: list, function, and R6 class. When defining parameters within scripts, we recommended using a function or class. But what about when you’re importing parameters from a file?
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)
You still have two main choices: a function or a R6 class.
Direct list import (loading the file straight into a named list) is rarely practical. It only works if the file structure exactly matches your model and no extra parameters are needed.
Function (returns a named list)
✅ 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
- ❌ Named list 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.
R6 class
✅ Includes all function benefits: processing, merging, in one place.
✅ Supports validation at creation or update, catching errors earlier.
For more context on validation, see the parameter validation page.
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
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.
<- list(
params adult = list(interarrival = 5L, service_time = 10L),
child = list(interarrival = 3L, service_time = 7L),
distribution = list(mean = 0L, sd = 1L)
)
$adult$interarrival params
[1] 5
A flat list can be simpler to use if everything fits in one layer.
<- list(
params adult_interarrival = 5L,
adult_service_time = 10L,
child_interarrival = 3L,
child_service_time = 7L
)
$adult_interarrival params
[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.load(f)
json_content = json_content["simulation_parameters"]
param_dict "number_of_runs"] = number_of_runs
param_dict[return param_dict
= create_parameters("parameters_file_resources/example_parameters.json")
param 4) print_dict(param,
{
"adult_consultation": {
"class_name": "Exponential",
"params": {
"mean": 20.0
}
},
"adult_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 5.0
}
},
"adult_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.3
}
},
"child_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 7.0
}
}
}
#' 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
<- function(json_file, number_of_runs = 10L) {
create_parameters <- jsonlite::fromJSON(json_file)
json_content <- json_content$simulation_parameters
param_list $number_of_runs <- number_of_runs
param_listreturn(param_list)
}
# Example usage:
<- create_parameters(
param file.path("parameters_file_resources", "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
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.
"""
= pd.read_csv(csv_file)
csv_content = {
param_dict f"{row.patient}_{row.metric}": float(row.value)
for row in csv_content.itertuples(index=False)
}"number_of_runs"] = number_of_runs
param_dict[return param_dict
= create_parameters("parameters_file_resources/example_parameters.csv")
param 4) print_dict(param,
{
"adult_consultation": 20.0,
"adult_interarrival": 5.0,
"adult_transfer": 0.3,
"child_interarrival": 7.0
}
#' 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
<- function(csv_file, number_of_runs = 10L) {
create_parameters <- read.csv(csv_file)
csv_content <- as.list(csv_content$value)
lst names(lst) <- paste(csv_content$patient, csv_content$metric, sep = "_")
$number_of_runs <- number_of_runs
lstreturn(lst)
}
# Example usage:
<- create_parameters(
param file.path("parameters_file_resources", "example_parameters.csv")
)print(param[1L:4L])
$adult_interarrival
[1] 5
$adult_consultation
[1] 20
$adult_transfer
[1] 0.3
$child_interarrival
[1] 7
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.
"""
= pd.read_csv(csv_file)
csv_content = defaultdict(dict)
param_dict for row in csv_content.itertuples(index=False):
= float(row.value)
param_dict[row.patient][row.metric] "number_of_runs"] = number_of_runs
param_dict[return dict(param_dict)
= create_parameters("parameters_file_resources/example_parameters.csv")
param 4) print_dict(param,
{
"adult": {
"consultation": 20.0,
"interarrival": 5.0,
"transfer": 0.3
},
"child": {
"consultation": 15.0,
"interarrival": 7.0,
"transfer": 0.2
},
"elderly": {
"consultation": 30.0,
"interarrival": 10.0,
"transfer": 0.5
},
"number_of_runs": 10
}
#' 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
<- function(csv_file, number_of_runs = 10L) {
create_parameters <- read.csv(csv_file)
csv_content <- lapply(split(csv_content, csv_content$patient), function(subset) {
lst setNames(as.list(subset$value), subset$metric)
})$number_of_runs <- number_of_runs
lstreturn(lst)
}
# Example usage:
<- create_parameters(
param file.path("parameters_file_resources", "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.load(f)
json_content = json_content["simulation_parameters"]
params
# 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
= CreateParameters("parameters_file_resources/example_parameters.json")
param 4) print_dict(param.__dict__,
{
"adult_consultation": {
"class_name": "Exponential",
"params": {
"mean": 20.0
}
},
"adult_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 5.0
}
},
"adult_transfer": {
"class_name": "Exponential",
"params": {
"mean": 0.3
}
},
"child_interarrival": {
"class_name": "Exponential",
"params": {
"mean": 7.0
}
}
}
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
= pd.read_csv(csv_file)
csv_content
# Assign CSV parameters as attributes (flat style)
for row in csv_content.itertuples(index=False):
= f"{row.patient}_{row.metric}"
param_key setattr(self, param_key, float(row.value))
# Add class-defined parameters
self.number_of_runs = number_of_runs
= CreateParameters("parameters_file_resources/example_parameters.csv")
param 4) print_dict(param.__dict__,
{
"adult_consultation": 20.0,
"adult_interarrival": 5.0,
"adult_transfer": 0.3,
"child_interarrival": 7.0
}
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
= pd.read_csv(csv_file)
csv_content = defaultdict(dict)
param_dict for row in csv_content.itertuples(index=False):
= float(row.value)
param_dict[row.patient][row.metric] "number_of_runs"] = number_of_runs
param_dict[
# 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
= CreateParameters("parameters_file_resources/example_parameters.csv")
param 4) print_dict(param.__dict__,
{
"adult": {
"consultation": 20.0,
"interarrival": 5.0,
"transfer": 0.3
},
"child": {
"consultation": 15.0,
"interarrival": 7.0,
"transfer": 0.2
},
"elderly": {
"consultation": 30.0,
"interarrival": 10.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_transfer
Use 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
= pd.read_csv(csv_path)
param_file
# 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
= setup_param_from_csv(
params "parameters_file_resources/example_parameters.csv"
)
# View object
4) print_dict(params.__dict__,
{
"adult_consultation": 20.0,
"adult_interarrival": 5.0,
"adult_transfer": 0.3,
"child_interarrival": 7.0
}
This requires each parameter to be a field, rather than storing all parameters in a list within the class (as explained on the parameter validation page).
In R, we cannot dynamically add these fields when initialising - i.e. we have to explicitly specify each attribute.
If you didn’t want to do that (e.g. for large numbers of parameters), it would be possible to store the parameters as a named list within the class, but the list items themselves would still be modifiable, so this has no advantages over just using a function - for either, validation of the parameters present would need to be done within the model function/class.
You’ll notice that this class is similar to those in parameters from script - except that it has no default values.
#' @title Parameter class.
#'
#' @field adult_interarrival Numeric. Time between adult patient arrivals
#' (minutes).
#' @field adult_consultation Numeric. Length of adult patient consultation
#' (minutes).
#' @field adult_transfer Numeric. Adult patient transfer probability (0-1).
#' @field child_interarrival Numeric. Time between child patient arrivals
#' (minutes).
#' @field child_consultation Numeric. Length of child patient consultation
#' (minutes).
#' @field child_transfer Numeric. Child patient transfer probability (0-1).
#' @field elderly_interarrival Numeric. Time between elderly patient arrivals
#' (minutes).
#' @field elderly_consultation Numeric. Length of elderly patient consultation
#' (minutes).
#' @field elderly_transfer Numeric. Elderly patient transfer probability (0-1).
<- R6Class( # nolint: object_name_linter
Parameters classname = "Parameters",
public = list(
# Adult parameters
adult_interarrival = NULL,
adult_consultation = NULL,
adult_transfer = NULL,
# Child parameters
child_interarrival = NULL,
child_consultation = NULL,
child_transfer = NULL,
# Elderly parameters
elderly_interarrival = NULL,
elderly_consultation = NULL,
elderly_transfer = NULL,
#' @description
#' Initialise Parameters instance.
initialize = function(
adult_interarrival, adult_consultation, adult_transfer,
child_interarrival, child_consultation, child_transfer,
elderly_interarrival, elderly_consultation, elderly_transfer
) {# Adult parameters
$adult_interarrival <- adult_interarrival
self$adult_consultation <- adult_consultation
self$adult_transfer <- adult_transfer
self
# Child parameters
$child_interarrival <- child_interarrival
self$child_consultation <- child_consultation
self$child_transfer <- child_transfer
self
# Elderly parameters
$elderly_interarrival <- elderly_interarrival
self$elderly_consultation <- elderly_consultation
self$elderly_transfer <- elderly_transfer
self
},
#' @description
#' Returns parameters as a named list.
#'
#' @return A named list containing all parameter values.
get_params = function() {
# Get all non-function fields
<- ls(self)
all_names <- vapply(
is_not_function
all_names,function(x) !is.function(self[[x]]),
FUN.VALUE = logical(1L)
)<- all_names[is_not_function]
param_names mget(param_names, envir = self)
}
) )
Use a helper function that loads the data and matches it to the class attributes. For example, from a CSV file:
#' Create parameter list from CSV file
#'
#' @param csv_path Character. Path to CSV file with columns:
#' "patient", "metric", "value".
#'
#' @return An instance of `Parameters` R6 class.
<- function(csv_path) {
setup_param_from_csv
# Import the parameters
<- read.csv(csv_path, stringsAsFactors = FALSE)
param_file
# Create named vector (e.g. "adult_interarrival" = 5)
<- setNames(
values $value,
param_filepaste(param_file$patient, param_file$metric, sep = "_")
)
# Instantiate Parameters with values
do.call(Parameters$new, as.list(values))
}
# Import parameters
<- setup_param_from_csv(
params file.path("parameters_file_resources", "example_parameters.csv")
)
# View object
$get_params() params
$adult_consultation
[1] 20
$adult_interarrival
[1] 5
$adult_transfer
[1] 0.3
$child_consultation
[1] 15
$child_interarrival
[1] 7
$child_transfer
[1] 0.2
$elderly_consultation
[1] 30
$elderly_interarrival
[1] 10
$elderly_transfer
[1] 0.5
Or, from a JSON file:
#' Create parameter list from JSON file
#'
#' @param json_path Character. Path to JSON file containing the parameters.
#'
#' @return A named list containing all simulation parameters.
<- function(json_path) {
setup_param_from_json
# Import the JSON parameters
<- jsonlite::fromJSON(json_path)
json_file
# Extract the simulation_parameters list
<- json_file$simulation_parameters
sim_params
# Pull out the mean values from the nested structure
<- lapply(sim_params, function(x) x$params$mean)
values
# Pass them into Parameters (arguments matched by name)
do.call(Parameters$new, values)
}
# Import parameters
<- setup_param_from_json(
params file.path("parameters_file_resources", "example_parameters.json")
)
# View object
$get_params() params
$adult_consultation
[1] 20
$adult_interarrival
[1] 5
$adult_transfer
[1] 0.3
$child_consultation
[1] 15
$child_interarrival
[1] 7
$child_transfer
[1] 0.2
$elderly_consultation
[1] 30
$elderly_interarrival
[1] 10
$elderly_transfer
[1] 0.5
🔍 Explore the example models
🩺 Nurse visit simulation
Not applicable as the nurse visit example stores parameters in a script.
🧠 Stroke pathway simulation
Click to visit pydesrap_stroke repository
Key files | simulation/parameters.py inputs/parameters.json inputs/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.R inst/extdata/parameters.json inst/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, function vs class, 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.