This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Framework

The ready4 framework comprises a set of standards and the software to implement it.

1 - Standards

The ready4 framework identifies a set of standards to which the ready4 model, its datasets and analyses are expected to adhere.

An essential component of the ready4 framework is standardisation. Defining and adhering to a common set of standards is important because:

We have therefore developed a set of standards that we believe support good practice in the development of open source computational models. We are describing these standards, their rationale and how ready4 implements them, in a manuscript being prepared for publication. When it is publicly available we will provide a link in an updated version of this page.

2 - Implementation

The software to help ensure that the ready4 model adheres to consistent standards is distributed as a collection of framework code libraries.

2.1 - Paradigms

ready4 software is implemented using a combination of object-oriented and functional programming paradigms.

2.1.1 - Why ready4 is object oriented

ready4 uses an object oriented programming (OOP) paradigm to implement computational models.

This below section renders a vignette article from the ready4 library. You can use the following links to:

Motivation

The practical utility and ease of use of computational models of mental health systems are in part shaped by the choice of programming paradigm used to develop them. ready4 adopts an object oriented programming (OOP) paradigm which in practice means that the framework principally consists of classes (representations of data structures useful for modelling mental health systems) and methods (algorithms that can be applied to these data-structures to generate insight useful for policy-making). Adopting an OOP approach is particular useful for making the ready4 model both modular and transparent.

Implementation

Modular Computational Models

Two commonly noted features of OOP - encapsulation and inheritance are particularly useful when developing modular computational models.

Encapsulation

Encapsulation allows us to define the data structures (“classes”) used in computational modelling projects in a manner that allows them to be safely combined. For example, assume there are two computational models, one (A) focused on predicting the types and intensity of services used by individuals that present to mental health services and the other (B) that predicts outcomes for recipients of these services. It may be desirable to develop a new model (C) that combines A and B to model both service use and outcomes. Using encapsulated code allows all of the features and functionality of A can be made available to B in a manner that protects the integrity of A. Specifically, B can only interact with A using the algorithms (“methods”) that have been already defined for A.

Furthermore, if appropriately implemented, methods associated with a class will work with any combination of input values that can be encapsulated by that class - making computational models more transferable. For example, imagine a class (X) that is used to structure summary data relevant to mental health systems. Methods associated with X (e.g. a method to derive an unmet need statistic) can then applied to two instances of X - one containing data relevant to the Australian context and one with data from the UK context.

Inheritence

The examples highlighted in the previous section have some potential limitations. What if the developers of A didn’t define methods that would allow B to interact with it in the desired way? Or what if there are a number of differences between the Australian and UK system that need to be accounted for when transfering a method from the former to the latter? These types of issues can be addressed by another key feature of OOP - inheritance. Inheritance allows for a “child” class to be created from a “parent” class. By default, the “child” inherits all of the features of the “parent” including all methods associated with the “parent” class. Importantly however, alternative or additional features can also be specified for the “child” to allow it to implement different methods where necessary. For example, when developing our new computational model C we could create a number of new classes that are children of the classes defined in A. We can then define any additional/alternative methods for these classes that overcome any integration issues between the classes and methods of A and B. In this way, we can enjoy the best of both worlds - leveraging all relevant algorithms from A and B (as there is no need to re-invent the wheel), while ensuring that we transparently develop the additional code required for C. This approach also ensures that the respective contributions of the (potentially different) authorship teams behind A, B and C is clearer.

Similarly, inheritance would allow re-use of much of the code from a model of the Australian mental health system when exploring similar topics within the UK context, while making it straightforward to develop additional code that addresses relevant divergence in features between the two jurisdictions. In practical terms, this would mean developing two child classes of X - class Y for use with Australian data and class Z for use in the UK system. All methods that are not specific to a particular jurisdiction are defined for X and inherited by Y and Z. Methods that are only appropriate for use in the Australian context are defined for Y, while UK specific methods are defined for Z.

Transparent Computational Models

To make analyses implemented using the ready4 model more readily understood, the ready4 package provides the model’s simple and consistent syntax. Such simplified approaches are facilitated by two other commonly noted features of OOP - polymorphism and abstraction.

Polymorphism

Polymorphism allows for similar concepts to be represented using consistent syntax. The same top level code can therefore be transferred to multiple model implementations, making algorithms simpler to understand and easier to re-use.

Returning to a previous example, the exact same command (e.g. a call to the method exhibit) can be applied to both Y (used for Australian data) and Z (used for UK data). However, the algorithm implemented by that command can vary based on the class that each method is applied to (ie a different algorithm is applied when the data is specified as being from the UK compared to being specified as Australian).

Abstraction

The simplicity enabled by polymorphism is enhanced by Abstraction, which basically means that only the briefest and easiest to comprehend parts of the code are exposed by default to potential users. Once an instance of a class is created, the entire program to ingest model data, analyse it and produce a scientific summary can be represented in a few brief lines of code, readily comprehensible to non-coders. When using open source languages, the elegance and simplicity of abstraction does not restrict the ability of more technically minded users exploring the detailed workings of the underpinning code.

2.1.2 - The role of functional programming in ready4 development

ready4 uses functional programming to maximise the re-usability of model algorithms.

Although the object-oriented programming (OOP) approach ready4 implements has many advantages, it can also have some limitations. Some of these limitations have been colorfully highlighted by a popular quote attributed to Joe Armstrong:

“The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.”

In practical terms, this means that if not carefully planned, using OOP can create barriers to code-reuse as algorithms come bundled with artefacts of no/low relevance to many potential users. To help maximise the accessibility and re-usability of ready4 algorithms, these algorithms are primarily written using the functional programming paradigm. Only once an algorithm has been implemented using functions are they then linked to a data-structure by means of a calling method. The typical development workflow for a ready4 computational modelling project might therefore look something the following three step process:

  1. A modelling study algorithm is implemented as a program.

  2. To help transfer the methods used in the study algorithm, it is decomposed into functions, which are bundled as a code library (or libraries). The program is updated to use the newly authored functions.

  3. A ready4 module is authored to define a data-structure along with a method (or methods) that call the functions to implement the transferable version of the study algorithm. The new module is added to the previously created code library and the program is again updated so that the algorithm is now implemented by supplying data to the ready4 module and then calling the desired method(s).

Modellers using ready4 for the most part will only use ready4 modules and will rarely interact directly with the functions that implement module methods. However, these functions are potentially of significant usefulness to coders authoring new algorithms. A helpful way of exploring currently available functions is to use the ready4 dependencies app. All ready4 functions are created with minimal, but consistent documentation with the aid of tools from the ready4fun library.

2.2 - Modularity

ready4 supports a modular approach to computational model development.

This below section renders a vignette article from the ready4 library. You can use the following links to:

Motivation

A potentially attractive approach to modelling complex youth mental health systems is to begin with a relatively simple computational model and to progressively extend its scope and sophistication. Such an approach could be described as “modular” if it is possible to readily combine multiple discrete modelling projects (potentially developed by different modelling teams) that each independently describe distinct aspects of the system being modelled. This modular and collaborative approach is being used in the development of ready4 - an open source health economic model of the systems shaping mental health and wellbeing in young people. The ready4 package provides the foundational tools to support the development and application of the ready4 modular model.

Implementation

The ready4 model is being implemented in R and its modular nature is enabled by the encapsulation and inheritance features of Object Oriented Programming (OOP). Specifically, ready4 uses two of R’s systems for implementing OOP - S3 and S4. An in-depth explanation of R’s different class system is beyond the scope of this article, but is explored in Hadley Wickham’s Advanced R handbook. However, it is useful to know some very high level information about S3 and S4 classes:

  • S4 classes are frequently said to be “formal”, “strict” or “rigorous”. The elements of an S4 class are called slots and the type of data that each slot is allowed to contain is specified in the class definition. An S4 class can be comprised of slots that contain different types of data (e.g. a slot that contains a character vector and another slot that contains tabular data).

  • S3 classes are often described as “simple”, “informal” and “flexible”. S3 objects attach an attribute label to base type objects (e.g. a character vector, a data.frame, a list), which in turn is used to work out what methods should be applied to the class.

ready4 Model Modules

A ready4 model module is a data-structure and associated algorithms that is used to model a discrete component of a system relevant to young people’s mental health. Each ready4 model module is created using the ready4 package’s Ready4Module class. We can create an instance (X) of Ready4Module using the following command.

X <- ready4::Ready4Module()

However, if we inspect X we can see it is of limited use as it contains no data other than an empty element called dissemination_1L_chr.

str(X)
#> Formal class 'Ready4Module' [package "ready4"] with 1 slot
#>   ..@ dissemination_1L_chr: chr NA

The Ready4Module class is therefore not intended to be called directly. Instead, the purpose of Ready4Module is to be the parent-class of all ready4 model modules. Ready4Module and all of its child-classes (ie all ready4 model modules) are “S4” classes.

ready4 Concept

Module

A formal (S4) Ready4Module child-class and its associated methods used to implement a discrete sub-component of the ready4 youth mental health model.

ready4 includes two child classes of Ready4Module. These are Ready4Public and Ready4Private and both are almost as minimally informative as their parent (the only difference being that their instances have the values “Public” or “Private” assigned to the dissemination_1L_chr slot).

Y <- Ready4Public()
str(Y)
#> Formal class 'Ready4Public' [package "ready4"] with 1 slot
#>   ..@ dissemination_1L_chr: chr "Public"
Z <- Ready4Private()
str(Z)
#> Formal class 'Ready4Private' [package "ready4"] with 1 slot
#>   ..@ dissemination_1L_chr: chr "Private"

Like the Ready4Module class they inherit from, the purpose of Ready4Public and Ready4Private is to be used as parent classes. Using either of Ready4Public and Ready4Private can be a potentially efficient way of partially automating access policies for model data. If all the data contained in a module can always be shared publicly, it may be convenient to note this by using a module that has been created as a child-class of Ready4Public. Similarly, if at least some of the data contained in a module will always be unsuitable for public dissemination, it can be useful to use a module that is a child of Ready4Private. When the dissemination policy for data contained in a module will vary depending on user or context, it is more appropriate to use a module that inherits from Ready4Module without being a child of either Ready4Public and Ready4Private. In this latest case, users may choose to add descriptive information about the data access policy themselves using the renewSlot method. The dissemination policy can be inspected with the procureSlot method.

X <- renewSlot(X,
               "dissemination_1L_chr",
               "Staff and students of research institutes")
procureSlot(X,
            "dissemination_1L_chr")
#> [1] "Staff and students of research institutes"

ready4 Model Sub-modules

In ready4, S3 classes are principally used to help define the structural properties of slots (array elements) of model modules and the methods that can be applied to these slots. S3 classes created for these purposes are called sub-modules.

ready4 Concept

Sub-Module

An informal (S3) class and its associated methods that describes, validates and applies algorithms to a slot of a ready4 module.

Module and Sub-module Methods

All methods associated with ready4 modules and sub-modules adopt a common syntax. However, the algorithms implemented by each command in that syntax will vary depending on which module it is applied to. A limited number of methods are defined at the level of the Ready4Module parent class and are therefore inherited by all ready4 modules. Currently, the only methods defined for Ready4Module are slot-methods and these can be itemised using the get_methods function.

get_methods()
#>  [1] "authorSlot"        "characterizeSlot"  "depictSlot"       
#>  [4] "enhanceSlot"       "exhibitSlot"       "ingestSlot"       
#>  [7] "investigateSlot"   "manufactureSlot"   "metamorphoseSlot" 
#> [10] "procureSlot"       "prognosticateSlot" "ratifySlot"       
#> [13] "reckonSlot"        "renewSlot"         "shareSlot"

2.3 - Syntax

ready4 modules use a simple and consistent syntax.

This below section renders a vignette article from the ready4 library. You can use the following links to:

Motivation

Transparency is one of the underpinning principles of open science. One way to improve the transparency of the ready4 model is to ensure that the programs implementing analyses using this model can be meaningfully inspected by readers with different levels of technical expertise. Even non-technical readers should be able to follow the high-level logic implemented by model algorithms. By using a simple programming syntax that can be consistently used across all model analyses programs, ready4 can help ensure that readers need to contend with relatively few new concepts when reviewing analysis code.

Implementation

ready4 provides a simple syntax that can be consistently applied to all ready4 model modules. It does so by taking advantage of the polymorphism and abstraction features of Object Oriented Programing and R’s use of generic functions. Generic functions don’t obviously do anything by themselves - their most salient features are a name and a high level description of the type of task that any method using that name should perform. Whenever a method is defined for classes that use R’s S4 and S3 systems (the types used for ready4 model modules and sub-modules), it is assigned to the generic that is the best match for the algorithm it implements.

Finding ready4 Methods

A table that summarises the syntax used by ready4 model module methods, can be generated by web-scraping using make_methods_tb (which produces up to date results but can be a little slow to excecute) or alternatively be downloaded from a periodically updated database using get_methods_tb (which is quicker to implement, but may miss the most recent additions).

# Not run
# x <- make_methods_tb()

Core generics

ready4 includes a number of core generic functions which describe the main types of method to be implemented by ready4 model modules. Notably, the ready4 package does not define methods for any of these core generics. Instead, methods are defined for these generics in R packages that contain ready4 modules. A HTML table of the core generics bundled with ready4 and examples of methods that implement each generic can be displayed using the print_methods function, using the return_1L_chr = "core" argument.

print_methods(x,
              return_1L_chr = "core",
              scroll_width_1L_chr = "100%") 
Method Purpose Examples
author Author and save files 5 , 6
characterize Characterize data by generating (tabular) descriptive statistics
depict Depict (plot) features of a dataset 2, 3 , 4
enhance Enhance a dataset by adding new elements
exhibit Exhibit features of a dataset by printing them to the R console 2, 3 , 4 , 6
ingest Ingest data 2, 3 , 4 , 5 , 6
investigate Investigate solutions to an inverse problem 6
manufacture Manufacture a new object
metamorphose Metamorphose data from one model module (or sub-module) instance to an instance of a different model module or sub-module 5 , 6
procure Procure items from a dataset 6
prognosticate Prognosticate (make predictions) by solving a forward problem
ratify Ratify that a dataset meets validity criteria 2, 6
reckon Reckon (calculate) a value
renew Renew values in a dataset 2, 3 , 4 , 5 , 6
share Share data via an online repository 2, 3 , 4

Slot generics and methods

Each of the “core” generics also has a “slot” version, for use when applying a core method to a specified slot of a class. The ready4 package defines methods for each of these “slot” generics for the Ready4Module class. Two of these “slot” methods can also be used for additional purposes:

  • procureSlot is a “getter” method - its default behaviour is to return the value of a specified slot. If the argument use_procure_mthd_1L_lgl = T is included in the method call, procureSlot will instead apply the procure method to a specified slot.

  • renewSlot is a “setter” method - if any value other than “use_renew_mthd” (the default) is passed to the new_val_xx argument, that value will be assigned to the specified slot.

A HTML table of the slot generics bundled with ready4 can be displayed using the print_methods function, using the return_1L_chr = "slot" argument.

print_methods(x,
              return_1L_chr = "slot",
              scroll_width_1L_chr = "100%")
Method Purpose Examples
authorSlot Apply the author method to a model module slot 5
characterizeSlot Apply the characterize method to a model module slot
depictSlot Apply the depict method to a model module slot
enhanceSlot Apply the enhance method to a model module slot 5
exhibitSlot Apply the exhibit method to a model module slot 5 , 6
ingestSlot Apply the ingest method to a model module slot
investigateSlot Apply the investigate method to a model module slot 5
manufactureSlot Apply the manufacture method to a model module slot 5
metamorphoseSlot Apply the metamorphose method to a model module slot 5
procureSlot Procure (get) data from a slot 3 , 5 , 6
prognosticateSlot Apply the prognosticate method to a model module slot
ratifySlot Apply the ratify method to a model module slot 5
reckonSlot Apply the reckon method to a model module slot
renewSlot Apply the renew method to a model module slot 2, 3 , 5 , 6
shareSlot Apply the share method to a model module slot 5

Extended author generics

Finally, there are a small number of other generics that are more general extensions of the core functions. Currently, these extended generics are all variants on the author generics, with each specifying the type of output to be authored by the method. The ready4 package does not include methods for any of these extended generics. A HTML table of the extended generics bundled with ready4 can be displayed using the print_methods function, using the return_1L_chr = "extended" argument.

print_methods(x,
              exclude_mthds_for_chr = "Ready4Module",
              return_1L_chr = "extended",
              scroll_width_1L_chr = "100%")
Method Purpose Examples
authorClasses Author and document classes
authorData Author and document datasets 5 , 6
authorFunctions Author and document functions
authorReport Author and save a report