The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
A component is a reusable code template for one data transformation
step. Its code takes an input data set and returns it with new or
changed columns or rows. Mustache placeholders, such as
{{{domain}}}, are filled in when the component is rendered,
so the same template works across studies. Roxygen-like tags, such as
@title, @param and @depends,
document what the component does, needs and creates.
Components are commonly used to build ADaM (Analysis Data Model)
programs. Each component handles one derivation, and several components
make up a program. In the mighty ecosystem, mighty.metadata provides
study configuration (via mighty_study()
and _study.yml) that controls which components are rendered
and with which parameters.
Below is a minimal component that creates a column as two times an existing column:
#' @title Double a column
#' @description
#' Creates a new column as two times an existing column.
#'
#' @param domain `character` Name of the domain
#' @param output `character` Name of the new column
#' @param input `character` Name of the existing column
#' @type column
#' @origin Derived
#' @method Two times the input column
#' @depends {{{domain}}} {{{input}}}
#' @outputs {{{output}}}
#' @code
{{{domain}}} <- {{{domain}}} |>
dplyr::mutate(
{{{output}}} = 2 * {{{input}}}
)The template syntax is described below.
A template starts with tags in roxygen comments (#').
All lines below the @code tag are the R code.
Templates use Mustache
placeholders. $render() fills them in with
whisker::whisker.render().
{{{name}}} inserts the value of parameter
name. Always use triple braces. Double braces
({{name}}) HTML-escape the value, e.g. a<b
becomes a<b.{{#name}}...{{/name}} repeats the enclosed text for
each element of the vector name. Use {{{.}}}
to insert the current element.column: Adds or modifies columns. The row count is
unchanged.row: Adds, removes or modifies rows.parameter: Derives a new PARAMCD (BDS
parameter).internal: Helper step with no define.xml output.{{{domain}}}.{{{domain}}}.@param.dplyr::mutate().by. This is enforced when rendering.See ?mighty_component for validation rules and a
rendered example.
List the example components shipped with the package:
Retrieve one by file path:
ady <- get_component(
system.file("examples", "ady.mustache", package = "mighty.component")
)
ady
#> <mighty_component/R6>
#> ady: Analysis relative day
#> Type: column
#> Parameters:
#> • domain: `character` Name of new domain being created
#> • variable: `character` Name of new variable to create
#> • date: `character` Name of date variable to use
#> Depends:
#> • {{{domain}}}.{{{date}}}
#> • {{{domain}}}.TRTSDT
#> Outputs:
#> • {{{variable}}}Access individual fields:
ady$id
#> [1] "ady"
ady$title
#> [1] "Analysis relative day"
ady$type
#> [1] "column"
ady$params
#> name description
#> 1 domain `character` Name of new domain being created
#> 2 variable `character` Name of new variable to create
#> 3 date `character` Name of date variable to use
ady$depends
#> domain column
#> 1 {{{domain}}} {{{date}}}
#> 2 {{{domain}}} TRTSDT
ady$outputs
#> [1] "{{{variable}}}"
ady$origin
#> [1] "Derived"
ady$method
#> [1] "Relative day computed from treatment start date"Rendering fills in the placeholders with values.
$render() takes one named argument per @param
tag and returns a mighty_component_rendered object. Every
placeholder is now a name:
ady_rendered <- ady$render(domain = "ADAE", variable = "ASTDY", date = "ASTDT")
ady_rendered
#> <mighty_component_rendered/mighty_component/R6>
#> ady: Analysis relative day
#> Type: column
#> Depends:
#> • ADAE.ASTDT
#> • ADAE.TRTSDT
#> Outputs:
#> • ASTDY
#> Code:
#> ADAE <- ADAE |>
#> dplyr::mutate(
#> ASTDY = admiral::compute_duration(
#> start_date = TRTSDT,
#> end_date = ASTDT,
#> in_unit = 'days',
#> out_unit = 'days',
#> add_one = TRUE
#> )
#> )The rendered code is validated: it must be valid R, and joins must
specify by (see Automatic code validation).
Columns are not checked. Use get_test_component() (see Testing components) to run a component on
real data.
get_rendered_component() retrieves and renders in one
step. It takes the parameters as a named list:
get_rendered_component(
system.file("examples", "ady.mustache", package = "mighty.component"),
list(domain = "ADAE", variable = "ASTDY", date = "ASTDT")
)
#> <mighty_component_rendered/mighty_component/R6>
#> ady: Analysis relative day
#> Type: column
#> Depends:
#> • ADAE.ASTDT
#> • ADAE.TRTSDT
#> Outputs:
#> • ASTDY
#> Code:
#> ADAE <- ADAE |>
#> dplyr::mutate(
#> ASTDY = admiral::compute_duration(
#> start_date = TRTSDT,
#> end_date = ASTDT,
#> in_unit = 'days',
#> out_unit = 'days',
#> add_one = TRUE
#> )
#> )A missing parameter raises an error:
$eval() runs the code. By default it runs in the calling
environment. Use the envir argument to choose another
environment. The code assigns the result back to the domain (e.g.,
ADAE <- ADAE |> ...), so ADAE is updated
without assigning the return value.
ADAE <- pharmaverseadam::adae |>
dplyr::select(USUBJID, ASTDT, TRTSDT)
names(ADAE)
#> [1] "USUBJID" "ASTDT" "TRTSDT"ASTDY does not exist yet. Run the rendered
component:
ady_rendered$eval()
names(ADAE)
#> [1] "USUBJID" "ASTDT" "TRTSDT" "ASTDY"
head(ADAE)
#> # A tibble: 6 × 4
#> USUBJID ASTDT TRTSDT ASTDY
#> <chr> <date> <date> <dbl>
#> 1 01-701-1015 2014-01-03 2014-01-02 2
#> 2 01-701-1015 2014-01-03 2014-01-02 2
#> 3 01-701-1015 2014-01-09 2014-01-02 8
#> 4 01-701-1023 2012-08-07 2012-08-05 3
#> 5 01-701-1023 2012-08-07 2012-08-05 3
#> 6 01-701-1023 2012-08-07 2012-08-05 3$stream() appends the code to an R script instead:
script_file <- tempfile(fileext = ".R")
ady_rendered$stream(script_file)
readLines(script_file)
#> [1] "ADAE <- ADAE |>"
#> [2] " dplyr::mutate("
#> [3] " ASTDY = admiral::compute_duration("
#> [4] " start_date = TRTSDT,"
#> [5] " end_date = ASTDT,"
#> [6] " in_unit = 'days',"
#> [7] " out_unit = 'days',"
#> [8] " add_one = TRUE"
#> [9] " )"
#> [10] " )"Write your own components as .mustache files. This
component derives the ratio of the analysis value to baseline:
#' @title Ratio to baseline
#' @description
#' Derives the ratio of the analysis value to the baseline value.
#'
#' @param domain `character` Name of the domain
#' @param variable `character` Name of the new ratio variable
#' @type column
#' @origin Derived
#' @method Ratio of AVAL to BASE
#' @depends {{{domain}}} AVAL
#' @depends {{{domain}}} BASE
#' @outputs {{{variable}}}
#' @code
{{{domain}}} <- {{{domain}}} |>
dplyr::mutate(
{{{variable}}} = dplyr::if_else(BASE != 0, AVAL / BASE, NA_real_)
)Save the template as r2base.mustache. Then load, render
and run it:
r2base <- get_component(r2base_file)
r2base
#> <mighty_component/R6>
#> r2base: Ratio to baseline
#> Type: column
#> Parameters:
#> • domain: `character` Name of the domain
#> • variable: `character` Name of the new ratio variable
#> Depends:
#> • {{{domain}}}.AVAL
#> • {{{domain}}}.BASE
#> Outputs:
#> • {{{variable}}}r2base_rendered <- r2base$render(
domain = "ADLB",
variable = "R2BASE"
)
r2base_rendered$code
#> [1] "ADLB <- ADLB |>"
#> [2] " dplyr::mutate("
#> [3] " R2BASE = dplyr::if_else(BASE != 0, AVAL / BASE, NA_real_)"
#> [4] " )"ADLB <- pharmaverseadam::adlb |>
dplyr::filter(PARAMCD == "ALB") |>
dplyr::select(USUBJID, PARAMCD, AVISIT, AVAL, BASE)
head(ADLB)
#> # A tibble: 6 × 5
#> USUBJID PARAMCD AVISIT AVAL BASE
#> <chr> <chr> <chr> <dbl> <dbl>
#> 1 01-701-1015 ALB Baseline 38 38
#> 2 01-701-1015 ALB Week 2 39 38
#> 3 01-701-1015 ALB POST-BASELINE MAXIMUM 39 38
#> 4 01-701-1015 ALB Week 4 38 38
#> 5 01-701-1015 ALB Week 6 37 38
#> 6 01-701-1015 ALB POST-BASELINE MINIMUM 37 38
r2base_rendered$eval()
ADLB |>
dplyr::select(USUBJID, PARAMCD, AVISIT, AVAL, BASE, R2BASE) |>
head()
#> # A tibble: 6 × 6
#> USUBJID PARAMCD AVISIT AVAL BASE R2BASE
#> <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 01-701-1015 ALB Baseline 38 38 1
#> 2 01-701-1015 ALB Week 2 39 38 1.03
#> 3 01-701-1015 ALB POST-BASELINE MAXIMUM 39 38 1.03
#> 4 01-701-1015 ALB Week 4 38 38 1
#> 5 01-701-1015 ALB Week 6 37 38 0.974
#> 6 01-701-1015 ALB POST-BASELINE MINIMUM 37 38 0.974A component repo is a collection of components.
get_component(), find_component() and
list_components() take a repos argument with
one or more repo specs:
"inst/components" or
"local::inst/components"."github::owner/repo/subdir@ref". Requires the gh and
remotes packages."url::https://example.com/components". Requires httr2
(>= 1.2.2).A component name is the file
<name>.mustache or <name>.R,
either directly in the repo or in a directory
<name>/. See ?mighty_repo for
details.
Put the r2base component from above in a team repo,
together with a modified copy of ady:
examples <- system.file("examples", package = "mighty.component")
team_repo <- tempfile("team")
dir.create(team_repo)
invisible(file.copy(r2base_file, team_repo))
ady_template <- readLines(file.path(examples, "ady.mustache"))
ady_template[[1]] <- "#' @title Analysis relative day (team version)"
writeLines(ady_template, file.path(team_repo, "ady.mustache"))Several repos are searched in order, and the first match wins. Here
ady is found in the team repo:
repos <- c(team_repo, examples)
list_components(repos)
#> [1] "ady" "r2base"
get_component("ady", repos = repos)$title
#> [1] "Analysis relative day (team version)"Reverse the order to use ady from the examples:
find_component() returns NULL if no repo
contains the component. get_component() raises an
error:
is.null(find_component("adt", repos = repos))
#> [1] TRUE
get_component("adt", repos = repos)
#> Error in `get_component()`:
#> ! Component `adt` not foundlist_components(as = "tibble") returns the metadata of
each component. It requires the tibble and tidyr packages:
list_components(repos, as = "tibble") |>
dplyr::select(id, title, type, origin)
#> # A tibble: 2 × 4
#> id title type origin
#> <chr> <chr> <chr> <chr>
#> 1 ady Analysis relative day (team version) column Derived
#> 2 r2base Ratio to baseline column DerivedA character vector is converted to repos on each call. Use
mighty_repos() to create the repos once and reuse them.
GitHub and URL repos need network access:
repos <- mighty_repos(c(
"github::NovoNordisk-OpenSource/mighty.standards/components@main",
paste0(
"url::https://raw.githubusercontent.com/",
"NovoNordisk-OpenSource/mighty.standards/main/components"
),
examples
))
get_component("dummy", repos = repos)See ?mighty_repo_github and
?mighty_repo_url for authentication and retries.
Rendered code is validated. It must be valid R, and joins from dplyr,
tidylog and dbplyr must specify by. Implicit joins are a
common source of bugs when key columns differ between studies.
This component fails validation:
#' @title Bad join example
#' @description Implicit join that will fail validation.
#'
#' @param domain `character` Name of the domain
#' @type row
#' @origin Derived
#' @method Implicit join on other_data
#' @depends {{{domain}}} USUBJID
#' @outputs MYCOL
#' @code
{{{domain}}} <- {{{domain}}} |>
dplyr::left_join(other_data)get_rendered_component(bad_file, list(domain = "ADAE"))
#> Error in `abort_validation_errors()`:
#> ! Component validation failed:
#>
#> ! Implicit dplyr join(s) detected in rendered component:
#> ✖ Join operations must explicitly specify the `by` argument
#> ℹ Line 2: dplyr::left_joinSpecify the join key with by to fix it:
#' @title Good join example
#' @description Explicit join that passes validation.
#'
#' @param domain `character` Name of the domain
#' @type row
#' @origin Derived
#' @method Explicit join on other_data by USUBJID
#' @depends {{{domain}}} USUBJID
#' @outputs MYCOL
#' @code
{{{domain}}} <- {{{domain}}} |>
dplyr::left_join(other_data, by = dplyr::join_by(USUBJID))get_test_component() creates a component that runs in a
separate R session and tracks code coverage. Use it for interactive
checks and for unit tests with testthat.
By default (check_coverage = TRUE), coverage is checked
when the calling environment ends, e.g. the test_that()
block. An error is raised if any line has not run. This vignette is not
a test, so it sets check_coverage = FALSE.
ady_path <- system.file(
"examples", "ady.mustache",
package = "mighty.component"
)
ady_test <- get_test_component(
component = ady_path,
params = list(domain = "ADAE", variable = "ASTDY", date = "ASTDT"),
check_coverage = FALSE # keep the default TRUE in tests
)
ady_test
#> <mighty_component_test/mighty_component_rendered/mighty_component/R6>
#> ady: Derives the relative day compared to the treatment start date.
#> Test Coverage: 0.00%
#> Code: (✔ Covered, ✖ Uncovered)
#> ✖ ADAE <- ADAE |>
#> ✖ dplyr::mutate(
#> ✖ ASTDY = admiral::compute_duration(
#> ✖ start_date = TRTSDT,
#> ✖ end_date = ASTDT,
#> ✖ in_unit = 'days',
#> ✖ out_unit = 'days',
#> ✖ add_one = TRUE
#> ✖ )
#> ✖ )Assign input data in the test session:
adae_input <- pharmaverseadam::adae |>
dplyr::select(USUBJID, ASTDT, TRTSDT)
ady_test$assign("ADAE", adae_input)
ady_test$ls()
#> [1] "ADAE"Run the component and get the result:
ady_test$eval()
ady_test$get("ADAE") |> head()
#> # A tibble: 6 × 4
#> USUBJID ASTDT TRTSDT ASTDY
#> <chr> <date> <date> <dbl>
#> 1 01-701-1015 2014-01-03 2014-01-02 2
#> 2 01-701-1015 2014-01-03 2014-01-02 2
#> 3 01-701-1015 2014-01-09 2014-01-02 8
#> 4 01-701-1023 2012-08-07 2012-08-05 3
#> 5 01-701-1023 2012-08-07 2012-08-05 3
#> 6 01-701-1023 2012-08-07 2012-08-05 3Every line of the code has now run:
# Print method
ady_test
#> <mighty_component_test/mighty_component_rendered/mighty_component/R6>
#> ady: Derives the relative day compared to the treatment start date.
#> Test Coverage: 100.00%
#> Code: (✔ Covered, ✖ Uncovered)
#> ✔ ADAE <- ADAE |>
#> ✔ dplyr::mutate(
#> ✔ ASTDY = admiral::compute_duration(
#> ✔ start_date = TRTSDT,
#> ✔ end_date = ASTDT,
#> ✔ in_unit = 'days',
#> ✔ out_unit = 'days',
#> ✔ add_one = TRUE
#> ✔ )
#> ✔ )
# Percent coverage
ady_test$percent_coverage
#> [1] 100
# Line coverage as a data.frame
ady_test$line_coverage
#> line value
#> 1 1 1
#> 2 2 1
#> 3 3 1
#> 4 4 1
#> 5 5 1
#> 6 6 1
#> 7 7 1
#> 8 8 1
#> 9 9 1
#> 10 10 1In a testthat file, create the test component inside
test_that(), assign data, evaluate and test the results.
Coverage is checked when the test ends. See
?mighty_component_test for the workflow.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.