In this document, we illustrate a way to create mockup table by using the metadata created by metalite. An easy-to-follow example is used for illustration purpose. If users want to get a comprehensive production ready work, then users need to refine this simple example to align with their cases.
In this example, the available datasets are adae and adsl, which are
available in the r2rtf package, i.e., r2rtf::r2rtf_adae
and
r2rtf::r2rtf_adsl
. For these two datasets, our objective is
to conduct two adverse event (AE) analysis: (1) AE summary analysis and
(3) specific AE analysis. And the population for these two analysis is
all participants as treated (APaT). Besides, there is one observations
for these two analysis: weeks 0-12. In the analysis, we are interested
in three types of AEs: (1) any AEs, (2) series AEs, and (3) drug-related
AEs.
Please note the above example servers for illustration purpose, instead of a comprehensive production ready work. If users are interested in more AE analysis with more AE categories, or get more types of populations/observations, modification of code in this vignette is needed.
In this section, we introduce the steps to prepare the metadata used for the aforementioned two AE analysis.
To define the metadata, users need to specify both population and
observations datasets. In our example, the observation dataset comes
from r2rtf::r2rtf_adae
and the population dataset comes
from r2rtf::r2rtf_adsl
. So we define the metadata by
meta_adam()
as follows, where users can specify the
observation dataset name and population dataset name.
To start mockup tables, we begin with defining the mockup analysis
plan. To define an analysis plan, we use plan()
and
add_plan()
to add details per one mockup
analysis.
Recall in this example, we have 2 analysis plans:
For the AE summary analysis, we will define
analysis = "ae_summary"
. Similarly we define proper
population, observation and parameter key words for
population = "apat"
: All Participants as Treated
(APaT).observation = "wk12"
: Weeks 0-12 analyses.parameter = "any;rel;ser"
: a table to cover “any”,
“drug related” and “serious” adverse events.For the AE specific analysis, users can follow the similar logic as in AE summary.
So, we can define an analysis plan as below for a total of 2 mockup to generate 4 TLFs.
plan <- plan(
analysis = "ae_summary", population = "apat",
observation = "wk12", parameter = "any;rel;ser"
) |>
add_plan(
analysis = "ae_specific", population = "apat",
observation = "wk12",
parameter = c("any", "rel", "ser")
)
plan
#> mock analysis population observation parameter
#> 1 1 ae_summary apat wk12 any;rel;ser
#> 2 2 ae_specific apat wk12 any
#> 3 2 ae_specific apat wk12 rel
#> 4 2 ae_specific apat wk12 ser
Then, we use incorporate the above defined plan into the metadata by
using define_plan()
, i.e.,
After defining the overview of the analysis plan, we begin to define the meaning of key words.
Define keywords in population:
The following code define the keywords "apat"
, which is
a population keywords for APaT population. And here we also cover the
key information such as group variable, the group order, etc…
meta <- meta |>
define_population(
name = "apat",
group = "TRTA",
group_order = c(
"High Dose" = "Xanomeline High Dose",
"Placebo" = "Placebo"
),
subset = SAFFL == "Y"
)
Define keywords in observation:
The following code define the keywords "wk12"
, which is
an observation keywords for Weeks 0-12 observations.
meta <- meta |>
define_observation(
name = "wk12",
group = "TRTA",
ae_var = "AEDECOD",
var = c("AEREL", "AESER"),
subset = SAFFL == "Y",
label = "Weeks 0 to 12"
)
Define keywords in analysis plans:
Note that in the analysis plan, there are 5 keywords:
"any"
: a parameter keywords for any adverse
events."rel"
: a parameter keywords for drug-related adverse
events."ser"
: a parameter keywords for serious adverse
events."ae_summary"
: an analysis keywords for AE summary
analysis."ae_specific"
: an analysis keywords for specific AE
analysis.For the first three keywords, we can define them by
define_parameter()
as follows. Here we can add (1) the
endnotes, (2) labels, (3) the criterion to filter, etc.
meta <- meta |>
define_parameter(
name = "any",
end_notes = c()
) |>
define_parameter(
name = "rel",
subset = AEREL %in% c("POSSIBLE", "PROBABLE"),
label = "drug-realted adverse events"
) |>
define_parameter(
name = "ser",
subset = AESER == "Y",
label = "serious adverse events",
end_notes = c("Serious adverse events up to 90 days of last dose are included.")
)
For the last two keywords, we can define them by
define_analysis()
. In this vignette, we only present the
mockup table of AE summary. If users are interested in specific AE
analysis, please define the keywords for "ae_specific"
as
define_analysis(name = "ae_specific", title = ...)
.
Once all keywords, datasets, analysis plan are defined, users can build the metadata to compose all the information.
After the entire metadata is built, users can review the analysis plan by
meta$plan
#> mock analysis population observation parameter
#> 1 1 ae_summary apat wk12 any;rel;ser
#> 2 2 ae_specific apat wk12 any
#> 3 2 ae_specific apat wk12 rel
#> 4 2 ae_specific apat wk12 ser
And the entire meta is
meta
#> ADaM metadata:
#> .$data_population Population data with 254 subjects
#> .$data_observation Observation data with 1191 records
#> .$plan Analysis plan with 4 plans
#>
#>
#> Analysis population type:
#> name id group var subset label
#> 1 'apat' 'USUBJID' 'TRTA' SAFFL == 'Y' 'All Participants as Treated'
#> group_order
#> 1 Xanomeline High Dose, Placebo
#>
#>
#> Analysis observation type:
#> name id group var subset label ae_var
#> 1 'wk12' 'USUBJID' 'TRTA' AEREL, AESER SAFFL == 'Y' 'Weeks 0 to 12' 'AEDECOD'
#>
#>
#> Analysis parameter type:
#> name label subset
#> 1 'any' 'any adverse events'
#> 2 'rel' 'drug-realted adverse events' AEREL %in% c('POSSIBLE', 'PROBABLE')
#> 3 'ser' 'serious adverse events' AESER == 'Y'
#>
#>
#> Analysis function:
#> name label
#> 1 'ae_summary' 'Table: adverse event summary'
#> 2 'ae_specific' 'Table: specific adverse event'
Based on the mockup table, we specify additional parameters that are required in the function we will define in the next section.
mockup = TRUE
as we will create mockup for both
analysis.output_report
to define the output path based on
analysis
keywords.meta$plan <- meta$plan |>
mutate(
mockup = TRUE,
output_report = paste0("./tlf/mock-", analysis, ".rtf")
)
meta$plan
#> mock analysis population observation parameter mockup
#> 1 1 ae_summary apat wk12 any;rel;ser TRUE
#> 2 2 ae_specific apat wk12 any TRUE
#> 3 2 ae_specific apat wk12 rel TRUE
#> 4 2 ae_specific apat wk12 ser TRUE
#> output_report
#> 1 ./tlf/mock-ae_summary.rtf
#> 2 ./tlf/mock-ae_specific.rtf
#> 3 ./tlf/mock-ae_specific.rtf
#> 4 ./tlf/mock-ae_specific.rtf
After the plan is updated, we can construct call program. Here we also provide a global argument for the data source.
spec_call_program(meta,
data_source = "[adam-adsl; adae]"
)
#> [1] "ae_summary(meta = meta, population = 'apat', observation = 'wk12', parameter = 'any;rel;ser', mockup = TRUE, output_report = './tlf/mock-ae_summary.rtf', data_source = '[adam-adsl; adae]')"
#> [2] "ae_specific(meta = meta, population = 'apat', observation = 'wk12', parameter = 'any', mockup = TRUE, output_report = './tlf/mock-ae_specific.rtf', data_source = '[adam-adsl; adae]')"
#> [3] "ae_specific(meta = meta, population = 'apat', observation = 'wk12', parameter = 'rel', mockup = TRUE, output_report = './tlf/mock-ae_specific.rtf', data_source = '[adam-adsl; adae]')"
#> [4] "ae_specific(meta = meta, population = 'apat', observation = 'wk12', parameter = 'ser', mockup = TRUE, output_report = './tlf/mock-ae_specific.rtf', data_source = '[adam-adsl; adae]')"
The pending effort is to define an ae_summary()
and an
ae_specific()
function that can generate the mock up tables
based on the inputs.
Let’s create a simplified version of ae_summary()
to
illustrate the idea.
ae_summary()
ae_summary <- function(meta,
population,
observation,
parameter,
mockup,
output_report,
data_source,
...) {
# Identify parameter keywords
para_list <- unlist(strsplit(parameter, ";"))
# Row label
ae_label <- vapply(para_list,
FUN = function(x) {
collect_adam_mapping(meta, x)$label
},
FUN.VALUE = character(1)
)
# Get treatment grouping order
trt_order <- eval(collect_adam_mapping(meta, population)$group_order)
# Get the title/endnotes of TLF
title_text <- collect_title(
meta = meta,
population = population,
observation = observation,
parameter = parameter, analysis = "ae_summary"
)
title_text[2] <- "{Week}"
# Logic to create mockup table
if (mockup) {
# Generate RTF
x <- tibble::tibble(
ae_label = ae_label,
n_1 = rep("x", length(ae_label)),
n_2 = rep("x", length(ae_label)), # no display_total/CI
pct_1 = rep("x.xx", length(ae_label)),
pct_2 = rep("x.xx", length(ae_label))
) |>
dplyr::select(ae_label, n_1, pct_1, n_2, pct_2, everything()) |>
# r2rtf::rtf_title(rtf_mock_color(title_text)) |>
r2rtf::rtf_title(title_text) |>
r2rtf::rtf_colheader(paste0(" | ", paste(names(trt_order), collapse = " | "), " "),
col_rel_width = c(3, rep(2, length(trt_order)))
) |>
r2rtf::rtf_colheader(" | n | (%) | n | (%) ",
border_top = c("", rep("single", 2 * length(trt_order))),
border_bottom = "single",
border_left = c("single", rep(c("single", ""), length(trt_order))),
col_rel_width = c(3, rep(1, 2 * length(trt_order)))
) |>
r2rtf::rtf_body(
col_rel_width = c(3, rep(1, 2 * length(trt_order))),
border_left = c("single", rep(c("single", ""), length(trt_order))),
text_justification = c("l", rep("c", 2 * length(trt_order)))
) |>
r2rtf::rtf_source(data_source)
# Require r2rtf to use color
attr(x, "page")$use_color <- TRUE
# Save RTF to a path
if (!is.null(output_report)) {
x |>
r2rtf::rtf_encode() |>
r2rtf::write_rtf(output_report)
}
}
if (!mockup) {
# Logic to perform actual AE summary table.
# Omit here
}
output_report
}
For illustration purposes, we only create mock up table for the first row.