From 31ecb20bda90d8d3b54e7f9f5ce58905e4f05ee4 Mon Sep 17 00:00:00 2001 From: Stefan Bundfuss Date: Thu, 13 Jul 2023 13:29:27 +0000 Subject: [PATCH 01/22] #1960 enhance_derive_extreme_event: start a draft --- NAMESPACE | 1 + R/derive_extreme_event.R | 211 +++++++++++++++++++-- man/basket_select.Rd | 1 + man/censor_source.Rd | 1 + man/date_source.Rd | 1 + man/dthcaus_source.Rd | 1 + man/event.Rd | 35 +++- man/event_joined.Rd | 96 ++++++++++ man/event_source.Rd | 1 + man/query.Rd | 1 + man/records_source.Rd | 1 + man/tte_source.Rd | 1 + man/tte_source_objects.Rd | 1 + tests/testthat/test-derive_extreme_event.R | 112 +++++++++++ 14 files changed, 438 insertions(+), 26 deletions(-) create mode 100644 man/event_joined.Rd diff --git a/NAMESPACE b/NAMESPACE index 500190a5ee..e8b78072f6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -125,6 +125,7 @@ export(desc) export(dose_freq_lookup) export(dthcaus_source) export(event) +export(event_joined) export(event_source) export(exprs) export(extend_source_datasets) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index e81dadfd86..4539a6a6b3 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -107,17 +107,33 @@ derive_extreme_event <- function(dataset, events, order, mode, + source_datasets = NULL, check_type = "warning", set_values_to) { # Check input parameters assert_vars(by_vars, optional = TRUE) - assert_list_of(events, "event") + assert_list_of(events, "event_def") assert_expr_list(order) assert_data_frame( dataset, required_vars = by_vars ) mode <- assert_character_scalar(mode, values = c("first", "last"), case_sensitive = FALSE) + assert_list_of(source_datasets, "data.frame") + source_names <- names(source_datasets) + assert_list_element( + list = events, + element = "dataset_name", + condition = is.null(dataset_name) || dataset_name %in% source_names, + source_names = source_names, + message_text = paste0( + "The dataset names must be included in the list specified for the ", + "`source_datasets` parameter.\n", + "Following names were provided by `source_datasets`:\n", + enumerate(source_names, quote_fun = squote) + ) + ) + check_type <- assert_character_scalar( check_type, @@ -128,19 +144,45 @@ derive_extreme_event <- function(dataset, # Create new observations ## Create a dataset (selected_records) from `events` - condition_ls <- map(events, "condition") - set_values_to_ls <- map(events, "set_values_to") - event_order <- map(seq_len(length(events)), function(x) x) + event_order <- as.list(seq_along(events)) tmp_event_no <- get_new_tmp_var(dataset, prefix = "tmp_event_no") - selected_records_ls <- pmap( - list(condition_ls, set_values_to_ls, event_order), - function(x, y, z) { - dataset %>% - group_by(!!!by_vars) %>% - filter(!!enexpr(x)) %>% - mutate(!!!y, !!tmp_event_no := z) %>% - ungroup() + selected_records_ls <- map2( + events, + event_order, + function(event, index) { + if (is.null(event$dataset_name)) { + data_source <- dataset + } else { + data_source <- source_datasets[[event$dataset_name]] + } + if (inherits(event, "event")) { + data_events <- dataset %>% + group_by(!!!by_vars) %>% + filter_if(event$condition) %>% + ungroup() + if (!is.null(event$mode)) { + data_events <- filter_extreme( + data_events, + by_vars = by_vars, + order = event$order, + mode = event$mode + ) + } + } else { + data_events <- filter_joined( + dataset, + by_vars = by_vars, + join_vars = event$join_vars, + join_type = event$join_type, + first_cond = event$first_cond, + order = event$order, + filter = event$condition + ) + } + data_events %>% + mutate(!!tmp_event_no := index) %>% + process_set_values_to(set_values_to = event$set_values_to) } ) selected_records <- bind_rows(selected_records_ls) @@ -180,32 +222,159 @@ derive_extreme_event <- function(dataset, #' The `event` object is used to define events as input for the #' `derive_extreme_event()` function. #' +#' @param dataset_name Dataset name of the dataset to be used as input for the +#' event. The name refers to the dataset specified for `source_datasets` in +#' `derive_extreme_event()`. If the argument is not specified, the input +#' dataset (`dataset`) of `derive_extreme_event()` is used. +#' #' @param condition An unquoted condition for selecting the observations, which -#' will contribute to the extreme event. +#' will contribute to the extreme event. If the condition contains summary +#' functions like `all()`, they are evaluated for each by group separately. #' -#' @param set_values_to A named list returned by `exprs()` defining the variables -#' to be set for the extreme answer, e.g. `exprs(PARAMCD = "WSP", -#' PARAM = "Worst Sleeping Problems"`. The values must be a symbol, a -#' character string, a numeric value, or `NA`. +#' *Permitted Values*: an unquoted condition +#' +#' @param mode If specified, the first or last observation with respect to `order` is +#' selected for each by group. #' +#' *Permitted Values*: `"first"`, `"last"`, `NULL` +#' +#' @param order The specified variables or expressions are used to select the +#' first or last observation if `mode` is specified. +#' +#' *Permitted Values*: list of expressions created by `exprs()`, e.g., +#' `exprs(ADT, desc(AVAL))` or `NULL` +#' +#' @param set_values_to A named list returned by `exprs()` defining the variables +#' to be set for the event, e.g. `exprs(PARAMCD = "WSP", +#' PARAM = "Worst Sleeping Problems")`. The values can be a symbol, a +#' character string, a numeric value, `NA` or an expression. #' #' @keywords source_specifications #' @family source_specifications #' -#' @seealso [derive_extreme_event()] +#' @seealso [derive_extreme_event()], [event_joined()] #' #' @export #' #' @return An object of class `event` -event <- function(condition, +event <- function(dataset_name = NULL, + condition = NULL, + mode = NULL, + order = NULL, + set_values_to = NULL) { + out <- list( + dataset_name = assert_character_scalar(dataset_name, optional = TRUE), + condition = assert_filter_cond(enexpr(condition), optional = TRUE), + mode = assert_character_scalar( + mode, + values = c("first", "last"), + case_sensitive = FALSE, + optional = TRUE + ), + order = assert_expr_list(order, optional = TRUE), + set_values_to = assert_expr_list( + set_values_to, + named = TRUE, + optional = TRUE + ) + ) + class(out) <- c("event", "event_def", "source", "list") + out +} + +#' Create a `event_joined` Object +#' +#' @description +#' +#' The `event_joined` object is used to define events as input for the +#' `derive_extreme_event()` function. This object should be used if the event +#' does not depend on a single observation of the source dataset but on multiple +#' observations. For example, if the event needs to be confirmed by a second +#' observation of the source dataset. +#' +#' The events are selected by calling `filter_joined()`. See its documentation +#' for more details. +#' +#' @param dataset_name Dataset name of the dataset to be used as input for the +#' event. The name refers to the dataset specified for `source_datasets` in +#' `derive_extreme_event()`. If the argument is not specified, the input +#' dataset (`dataset`) of `derive_extreme_event()` is used. +#' +#' @param condition An unquoted condition for selecting the observations, which +#' will contribute to the extreme event. +#' +#' *Permitted Values*: an unquoted condition +#' +#' @param join_vars Variables to keep from joined dataset +#' +#' The variables needed from the other observations should be specified for +#' this parameter. The specified variables are added to the joined dataset +#' with suffix ".join". For example to select all observations with `AVALC == +#' "Y"` and `AVALC == "Y"` for at least one subsequent visit `join_vars = +#' exprs(AVALC, AVISITN)` and `filter = AVALC == "Y" & AVALC.join == "Y" & +#' AVISITN < AVISITN.join` could be specified. +#' +#' The `*.join` variables are not included in the output dataset. +#' +#' @param join_type Observations to keep after joining +#' +#' The argument determines which of the joined observations are kept with +#' respect to the original observation. For example, if `join_type = +#' "after"` is specified all observations after the original observations are +#' kept. +#' +#' *Permitted Values:* `"before"`, `"after"`, `"all"` +#' +#' @param first_cond Condition for selecting range of data +#' +#' If this argument is specified, the other observations are restricted up to +#' the first observation where the specified condition is fulfilled. If the +#' condition is not fulfilled for any of the subsequent observations, all +#' observations are removed. +#' +#' @param order If specified, the specified variables or expressions are used to +#' select the first observation. +#' +#' *Permitted Values*: list of expressions created by `exprs()`, e.g., +#' `exprs(ADT, desc(AVAL))` or `NULL` +#' +#' @param set_values_to A named list returned by `exprs()` defining the variables +#' to be set for the event, e.g. `exprs(PARAMCD = "WSP", +#' PARAM = "Worst Sleeping Problems")`. The values can be a symbol, a +#' character string, a numeric value, `NA` or an expression. +#' +#' @keywords source_specifications +#' @family source_specifications +#' +#' @seealso [derive_extreme_event()], [event()] +#' +#' @export +#' +#' @return An object of class `event_joined` +event_joined <- function(dataset_name = NULL, + condition, + order = NULL, + join_vars, + join_type, + first_cond = NULL, set_values_to = NULL) { out <- list( + dataset_name = assert_character_scalar(dataset_name, optional = TRUE), condition = assert_filter_cond(enexpr(condition), optional = TRUE), - set_values_to = assert_varval_list( + order = assert_expr_list(order, optional = TRUE), + join_vars = assert_vars(join_vars), + join_type = assert_character_scalar( + join_type, + values = c("before", "after", "all"), + case_sensitive = FALSE + ), + first_cond = assert_filter_cond(enexpr(first_cond), optional = TRUE), + set_values_to = assert_expr_list( set_values_to, + named = TRUE, optional = TRUE ) ) - class(out) <- c("event", "source", "list") + class(out) <- c("event_joined", "event_def", "source", "list") out } diff --git a/man/basket_select.Rd b/man/basket_select.Rd index 2eb207dcd4..82ca48a8d1 100644 --- a/man/basket_select.Rd +++ b/man/basket_select.Rd @@ -39,6 +39,7 @@ Source Objects: \code{\link{date_source}()}, \code{\link{death_event}}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{event}()}, \code{\link{query}()}, diff --git a/man/censor_source.Rd b/man/censor_source.Rd index b2c3d5f31b..e3c83c606e 100644 --- a/man/censor_source.Rd +++ b/man/censor_source.Rd @@ -67,6 +67,7 @@ Source Objects: \code{\link{date_source}()}, \code{\link{death_event}}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{event}()}, \code{\link{query}()}, diff --git a/man/date_source.Rd b/man/date_source.Rd index e9fd8e1a52..1f2051cf3b 100644 --- a/man/date_source.Rd +++ b/man/date_source.Rd @@ -59,6 +59,7 @@ Source Objects: \code{\link{censor_source}()}, \code{\link{death_event}}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{event}()}, \code{\link{query}()}, diff --git a/man/dthcaus_source.Rd b/man/dthcaus_source.Rd index 55c9e4c83e..7c1b78fbe8 100644 --- a/man/dthcaus_source.Rd +++ b/man/dthcaus_source.Rd @@ -84,6 +84,7 @@ Source Objects: \code{\link{censor_source}()}, \code{\link{date_source}()}, \code{\link{death_event}}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{event}()}, \code{\link{query}()}, diff --git a/man/event.Rd b/man/event.Rd index 39573f0a2e..5bd52718ff 100644 --- a/man/event.Rd +++ b/man/event.Rd @@ -4,15 +4,39 @@ \alias{event} \title{Create a \code{event} Object} \usage{ -event(condition, set_values_to = NULL) +event( + dataset_name = NULL, + condition, + mode = NULL, + order = NULL, + set_values_to = NULL +) } \arguments{ +\item{dataset_name}{Dataset name of the dataset to be used as input for the +event. The name refers to the dataset specified for \code{source_datasets} in +\code{derive_extreme_event()}. If the argument is not specified, the input +dataset (\code{dataset}) of \code{derive_extreme_event()} is used.} + \item{condition}{An unquoted condition for selecting the observations, which -will contribute to the extreme event.} +will contribute to the extreme event. + +\emph{Permitted Values}: an unquoted condition} + +\item{mode}{If specified, the first or last observation with respect to \code{order} is +selected for each by group. + +\emph{Permitted Values}: \code{"first"}, \code{"last"}, \code{NULL}} + +\item{order}{The specified variable or expression are used to select the +first or last observation if \code{mode} is specified. + +\emph{Permitted Values}: list of expressions created by \code{exprs()}, e.g., +\code{exprs(ADT, desc(AVAL))} or \code{NULL}} \item{set_values_to}{A named list returned by \code{exprs()} defining the variables -to be set for the extreme answer, e.g. \verb{exprs(PARAMCD = "WSP", PARAM = "Worst Sleeping Problems"}. The values must be a symbol, a -character string, a numeric value, or \code{NA}.} +to be set for the event, e.g. \code{exprs(PARAMCD = "WSP", PARAM = "Worst Sleeping Problems")}. The values can be a symbol, a +character string, a numeric value, \code{NA} or an expression.} } \value{ An object of class \code{event} @@ -22,7 +46,7 @@ The \code{event} object is used to define events as input for the \code{derive_extreme_event()} function. } \seealso{ -\code{\link[=derive_extreme_event]{derive_extreme_event()}} +\code{\link[=derive_extreme_event]{derive_extreme_event()}}, \code{\link[=event_joined]{event_joined()}} Source Objects: \code{\link{basket_select}()}, @@ -30,6 +54,7 @@ Source Objects: \code{\link{date_source}()}, \code{\link{death_event}}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{query}()}, \code{\link{records_source}()}, diff --git a/man/event_joined.Rd b/man/event_joined.Rd new file mode 100644 index 0000000000..972541904f --- /dev/null +++ b/man/event_joined.Rd @@ -0,0 +1,96 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_extreme_event.R +\name{event_joined} +\alias{event_joined} +\title{Create a \code{event_joined} Object} +\usage{ +event_joined( + dataset_name = NULL, + condition, + mode = NULL, + order = NULL, + join_vars, + join_type, + first_cond = NULL, + set_values_to = NULL +) +} +\arguments{ +\item{dataset_name}{Dataset name of the dataset to be used as input for the +event. The name refers to the dataset specified for \code{source_datasets} in +\code{derive_extreme_event()}. If the argument is not specified, the input +dataset (\code{dataset}) of \code{derive_extreme_event()} is used.} + +\item{condition}{An unquoted condition for selecting the observations, which +will contribute to the extreme event. + +\emph{Permitted Values}: an unquoted condition} + +\item{mode}{If specified, the first or last observation with respect to \code{order} is +selected for each by group. + +\emph{Permitted Values}: \code{"first"}, \code{"last"}, \code{NULL}} + +\item{order}{The specified variable or expression are used to select the +first or last observation if \code{mode} is specified. + +\emph{Permitted Values}: list of expressions created by \code{exprs()}, e.g., +\code{exprs(ADT, desc(AVAL))} or \code{NULL}} + +\item{join_vars}{Variables to keep from joined dataset + +The variables needed from the other observations should be specified for +this parameter. The specified variables are added to the joined dataset +with suffix ".join". For example to select all observations with \code{AVALC == "Y"} and \code{AVALC == "Y"} for at least one subsequent visit \code{join_vars = exprs(AVALC, AVISITN)} and \code{filter = AVALC == "Y" & AVALC.join == "Y" & AVISITN < AVISITN.join} could be specified. + +The \verb{*.join} variables are not included in the output dataset.} + +\item{join_type}{Observations to keep after joining + +The argument determines which of the joined observations are kept with +respect to the original observation. For example, if \code{join_type = "after"} is specified all observations after the original observations are +kept. + +\emph{Permitted Values:} \code{"before"}, \code{"after"}, \code{"all"}} + +\item{first_cond}{Condition for selecting range of data + +If this argument is specified, the other observations are restricted up to +the first observation where the specified condition is fulfilled. If the +condition is not fulfilled for any of the subsequent observations, all +observations are removed.} + +\item{set_values_to}{A named list returned by \code{exprs()} defining the variables +to be set for the event, e.g. \code{exprs(PARAMCD = "WSP", PARAM = "Worst Sleeping Problems")}. The values can be a symbol, a +character string, a numeric value, \code{NA} or an expression.} +} +\value{ +An object of class \code{event_joined} +} +\description{ +The \code{event_joined} object is used to define events as input for the +\code{derive_extreme_event()} function. This object should be used if the event +does not depend on a single observation of the source dataset but on multiple +observations. For example, if the event needs to be confirmed by a second +observation of the source dataset. + +The events are selected by calling \code{filter_joined()}. See its documentation +for more details. +} +\seealso{ +\code{\link[=derive_extreme_event]{derive_extreme_event()}}, \code{\link[=event]{event()}} + +Source Objects: +\code{\link{basket_select}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{dthcaus_source}()}, +\code{\link{event_source}()}, +\code{\link{event}()}, +\code{\link{query}()}, +\code{\link{records_source}()}, +\code{\link{tte_source}()} +} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/event_source.Rd b/man/event_source.Rd index ba9d6cb3fb..dc27054758 100644 --- a/man/event_source.Rd +++ b/man/event_source.Rd @@ -58,6 +58,7 @@ Source Objects: \code{\link{date_source}()}, \code{\link{death_event}}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event}()}, \code{\link{query}()}, \code{\link{records_source}()}, diff --git a/man/query.Rd b/man/query.Rd index 0478406e01..3efe0bfdaf 100644 --- a/man/query.Rd +++ b/man/query.Rd @@ -140,6 +140,7 @@ Source Objects: \code{\link{date_source}()}, \code{\link{death_event}}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{event}()}, \code{\link{records_source}()}, diff --git a/man/records_source.Rd b/man/records_source.Rd index f0d011aa39..ec424bd775 100644 --- a/man/records_source.Rd +++ b/man/records_source.Rd @@ -46,6 +46,7 @@ Source Objects: \code{\link{date_source}()}, \code{\link{death_event}}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{event}()}, \code{\link{query}()}, diff --git a/man/tte_source.Rd b/man/tte_source.Rd index 5051d0de38..780a49b172 100644 --- a/man/tte_source.Rd +++ b/man/tte_source.Rd @@ -46,6 +46,7 @@ Source Objects: \code{\link{date_source}()}, \code{\link{death_event}}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{event}()}, \code{\link{query}()}, diff --git a/man/tte_source_objects.Rd b/man/tte_source_objects.Rd index 9608eaab54..9570867e2c 100644 --- a/man/tte_source_objects.Rd +++ b/man/tte_source_objects.Rd @@ -65,6 +65,7 @@ Source Objects: \code{\link{censor_source}()}, \code{\link{date_source}()}, \code{\link{dthcaus_source}()}, +\code{\link{event_joined}()}, \code{\link{event_source}()}, \code{\link{event}()}, \code{\link{query}()}, diff --git a/tests/testthat/test-derive_extreme_event.R b/tests/testthat/test-derive_extreme_event.R index 8905d88e17..5f33d8b5cb 100644 --- a/tests/testthat/test-derive_extreme_event.R +++ b/tests/testthat/test-derive_extreme_event.R @@ -130,3 +130,115 @@ test_that("derive_extreme_records Test 2: `mode` = last", { keys = c("USUBJID", "PARAMCD", "ADY") ) }) + +## Test 3: `source_datasets` works ---- +test_that("derive_extreme_records Test 3: `source_datasets` works", { + adsl <- tibble::tribble( + ~USUBJID, ~TRTSDTC, ~CHECKKEPTCOL, ~CHECKNOTKEPTCOL, + "1", "2020-01-01", "001", "991", + "2", "2019-12-12", "002", "992", + "3", "2019-11-11", "003", "993", + "4", "2019-12-30", "004", "994", + "5", "2020-01-01", "005", "995", + "6", "2020-02-02", "006", "996", + "7", "2020-02-02", "007", "997", + "8", "2020-04-01", "008", "999" + ) %>% + mutate( + TRTSDT = lubridate::ymd(TRTSDTC), + STUDYID = "XX1234" + ) + + adrs <- tibble::tribble( + ~USUBJID, ~ADTC, ~AVALC, ~CHECKKEPTCOL, + "1", "2020-01-01", "PR", "001", + "1", "2020-02-01", "CR", "001", + "1", "2020-02-16", "NE", "001", + "1", "2020-03-01", "CR", "001", + "1", "2020-04-01", "SD", "001", + "2", "2020-01-01", "SD", "002", + "2", "2020-02-01", "PR", "002", + "2", "2020-03-01", "SD", "002", + "2", "2020-03-13", "CR", "002", + "3", "2019-11-12", "CR", "003", + "3", "2019-12-02", "CR", "003", + "3", "2020-01-01", "SD", "003", + "4", "2020-01-01", "PR", "004", + "4", "2020-03-01", "SD", "004", + "4", "2020-04-01", "SD", "004", + "4", "2020-05-01", "PR", "004", + "4", "2020-05-15", "NON-CR/NON-PD", "004", + "5", "2020-01-01", "PR", "005", + "5", "2020-01-10", "SD", "005", + "5", "2020-01-20", "PR", "005", + "5", "2020-05-15", "NON-CR/NON-PD", "005", + "6", "2020-02-06", "PR", "006", + "6", "2020-02-16", "CR", "006", + "6", "2020-03-30", "PR", "006", + "7", "2020-02-06", "PR", "007", + "7", "2020-02-16", "CR", "007", + "7", "2020-04-01", "NE", "007" + ) %>% + mutate( + PARAMCD = "OVR", + ADT = lubridate::ymd(ADTC), + STUDYID = "XX1234" + ) %>% + select(-ADTC) %>% + derive_vars_merged( + dataset_add = adsl, + by_vars = exprs(STUDYID, USUBJID), + new_vars = exprs(TRTSDT) + ) + expected_01 <- bind_rows( + adrs, + tibble::tribble( + ~USUBJID, ~ADTC, ~AVALC, ~AVAL, ~TRTSDTC, ~CHECKKEPTCOL, + "1", "2020-02-01", "CR", 11, "2020-01-01", "001", + "2", "2020-03-13", "CR", 11, "2019-12-12", "002", + "3", "2019-11-12", "CR", 11, "2019-11-11", "003", + "4", "2020-01-01", "PR", 22, "2019-12-30", "004", + "5", "2020-01-01", "PR", 22, "2020-01-01", "005", + "6", "2020-02-16", "CR", 11, "2020-02-02", "006", + "7", "2020-02-16", "CR", 11, "2020-02-02", "007", + "8", "", "MISSING", 77, "2020-04-01", "008" + ) %>% + mutate( + ADT = lubridate::ymd(ADTC), + TRTSDT = lubridate::ymd(TRTSDTC), + STUDYID = "XX1234", + PARAMCD = "BOR", + PARAM = "Best Overall Response" + ) %>% + select(-ADTC, -TRTSDTC) + ) + + actual_01 <- derive_extreme_event( + dataset = adrs, + by_vars = exprs(STUDYID, USUBJID), + order = exprs(ADT), + mode = "first", + filter_source = PARAMCD == "OVR", + source_datasets = list(adsl = adsl), + events = list( + event( + condition = AVALC == "CR", + set_values_to = exprs( + AVALC = "CR" + ) ) + ), + reference_date = TRTSDT, + ref_start_window = 28, + set_values_to = exprs( + AVAL = {{ aval_fun_pass }}(AVALC), + PARAMCD = "BOR", + PARAM = "Best Overall Response" + ) + ) + + expect_dfs_equal( + base = expected_01, + compare = actual_01, + keys = c("USUBJID", "PARAMCD", "ADT") + ) +}) From 92eead90968c49231ff853fb303c73b34ead259a Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Thu, 13 Jul 2023 18:44:21 +0200 Subject: [PATCH 02/22] #1960 enhance_derive_extreme_event: continue draft --- R/derive_extreme_event.R | 25 ++++++++++++---------- tests/testthat/test-derive_extreme_event.R | 8 +++---- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 4539a6a6b3..5a854e8a28 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -121,18 +121,21 @@ derive_extreme_event <- function(dataset, mode <- assert_character_scalar(mode, values = c("first", "last"), case_sensitive = FALSE) assert_list_of(source_datasets, "data.frame") source_names <- names(source_datasets) - assert_list_element( - list = events, - element = "dataset_name", - condition = is.null(dataset_name) || dataset_name %in% source_names, - source_names = source_names, - message_text = paste0( - "The dataset names must be included in the list specified for the ", - "`source_datasets` parameter.\n", - "Following names were provided by `source_datasets`:\n", - enumerate(source_names, quote_fun = squote) + events_to_check <- events[map_lgl(events, ~ !is.null(.x$dataset_name))] + if (length(events_to_check) > 0) { + assert_list_element( + list = events_to_check, + element = "dataset_name", + condition = dataset_name %in% source_names, + source_names = source_names, + message_text = paste0( + "The dataset names must be included in the list specified for the ", + "`source_datasets` parameter.\n", + "Following names were provided by `source_datasets`:\n", + enumerate(source_names, quote_fun = squote) + ) ) - ) + } check_type <- assert_character_scalar( diff --git a/tests/testthat/test-derive_extreme_event.R b/tests/testthat/test-derive_extreme_event.R index 5f33d8b5cb..e3d64877c1 100644 --- a/tests/testthat/test-derive_extreme_event.R +++ b/tests/testthat/test-derive_extreme_event.R @@ -190,7 +190,7 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { by_vars = exprs(STUDYID, USUBJID), new_vars = exprs(TRTSDT) ) - expected_01 <- bind_rows( + expected <- bind_rows( adrs, tibble::tribble( ~USUBJID, ~ADTC, ~AVALC, ~AVAL, ~TRTSDTC, ~CHECKKEPTCOL, @@ -213,7 +213,7 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { select(-ADTC, -TRTSDTC) ) - actual_01 <- derive_extreme_event( + actual <- derive_extreme_event( dataset = adrs, by_vars = exprs(STUDYID, USUBJID), order = exprs(ADT), @@ -237,8 +237,8 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { ) expect_dfs_equal( - base = expected_01, - compare = actual_01, + base = expected, + compare = actual, keys = c("USUBJID", "PARAMCD", "ADT") ) }) From ce03b2f2cdbdc25509a5f39639f6451b3fdc1c93 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Mon, 17 Jul 2023 18:03:34 +0200 Subject: [PATCH 03/22] #1960 enhance_derive_extreme_event: continue draft --- R/derive_extreme_event.R | 28 +++-- tests/testthat/test-derive_extreme_event.R | 137 +++++++++++++-------- 2 files changed, 106 insertions(+), 59 deletions(-) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 5a854e8a28..78698d2df6 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -109,7 +109,8 @@ derive_extreme_event <- function(dataset, mode, source_datasets = NULL, check_type = "warning", - set_values_to) { + set_values_to, + keep_vars_source = exprs(everything())) { # Check input parameters assert_vars(by_vars, optional = TRUE) assert_list_of(events, "event_def") @@ -144,6 +145,7 @@ derive_extreme_event <- function(dataset, case_sensitive = FALSE ) assert_varval_list(set_values_to) + keep_vars_source <- assert_expr_list(keep_vars_source) # Create new observations ## Create a dataset (selected_records) from `events` @@ -160,13 +162,13 @@ derive_extreme_event <- function(dataset, data_source <- source_datasets[[event$dataset_name]] } if (inherits(event, "event")) { - data_events <- dataset %>% + data_events <- data_source %>% group_by(!!!by_vars) %>% filter_if(event$condition) %>% ungroup() if (!is.null(event$mode)) { data_events <- filter_extreme( - data_events, + data_source, by_vars = by_vars, order = event$order, mode = event$mode @@ -183,9 +185,15 @@ derive_extreme_event <- function(dataset, filter = event$condition ) } + if (is.null(event$keep_vars_source)) { + event_keep_vars_source <- keep_vars_source + } else { + event_keep_vars_source <- event$keep_vars_source + } data_events %>% mutate(!!tmp_event_no := index) %>% - process_set_values_to(set_values_to = event$set_values_to) + process_set_values_to(set_values_to = event$set_values_to) %>% + select(names(set_values_to), !!!event_keep_vars_source) } ) selected_records <- bind_rows(selected_records_ls) @@ -264,7 +272,8 @@ event <- function(dataset_name = NULL, condition = NULL, mode = NULL, order = NULL, - set_values_to = NULL) { + set_values_to = NULL, + keep_vars_source = NULL) { out <- list( dataset_name = assert_character_scalar(dataset_name, optional = TRUE), condition = assert_filter_cond(enexpr(condition), optional = TRUE), @@ -279,7 +288,8 @@ event <- function(dataset_name = NULL, set_values_to, named = TRUE, optional = TRUE - ) + ), + keep_vars_source = assert_expr_list(keep_vars_source, optional = TRUE) ) class(out) <- c("event", "event_def", "source", "list") out @@ -360,7 +370,8 @@ event_joined <- function(dataset_name = NULL, join_vars, join_type, first_cond = NULL, - set_values_to = NULL) { + set_values_to = NULL, + keep_vars_source = NULL) { out <- list( dataset_name = assert_character_scalar(dataset_name, optional = TRUE), condition = assert_filter_cond(enexpr(condition), optional = TRUE), @@ -376,7 +387,8 @@ event_joined <- function(dataset_name = NULL, set_values_to, named = TRUE, optional = TRUE - ) + ), + keep_vars_source = assert_expr_list(keep_vars_source, optional = TRUE) ) class(out) <- c("event_joined", "event_def", "source", "list") out diff --git a/tests/testthat/test-derive_extreme_event.R b/tests/testthat/test-derive_extreme_event.R index e3d64877c1..fdf7b656dd 100644 --- a/tests/testthat/test-derive_extreme_event.R +++ b/tests/testthat/test-derive_extreme_event.R @@ -134,15 +134,15 @@ test_that("derive_extreme_records Test 2: `mode` = last", { ## Test 3: `source_datasets` works ---- test_that("derive_extreme_records Test 3: `source_datasets` works", { adsl <- tibble::tribble( - ~USUBJID, ~TRTSDTC, ~CHECKKEPTCOL, ~CHECKNOTKEPTCOL, - "1", "2020-01-01", "001", "991", - "2", "2019-12-12", "002", "992", - "3", "2019-11-11", "003", "993", - "4", "2019-12-30", "004", "994", - "5", "2020-01-01", "005", "995", - "6", "2020-02-02", "006", "996", - "7", "2020-02-02", "007", "997", - "8", "2020-04-01", "008", "999" + ~USUBJID, ~TRTSDTC, + "1", "2020-01-01", + "2", "2019-12-12", + "3", "2019-11-11", + "4", "2019-12-30", + "5", "2020-01-01", + "6", "2020-02-02", + "7", "2020-02-02", + "8", "2020-04-01" ) %>% mutate( TRTSDT = lubridate::ymd(TRTSDTC), @@ -150,34 +150,34 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { ) adrs <- tibble::tribble( - ~USUBJID, ~ADTC, ~AVALC, ~CHECKKEPTCOL, - "1", "2020-01-01", "PR", "001", - "1", "2020-02-01", "CR", "001", - "1", "2020-02-16", "NE", "001", - "1", "2020-03-01", "CR", "001", - "1", "2020-04-01", "SD", "001", - "2", "2020-01-01", "SD", "002", - "2", "2020-02-01", "PR", "002", - "2", "2020-03-01", "SD", "002", - "2", "2020-03-13", "CR", "002", - "3", "2019-11-12", "CR", "003", - "3", "2019-12-02", "CR", "003", - "3", "2020-01-01", "SD", "003", - "4", "2020-01-01", "PR", "004", - "4", "2020-03-01", "SD", "004", - "4", "2020-04-01", "SD", "004", - "4", "2020-05-01", "PR", "004", - "4", "2020-05-15", "NON-CR/NON-PD", "004", - "5", "2020-01-01", "PR", "005", - "5", "2020-01-10", "SD", "005", - "5", "2020-01-20", "PR", "005", - "5", "2020-05-15", "NON-CR/NON-PD", "005", - "6", "2020-02-06", "PR", "006", - "6", "2020-02-16", "CR", "006", - "6", "2020-03-30", "PR", "006", - "7", "2020-02-06", "PR", "007", - "7", "2020-02-16", "CR", "007", - "7", "2020-04-01", "NE", "007" + ~USUBJID, ~ADTC, ~AVALC, + "1", "2020-01-01", "PR", + "1", "2020-02-01", "CR", + "1", "2020-02-16", "NE", + "1", "2020-03-01", "CR", + "1", "2020-04-01", "SD", + "2", "2020-01-01", "SD", + "2", "2020-02-01", "PR", + "2", "2020-03-01", "SD", + "2", "2020-03-13", "CR", + "3", "2019-11-12", "CR", + "3", "2019-12-02", "CR", + "3", "2020-01-01", "SD", + "4", "2020-01-01", "PR", + "4", "2020-03-01", "SD", + "4", "2020-04-01", "SD", + "4", "2020-05-01", "PR", + "4", "2020-05-15", "NON-CR/NON-PD", + "5", "2020-01-01", "PR", + "5", "2020-01-10", "SD", + "5", "2020-01-20", "PR", + "5", "2020-05-15", "NON-CR/NON-PD", + "6", "2020-02-06", "PR", + "6", "2020-02-16", "CR", + "6", "2020-03-30", "PR", + "7", "2020-02-06", "PR", + "7", "2020-02-16", "CR", + "7", "2020-04-01", "NE" ) %>% mutate( PARAMCD = "OVR", @@ -193,15 +193,15 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { expected <- bind_rows( adrs, tibble::tribble( - ~USUBJID, ~ADTC, ~AVALC, ~AVAL, ~TRTSDTC, ~CHECKKEPTCOL, - "1", "2020-02-01", "CR", 11, "2020-01-01", "001", - "2", "2020-03-13", "CR", 11, "2019-12-12", "002", - "3", "2019-11-12", "CR", 11, "2019-11-11", "003", - "4", "2020-01-01", "PR", 22, "2019-12-30", "004", - "5", "2020-01-01", "PR", 22, "2020-01-01", "005", - "6", "2020-02-16", "CR", 11, "2020-02-02", "006", - "7", "2020-02-16", "CR", 11, "2020-02-02", "007", - "8", "", "MISSING", 77, "2020-04-01", "008" + ~USUBJID, ~ADTC, ~AVALC, ~TRTSDTC, + "1", "2020-02-01", "CR", "2020-01-01", + "2", "2020-03-13", "CR", "2019-12-12", + "3", "2019-11-12", "CR", "2019-11-11", + "4", "2020-01-01", "PR", "2019-12-30", + "5", "2020-01-01", "PR", "2020-01-01", + "6", "2020-02-16", "CR", "2020-02-02", + "7", "2020-02-16", "CR", "2020-02-02", + "8", "", "MISSING", "2020-04-01" ) %>% mutate( ADT = lubridate::ymd(ADTC), @@ -218,19 +218,54 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { by_vars = exprs(STUDYID, USUBJID), order = exprs(ADT), mode = "first", - filter_source = PARAMCD == "OVR", source_datasets = list(adsl = adsl), events = list( event( condition = AVALC == "CR", set_values_to = exprs( AVALC = "CR" - ) ) + ) + ), + event( + condition = AVALC == "PR", + set_values_to = exprs( + AVALC = "PR" + ) + ), + event( + condition = AVALC == "SD" & ADT >= TRTSDT + 28, + set_values_to = exprs( + AVALC = "CR" + ) + ), + event( + condition = AVALC == "NON-CR/NON-PD" & ADT >= TRTSDT + 28, + set_values_to = exprs( + AVALC = "CR" + ) + ), + event( + condition = AVALC == "PD", + set_values_to = exprs( + AVALC = "PD" + ) + ), + event( + condition = AVALC %in% c("SD", "NON-CR/NON-PD"), + set_values_to = exprs( + AVALC = "NE" + ) + ), + event( + dataset_name = "adsl", + condition = TRUE, + set_values_to = exprs( + AVALC = "MISSING" + ), + keep_vars_source = exprs() + ) ), - reference_date = TRTSDT, - ref_start_window = 28, set_values_to = exprs( - AVAL = {{ aval_fun_pass }}(AVALC), PARAMCD = "BOR", PARAM = "Best Overall Response" ) From 9f5ac4057968a75ece467a04e28ff4eb22295e08 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Tue, 18 Jul 2023 15:07:15 +0200 Subject: [PATCH 04/22] #1960 enhance_derive_extreme_event: add tests and documentation --- R/derive_extreme_event.R | 83 +++++++--- docs/pkgdown.yml | 6 +- man/derive_extreme_event.Rd | 48 +++++- man/event.Rd | 19 ++- man/event_joined.Rd | 22 ++- tests/testthat/test-derive_extreme_event.R | 182 ++++++++++++++++++++- 6 files changed, 315 insertions(+), 45 deletions(-) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 78698d2df6..9280d00cbe 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -7,6 +7,17 @@ #' where extreme records are derived based on certain order of existing #' variables. #' +#' @param events Conditions and new values defining events +#' +#' A list of `event()` or `event_joined()` objects is expected. Only +#' observations listed in the `events` are considered for deriving extreme +#' event. If multiple records meet the filter `condition`, take the first +#' record sorted by `order`. The data is grouped by `by_vars`, i.e., summary +#' functions like `all()` or `any()` can be used in `condition`. +#' +#' For `event_joined()` events the observations are selected by calling +#' `filter_joined`. The `condition` field is passed to the `filter` argument. +#' #' @param order Sort order #' #' If a particular event from `events` has more than one observation, within @@ -23,20 +34,42 @@ #' #' *Permitted Values:* `"first"`, `"last"` #' -#' @param events Conditions and new values defining events +#' @param source_datasets Source datasets #' -#' A list of `event()` objects is expected. Only observations listed in the -#' `events` are considered for deriving extreme event. If multiple records -#' meet the filter `condition`, take the first record sorted by `order`. +#' A named list of datasets is expected. The `dataset_name` field of `event()` +#' and `event_joined()` refers to the dataset provided in the list. +#' +#' @param keep_vars_source Variables to keep from the source dataset +#' +#' For each event the specified variables are kept from the selected +#' observations. The variables specified for `by_vars` and created by +#' `set_values_to` are always kept. +#' +#' *Permitted Values*: A list of expressions where each element is +#' a symbol or a tidyselect expression, e.g., `exprs(VISIT, VISITNUM, +#' starts_with("RS"))`. #' #' @inheritParams filter_extreme #' @inheritParams derive_summary_records #' #' @details -#' 1. Construct a dataset based on `events`: apply the filter `condition` and -#' `set_values_to` to the input dataset. +#' 1. For each event select the observations to consider: +#' +#' 1. If the event is of class `event`, the observations of the source dataset +#' are restricted by `condition` and then the first or last (`mode`) +#' observation per by group (`by_vars`) is selected. +#' +#' If the event is of class `event_joined`, `filter_joined()` is called to +#' select the observations. +#' +#' 1. The variables specified by the `set_values_to` field of the event +#' are added to the selected observations. +#' 1. Only the variables specified for the `keep_vars_source` field of the +#' event, and the by variables (`by_vars`) and the variables created by +#' `set_values_to` are kept. #' 1. For each group (with respect to the variables specified for the -#' `by_vars` parameter) the first or last observation (with respect to the +#' `by_vars` parameter) the first event is selected. If there is more than one +#' observation per event the first or last observation (with respect to the #' order specified for the `order` parameter and the mode specified for the #' `mode` parameter) is selected. #' 1. The variables specified by the `set_values_to` parameter are added to @@ -149,12 +182,12 @@ derive_extreme_event <- function(dataset, # Create new observations ## Create a dataset (selected_records) from `events` - event_order <- as.list(seq_along(events)) + event_index <- as.list(seq_along(events)) tmp_event_no <- get_new_tmp_var(dataset, prefix = "tmp_event_no") selected_records_ls <- map2( events, - event_order, + event_index, function(event, index) { if (is.null(event$dataset_name)) { data_source <- dataset @@ -175,14 +208,19 @@ derive_extreme_event <- function(dataset, ) } } else { + if (is.null(event$order)) { + event_order <- order + } else { + event_order <- event$order + } data_events <- filter_joined( dataset, by_vars = by_vars, join_vars = event$join_vars, join_type = event$join_type, - first_cond = event$first_cond, - order = event$order, - filter = event$condition + first_cond = !!event$first_cond, + order = event_order, + filter = !!event$condition ) } if (is.null(event$keep_vars_source)) { @@ -193,14 +231,14 @@ derive_extreme_event <- function(dataset, data_events %>% mutate(!!tmp_event_no := index) %>% process_set_values_to(set_values_to = event$set_values_to) %>% - select(names(set_values_to), !!!event_keep_vars_source) + select(!!!event_keep_vars_source, !!!by_vars, names(event$set_values_to)) } ) selected_records <- bind_rows(selected_records_ls) ## tmp obs number within by_vars and a type of event tmp_obs <- get_new_tmp_var(selected_records) - selected_records_obs <- selected_records %>% + selected_records <- selected_records %>% derive_var_obs_number( new_var = !!tmp_obs, order = order, @@ -214,7 +252,7 @@ derive_extreme_event <- function(dataset, } else { tmp_obs_expr <- expr(desc(!!tmp_obs)) } - new_obs <- selected_records_obs %>% + new_obs <- selected_records %>% filter_extreme( by_vars = by_vars, order = expr_c(expr(!!tmp_event_no), tmp_obs_expr), @@ -260,6 +298,16 @@ derive_extreme_event <- function(dataset, #' PARAM = "Worst Sleeping Problems")`. The values can be a symbol, a #' character string, a numeric value, `NA` or an expression. #' +#' @param keep_vars_source Variables to keep from the source dataset +#' +#' The specified variables are kept for the selected observations. The +#' variables specified for `by_vars` (of `derive_extreme_event()`) and created +#' by `set_values_to` are always kept. +#' +#' *Permitted Values*: A list of expressions where each element is +#' a symbol or a tidyselect expression, e.g., `exprs(VISIT, VISITNUM, +#' starts_with("RS"))`. +#' #' @keywords source_specifications #' @family source_specifications #' @@ -351,10 +399,7 @@ event <- function(dataset_name = NULL, #' *Permitted Values*: list of expressions created by `exprs()`, e.g., #' `exprs(ADT, desc(AVAL))` or `NULL` #' -#' @param set_values_to A named list returned by `exprs()` defining the variables -#' to be set for the event, e.g. `exprs(PARAMCD = "WSP", -#' PARAM = "Worst Sleeping Problems")`. The values can be a symbol, a -#' character string, a numeric value, `NA` or an expression. +#' @inheritParams event #' #' @keywords source_specifications #' @family source_specifications diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index 8492dafdf7..3de7593245 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -19,8 +19,8 @@ articles: queries_dataset: queries_dataset.html questionnaires: questionnaires.html visits_periods: visits_periods.html -last_built: 2023-05-31T14:16Z +last_built: 2023-07-18T11:09Z urls: - reference: https://pharmaverse.github.io/admiral/cran-release/reference - article: https://pharmaverse.github.io/admiral/cran-release/articles + reference: https://pharmaverse.github.io/admiral/reference + article: https://pharmaverse.github.io/admiral/articles diff --git a/man/derive_extreme_event.Rd b/man/derive_extreme_event.Rd index b2912f1783..124099090f 100644 --- a/man/derive_extreme_event.Rd +++ b/man/derive_extreme_event.Rd @@ -10,8 +10,10 @@ derive_extreme_event( events, order, mode, + source_datasets = NULL, check_type = "warning", - set_values_to + set_values_to, + keep_vars_source = exprs(everything()) ) } \arguments{ @@ -28,9 +30,14 @@ expected.} \item{events}{Conditions and new values defining events -A list of \code{event()} objects is expected. Only observations listed in the -\code{events} are considered for deriving extreme event. If multiple records -meet the filter \code{condition}, take the first record sorted by \code{order}.} +A list of \code{event()} or \code{event_joined()} objects is expected. Only +observations listed in the \code{events} are considered for deriving extreme +event. If multiple records meet the filter \code{condition}, take the first +record sorted by \code{order}. The data is grouped by \code{by_vars}, i.e., summary +functions like \code{all()} or \code{any()} can be used in \code{condition}. + +For \code{event_joined()} events the observations are selected by calling +\code{filter_joined}. The \code{condition} field is passed to the \code{filter} argument.} \item{order}{Sort order @@ -48,6 +55,11 @@ sorting by \code{order}. \emph{Permitted Values:} \code{"first"}, \code{"last"}} +\item{source_datasets}{Source datasets + +A named list of datasets is expected. The \code{dataset_name} field of \code{event()} +and \code{event_joined()} refers to the dataset provided in the list.} + \item{check_type}{Check uniqueness? If \code{"warning"} or \code{"error"} is specified, the specified message is issued @@ -69,6 +81,15 @@ A list of variable name-value pairs is expected. \item RHS refers to the values to set to the variable. This can be a string, a symbol, a numeric value, an expression, or \code{NA}, e.g., \code{exprs(PARAMCD = "TDOSE", PARCAT1 = "OVERALL")}. }} + +\item{keep_vars_source}{Variables to keep from the source dataset + +For each event the specified variables are kept from the selected +observations. The variables specified for \code{by_vars} and created by +\code{set_values_to} are always kept. + +\emph{Permitted Values}: A list of expressions where each element is +a symbol or a tidyselect expression, e.g., \code{exprs(VISIT, VISITNUM, starts_with("RS"))}.} } \value{ The input dataset with the best or worst observation of each by group @@ -84,10 +105,23 @@ variables. } \details{ \enumerate{ -\item Construct a dataset based on \code{events}: apply the filter \code{condition} and -\code{set_values_to} to the input dataset. +\item For each event select the observations to consider: +\enumerate{ +\item If the event is of class \code{event}, the observations of the source dataset +are restricted by \code{condition} and then the first or last (\code{mode}) +observation per by group (\code{by_vars}) is selected. + +If the event is of class \code{event_joined}, \code{filter_joined()} is called to +select the observations. +\item The variables specified by the \code{set_values_to} field of the event +are added to the selected observations. +\item Only the variables specified for the \code{keep_vars_source} field of the +event, and the by variables (\code{by_vars}) and the variables created by +\code{set_values_to} are kept. +} \item For each group (with respect to the variables specified for the -\code{by_vars} parameter) the first or last observation (with respect to the +\code{by_vars} parameter) the first event is selected. If there is more than one +observation per event the first or last observation (with respect to the order specified for the \code{order} parameter and the mode specified for the \code{mode} parameter) is selected. \item The variables specified by the \code{set_values_to} parameter are added to diff --git a/man/event.Rd b/man/event.Rd index 5bd52718ff..292b73becb 100644 --- a/man/event.Rd +++ b/man/event.Rd @@ -6,10 +6,11 @@ \usage{ event( dataset_name = NULL, - condition, + condition = NULL, mode = NULL, order = NULL, - set_values_to = NULL + set_values_to = NULL, + keep_vars_source = NULL ) } \arguments{ @@ -19,7 +20,8 @@ event. The name refers to the dataset specified for \code{source_datasets} in dataset (\code{dataset}) of \code{derive_extreme_event()} is used.} \item{condition}{An unquoted condition for selecting the observations, which -will contribute to the extreme event. +will contribute to the extreme event. If the condition contains summary +functions like \code{all()}, they are evaluated for each by group separately. \emph{Permitted Values}: an unquoted condition} @@ -28,7 +30,7 @@ selected for each by group. \emph{Permitted Values}: \code{"first"}, \code{"last"}, \code{NULL}} -\item{order}{The specified variable or expression are used to select the +\item{order}{The specified variables or expressions are used to select the first or last observation if \code{mode} is specified. \emph{Permitted Values}: list of expressions created by \code{exprs()}, e.g., @@ -37,6 +39,15 @@ first or last observation if \code{mode} is specified. \item{set_values_to}{A named list returned by \code{exprs()} defining the variables to be set for the event, e.g. \code{exprs(PARAMCD = "WSP", PARAM = "Worst Sleeping Problems")}. The values can be a symbol, a character string, a numeric value, \code{NA} or an expression.} + +\item{keep_vars_source}{Variables to keep from the source dataset + +The specified variables are kept for the selected observations. The +variables specified for \code{by_vars} (of \code{derive_extreme_event()}) and created +by \code{set_values_to} are always kept. + +\emph{Permitted Values}: A list of expressions where each element is +a symbol or a tidyselect expression, e.g., \code{exprs(VISIT, VISITNUM, starts_with("RS"))}.} } \value{ An object of class \code{event} diff --git a/man/event_joined.Rd b/man/event_joined.Rd index 972541904f..c54d060bcc 100644 --- a/man/event_joined.Rd +++ b/man/event_joined.Rd @@ -7,12 +7,12 @@ event_joined( dataset_name = NULL, condition, - mode = NULL, order = NULL, join_vars, join_type, first_cond = NULL, - set_values_to = NULL + set_values_to = NULL, + keep_vars_source = NULL ) } \arguments{ @@ -26,13 +26,8 @@ will contribute to the extreme event. \emph{Permitted Values}: an unquoted condition} -\item{mode}{If specified, the first or last observation with respect to \code{order} is -selected for each by group. - -\emph{Permitted Values}: \code{"first"}, \code{"last"}, \code{NULL}} - -\item{order}{The specified variable or expression are used to select the -first or last observation if \code{mode} is specified. +\item{order}{If specified, the specified variables or expressions are used to +select the first observation. \emph{Permitted Values}: list of expressions created by \code{exprs()}, e.g., \code{exprs(ADT, desc(AVAL))} or \code{NULL}} @@ -63,6 +58,15 @@ observations are removed.} \item{set_values_to}{A named list returned by \code{exprs()} defining the variables to be set for the event, e.g. \code{exprs(PARAMCD = "WSP", PARAM = "Worst Sleeping Problems")}. The values can be a symbol, a character string, a numeric value, \code{NA} or an expression.} + +\item{keep_vars_source}{Variables to keep from the source dataset + +The specified variables are kept for the selected observations. The +variables specified for \code{by_vars} (of \code{derive_extreme_event()}) and created +by \code{set_values_to} are always kept. + +\emph{Permitted Values}: A list of expressions where each element is +a symbol or a tidyselect expression, e.g., \code{exprs(VISIT, VISITNUM, starts_with("RS"))}.} } \value{ An object of class \code{event_joined} diff --git a/tests/testthat/test-derive_extreme_event.R b/tests/testthat/test-derive_extreme_event.R index fdf7b656dd..8ac6c28b18 100644 --- a/tests/testthat/test-derive_extreme_event.R +++ b/tests/testthat/test-derive_extreme_event.R @@ -235,13 +235,13 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { event( condition = AVALC == "SD" & ADT >= TRTSDT + 28, set_values_to = exprs( - AVALC = "CR" + AVALC = "SD" ) ), event( condition = AVALC == "NON-CR/NON-PD" & ADT >= TRTSDT + 28, set_values_to = exprs( - AVALC = "CR" + AVALC = "NON-CR/NON-PD" ) ), event( @@ -262,7 +262,7 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { set_values_to = exprs( AVALC = "MISSING" ), - keep_vars_source = exprs() + keep_vars_source = exprs(TRTSDT) ) ), set_values_to = exprs( @@ -277,3 +277,179 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { keys = c("USUBJID", "PARAMCD", "ADT") ) }) + +## Test 4: event_joined() is handled correctly ---- +test_that("derive_extreme_records Test 4: event_joined() is handled correctly", { +adsl <- tibble::tribble( + ~USUBJID, ~TRTSDTC, + "1", "2020-01-01", + "2", "2019-12-12", + "3", "2019-11-11", + "4", "2019-12-30", + "5", "2020-01-01", + "6", "2020-02-02", + "7", "2020-02-02", + "8", "2020-04-01", + "9", "2020-02-01" +) %>% + mutate( + TRTSDT = lubridate::ymd(TRTSDTC), + STUDYID = "XX1234" + ) + +adrs <- tibble::tribble( + ~USUBJID, ~ADTC, ~AVALC, + "1", "2020-01-01", "PR", + "1", "2020-02-01", "CR", + "1", "2020-02-16", "NE", + "1", "2020-03-01", "CR", + "1", "2020-04-01", "SD", + "2", "2020-01-01", "SD", + "2", "2020-02-01", "PR", + "2", "2020-03-01", "SD", + "2", "2020-03-13", "CR", + "3", "2019-11-12", "CR", + "3", "2019-12-02", "CR", + "3", "2020-01-01", "SD", + "4", "2020-01-01", "PR", + "4", "2020-03-01", "SD", + "4", "2020-04-01", "SD", + "4", "2020-05-01", "PR", + "4", "2020-05-15", "NON-CR/NON-PD", + "5", "2020-01-01", "PR", + "5", "2020-01-10", "SD", + "5", "2020-01-20", "PR", + "5", "2020-05-15", "NON-CR/NON-PD", + "6", "2020-02-06", "PR", + "6", "2020-02-16", "CR", + "6", "2020-03-30", "PR", + "7", "2020-02-06", "PR", + "7", "2020-02-16", "CR", + "7", "2020-04-01", "NE", + "9", "2020-02-16", "PD" +) %>% + mutate( + PARAMCD = "OVR", + ADT = lubridate::ymd(ADTC), + STUDYID = "XX1234" + ) %>% + derive_vars_merged( + dataset_add = adsl, + by_vars = exprs(STUDYID, USUBJID), + new_vars = exprs(TRTSDT) + ) + + actual <- + derive_extreme_event( + adrs, + by_vars = exprs(STUDYID, USUBJID), + order = exprs(ADT), + mode = "first", + source_datasets = list(adsl = adsl), + events = list( + event_joined( + join_vars = exprs(AVALC, ADT), + join_type = "after", + first_cond = AVALC.join == "CR" & + ADT.join >= ADT + 28, + condition = AVALC == "CR" & + all(AVALC.join %in% c("CR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1, + set_values_to = exprs( + AVALC = "CR" + ) + ), + event_joined( + join_vars = exprs(AVALC, ADT), + join_type = "after", + first_cond = AVALC.join %in% c("CR", "PR") & + ADT.join >= ADT + 28, + condition = AVALC == "PR" & + all(AVALC.join %in% c("CR", "PR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1 & + ( + min_cond( + var = ADT.join, + cond = AVALC.join == "CR" + ) > max_cond(var = ADT.join, cond = AVALC.join == "PR") | + count_vals(var = AVALC.join, val = "CR") == 0 | + count_vals(var = AVALC.join, val = "PR") == 0 + ), + set_values_to = exprs( + AVALC = "PR" + ) + ), + event( + condition = AVALC %in% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, + set_values_to = exprs( + AVALC = "SD" + ) + ), + event( + condition = AVALC == "NON-CR/NON-PD" & ADT >= TRTSDT + 28, + set_values_to = exprs( + AVALC = "NON-CR/NON-PD" + ) + ), + event( + condition = AVALC == "PD", + set_values_to = exprs( + AVALC = "PD" + ) + ), + event( + condition = AVALC %in% c("CR", "PR", "SD", "NON-CR/NON-PD", "NE"), + set_values_to = exprs( + AVALC = "NE" + ) + ), + event( + dataset_name = "adsl", + condition = TRUE, + set_values_to = exprs( + AVALC = "MISSING" + ), + keep_vars_source = exprs(TRTSDT) + ) + + ), + set_values_to = exprs( + PARAMCD = "CBOR", + PARAM = "Best Confirmed Overall Response by Investigator" + ) + ) + +expected <- bind_rows( + adrs, + tibble::tribble( + ~USUBJID, ~ADTC, ~AVALC, + "1", "2020-02-01", "CR", + "2", "2020-02-01", "SD", + "3", "2020-01-01", "SD", + "4", "2020-03-01", "SD", + "5", "2020-05-15", "NON-CR/NON-PD", + "6", "2020-03-30", "SD", + "7", "2020-02-06", "NE", + "8", NA_character_, "MISSING", + "9", "2020-02-16", "PD" + ) %>% + mutate( + ADT = lubridate::ymd(ADTC), + STUDYID = "XX1234", + PARAMCD = "CBOR", + PARAM = "Best Confirmed Overall Response by Investigator" + ) %>% + derive_vars_merged( + dataset_add = adsl, + by_vars = exprs(STUDYID, USUBJID), + new_vars = exprs(TRTSDT) + ) +) + +expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "PARAMCD", "ADT") +) + +}) From e3d009b0ad82a38942727e5ef103c7d0a3af84ea Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Tue, 18 Jul 2023 15:21:12 +0200 Subject: [PATCH 05/22] #1960 enhance_derive_extreme_event: update NEWS --- NEWS.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/NEWS.md b/NEWS.md index 788b98978f..d6394a2bc9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,24 @@ ## Updates of Existing Functions - The functions `derive_param_bmi()` and `derive_param_bsa()` are updated to have the option of producing more values at visits when only weight is collected (#1228). +- `derive_extreme_event()` was enhanced (#1960): + + - `event_joined()` events can be specified for the `events` argument. This + allows to define events based on more than one observation, e.g., events + which need to be confirmed by a second assessment. + + - The `source_datasets` argument was added to the function and the + `dataset_name` field to `event()`. It can be used to define events based on + a different dataset than the input dataset. + + - The `keep_vars_source` argument was added to the function and the + `keep_vars_source` field to `event()`. It allows to select which variables + should be kept for the selected observations. + + - The `mode` and `order` field were added to `event()`. They allow to select + the first or last observation per by group if there are multiple observation + fulfilling the event condition. + ## Breaking Changes - The following functions, which were deprecated in previous `{admiral}` versions, have been removed: (#1950) From a592b92f3fa20bc55f30ca6a73ec4fb3abec44f5 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Tue, 18 Jul 2023 15:28:28 +0200 Subject: [PATCH 06/22] #1960 enhance_derive_extreme_event: fix links --- docs/pkgdown.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index 3de7593245..f68625fda2 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -21,6 +21,6 @@ articles: visits_periods: visits_periods.html last_built: 2023-07-18T11:09Z urls: - reference: https://pharmaverse.github.io/admiral/reference - article: https://pharmaverse.github.io/admiral/articles + reference: https://pharmaverse.github.io/admiral/cran-release/reference + article: https://pharmaverse.github.io/admiral/cran-release/articles From 246dd9f47d179178518691ec87b72ef127edd339 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Tue, 18 Jul 2023 18:00:27 +0200 Subject: [PATCH 07/22] #1960 enhance_derive_extreme_event: style files --- R/derive_extreme_event.R | 14 +- inst/WORDLIST | 1 + tests/testthat/test-derive_extreme_event.R | 174 ++++++++++----------- 3 files changed, 94 insertions(+), 95 deletions(-) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 9280d00cbe..92f62978cd 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -410,13 +410,13 @@ event <- function(dataset_name = NULL, #' #' @return An object of class `event_joined` event_joined <- function(dataset_name = NULL, - condition, - order = NULL, - join_vars, - join_type, - first_cond = NULL, - set_values_to = NULL, - keep_vars_source = NULL) { + condition, + order = NULL, + join_vars, + join_type, + first_cond = NULL, + set_values_to = NULL, + keep_vars_source = NULL) { out <- list( dataset_name = assert_character_scalar(dataset_name, optional = TRUE), condition = assert_filter_cond(enexpr(condition), optional = TRUE), diff --git a/inst/WORDLIST b/inst/WORDLIST index fcda52fa0c..d7b74865a6 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -322,6 +322,7 @@ summarization th thromboplastin tidyverse +tidyselect timeframe timepart timepoint diff --git a/tests/testthat/test-derive_extreme_event.R b/tests/testthat/test-derive_extreme_event.R index 8ac6c28b18..bb754bd645 100644 --- a/tests/testthat/test-derive_extreme_event.R +++ b/tests/testthat/test-derive_extreme_event.R @@ -280,64 +280,64 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { ## Test 4: event_joined() is handled correctly ---- test_that("derive_extreme_records Test 4: event_joined() is handled correctly", { -adsl <- tibble::tribble( - ~USUBJID, ~TRTSDTC, - "1", "2020-01-01", - "2", "2019-12-12", - "3", "2019-11-11", - "4", "2019-12-30", - "5", "2020-01-01", - "6", "2020-02-02", - "7", "2020-02-02", - "8", "2020-04-01", - "9", "2020-02-01" -) %>% - mutate( - TRTSDT = lubridate::ymd(TRTSDTC), - STUDYID = "XX1234" - ) + adsl <- tibble::tribble( + ~USUBJID, ~TRTSDTC, + "1", "2020-01-01", + "2", "2019-12-12", + "3", "2019-11-11", + "4", "2019-12-30", + "5", "2020-01-01", + "6", "2020-02-02", + "7", "2020-02-02", + "8", "2020-04-01", + "9", "2020-02-01" + ) %>% + mutate( + TRTSDT = lubridate::ymd(TRTSDTC), + STUDYID = "XX1234" + ) -adrs <- tibble::tribble( - ~USUBJID, ~ADTC, ~AVALC, - "1", "2020-01-01", "PR", - "1", "2020-02-01", "CR", - "1", "2020-02-16", "NE", - "1", "2020-03-01", "CR", - "1", "2020-04-01", "SD", - "2", "2020-01-01", "SD", - "2", "2020-02-01", "PR", - "2", "2020-03-01", "SD", - "2", "2020-03-13", "CR", - "3", "2019-11-12", "CR", - "3", "2019-12-02", "CR", - "3", "2020-01-01", "SD", - "4", "2020-01-01", "PR", - "4", "2020-03-01", "SD", - "4", "2020-04-01", "SD", - "4", "2020-05-01", "PR", - "4", "2020-05-15", "NON-CR/NON-PD", - "5", "2020-01-01", "PR", - "5", "2020-01-10", "SD", - "5", "2020-01-20", "PR", - "5", "2020-05-15", "NON-CR/NON-PD", - "6", "2020-02-06", "PR", - "6", "2020-02-16", "CR", - "6", "2020-03-30", "PR", - "7", "2020-02-06", "PR", - "7", "2020-02-16", "CR", - "7", "2020-04-01", "NE", - "9", "2020-02-16", "PD" -) %>% - mutate( - PARAMCD = "OVR", - ADT = lubridate::ymd(ADTC), - STUDYID = "XX1234" + adrs <- tibble::tribble( + ~USUBJID, ~ADTC, ~AVALC, + "1", "2020-01-01", "PR", + "1", "2020-02-01", "CR", + "1", "2020-02-16", "NE", + "1", "2020-03-01", "CR", + "1", "2020-04-01", "SD", + "2", "2020-01-01", "SD", + "2", "2020-02-01", "PR", + "2", "2020-03-01", "SD", + "2", "2020-03-13", "CR", + "3", "2019-11-12", "CR", + "3", "2019-12-02", "CR", + "3", "2020-01-01", "SD", + "4", "2020-01-01", "PR", + "4", "2020-03-01", "SD", + "4", "2020-04-01", "SD", + "4", "2020-05-01", "PR", + "4", "2020-05-15", "NON-CR/NON-PD", + "5", "2020-01-01", "PR", + "5", "2020-01-10", "SD", + "5", "2020-01-20", "PR", + "5", "2020-05-15", "NON-CR/NON-PD", + "6", "2020-02-06", "PR", + "6", "2020-02-16", "CR", + "6", "2020-03-30", "PR", + "7", "2020-02-06", "PR", + "7", "2020-02-16", "CR", + "7", "2020-04-01", "NE", + "9", "2020-02-16", "PD" ) %>% - derive_vars_merged( - dataset_add = adsl, - by_vars = exprs(STUDYID, USUBJID), - new_vars = exprs(TRTSDT) - ) + mutate( + PARAMCD = "OVR", + ADT = lubridate::ymd(ADTC), + STUDYID = "XX1234" + ) %>% + derive_vars_merged( + dataset_add = adsl, + by_vars = exprs(STUDYID, USUBJID), + new_vars = exprs(TRTSDT) + ) actual <- derive_extreme_event( @@ -411,7 +411,6 @@ adrs <- tibble::tribble( ), keep_vars_source = exprs(TRTSDT) ) - ), set_values_to = exprs( PARAMCD = "CBOR", @@ -419,37 +418,36 @@ adrs <- tibble::tribble( ) ) -expected <- bind_rows( - adrs, - tibble::tribble( - ~USUBJID, ~ADTC, ~AVALC, - "1", "2020-02-01", "CR", - "2", "2020-02-01", "SD", - "3", "2020-01-01", "SD", - "4", "2020-03-01", "SD", - "5", "2020-05-15", "NON-CR/NON-PD", - "6", "2020-03-30", "SD", - "7", "2020-02-06", "NE", - "8", NA_character_, "MISSING", - "9", "2020-02-16", "PD" - ) %>% - mutate( - ADT = lubridate::ymd(ADTC), - STUDYID = "XX1234", - PARAMCD = "CBOR", - PARAM = "Best Confirmed Overall Response by Investigator" + expected <- bind_rows( + adrs, + tibble::tribble( + ~USUBJID, ~ADTC, ~AVALC, + "1", "2020-02-01", "CR", + "2", "2020-02-01", "SD", + "3", "2020-01-01", "SD", + "4", "2020-03-01", "SD", + "5", "2020-05-15", "NON-CR/NON-PD", + "6", "2020-03-30", "SD", + "7", "2020-02-06", "NE", + "8", NA_character_, "MISSING", + "9", "2020-02-16", "PD" ) %>% - derive_vars_merged( - dataset_add = adsl, - by_vars = exprs(STUDYID, USUBJID), - new_vars = exprs(TRTSDT) - ) -) - -expect_dfs_equal( - base = expected, - compare = actual, - keys = c("USUBJID", "PARAMCD", "ADT") -) + mutate( + ADT = lubridate::ymd(ADTC), + STUDYID = "XX1234", + PARAMCD = "CBOR", + PARAM = "Best Confirmed Overall Response by Investigator" + ) %>% + derive_vars_merged( + dataset_add = adsl, + by_vars = exprs(STUDYID, USUBJID), + new_vars = exprs(TRTSDT) + ) + ) + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "PARAMCD", "ADT") + ) }) From 45d182f82a83bf325d1de9fbd1ab3731ebeeb8d1 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Wed, 19 Jul 2023 13:46:03 +0200 Subject: [PATCH 08/22] #1960 enhance_derive_extreme_event: add example --- R/derive_extreme_event.R | 125 ++++++++++++++++++++++++++++++++++++ man/derive_extreme_event.Rd | 125 ++++++++++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 92f62978cd..7863ec936d 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -87,6 +87,8 @@ #' #' @examples #' library(tibble) +#' library(dplyr) +#' library(lubridate) #' #' adqs <- tribble( #' ~USUBJID, ~PARAMCD, ~AVALC, ~ADY, @@ -135,6 +137,129 @@ #' PARAM = "Worst Sleeping Problems" #' ) #' ) +#' +#' # derive confirmed best overall response +#' adsl <- tribble( +#' ~USUBJID, ~TRTSDTC, +#' "1", "2020-01-01", +#' "2", "2019-12-12", +#' "3", "2019-11-11", +#' "4", "2019-12-30", +#' "5", "2020-01-01", +#' "6", "2020-02-02", +#' "7", "2020-02-02", +#' "8", "2020-02-01" +#' ) %>% +#' mutate(TRTSDT = ymd(TRTSDTC)) +#' +#' adrs <- tribble( +#' ~USUBJID, ~ADTC, ~AVALC, +#' "1", "2020-01-01", "PR", +#' "1", "2020-02-01", "CR", +#' "1", "2020-02-16", "NE", +#' "1", "2020-03-01", "CR", +#' "1", "2020-04-01", "SD", +#' "2", "2020-01-01", "SD", +#' "2", "2020-02-01", "PR", +#' "2", "2020-03-01", "SD", +#' "2", "2020-03-13", "CR", +#' "4", "2020-01-01", "PR", +#' "4", "2020-03-01", "NE", +#' "4", "2020-04-01", "NE", +#' "4", "2020-05-01", "PR", +#' "5", "2020-01-01", "PR", +#' "5", "2020-01-10", "PR", +#' "5", "2020-01-20", "PR", +#' "6", "2020-02-06", "PR", +#' "6", "2020-02-16", "CR", +#' "6", "2020-03-30", "PR", +#' "7", "2020-02-06", "PR", +#' "7", "2020-02-16", "CR", +#' "7", "2020-04-01", "NE", +#' "8", "2020-02-16", "PD" +#' ) %>% +#' mutate( +#' ADT = ymd(ADTC), +#' PARAMCD = "OVR", +#' PARAM = "Overall Response by Investigator" +#' ) %>% +#' derive_vars_merged( +#' dataset_add = adsl, +#' by_vars = exprs(USUBJID), +#' new_vars = exprs(TRTSDT) +#' ) +#' +#' derive_extreme_event( +#' adrs, +#' by_vars = exprs(USUBJID), +#' order = exprs(ADT), +#' mode = "first", +#' source_datasets = list(adsl = adsl), +#' events = list( +#' # CR needs to be confirmed by a second CR at least 28 days later +#' # at most one NE is acceptable between the two assessments +#' event_joined( +#' join_vars = exprs(AVALC, ADT), +#' join_type = "after", +#' first_cond = AVALC.join == "CR" & +#' ADT.join >= ADT + 28, +#' condition = AVALC == "CR" & +#' all(AVALC.join %in% c("CR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1, +#' set_values_to = exprs( +#' AVALC = "CR" +#' ) +#' ), +#' # CR needs to be confirmed by a second CR or PR at least 28 days later +#' # at most one NE is acceptable between the two assessments +#' event_joined( +#' join_vars = exprs(AVALC, ADT), +#' join_type = "after", +#' first_cond = AVALC.join %in% c("CR", "PR") & +#' ADT.join >= ADT + 28, +#' condition = AVALC == "PR" & +#' all(AVALC.join %in% c("CR", "PR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1, +#' set_values_to = exprs( +#' AVALC = "PR" +#' ) +#' ), +#' event( +#' condition = AVALC %in% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, +#' set_values_to = exprs( +#' AVALC = "SD" +#' ) +#' ), +#' event( +#' condition = AVALC == "PD", +#' set_values_to = exprs( +#' AVALC = "PD" +#' ) +#' ), +#' event( +#' condition = AVALC %in% c("CR", "PR", "SD", "NE"), +#' set_values_to = exprs( +#' AVALC = "NE" +#' ) +#' ), +#' # set response to MISSING for patients without records in ADRS +#' event( +#' dataset_name = "adsl", +#' condition = TRUE, +#' set_values_to = exprs( +#' AVALC = "MISSING" +#' ), +#' keep_vars_source = exprs(TRTSDT) +#' ) +#' ), +#' set_values_to = exprs( +#' PARAMCD = "CBOR", +#' PARAM = "Best Confirmed Overall Response by Investigator" +#' ) +#' ) %>% +#' filter(PARAMCD == "CBOR") +#' +#' derive_extreme_event <- function(dataset, by_vars = NULL, events, diff --git a/man/derive_extreme_event.Rd b/man/derive_extreme_event.Rd index 124099090f..b79467fada 100644 --- a/man/derive_extreme_event.Rd +++ b/man/derive_extreme_event.Rd @@ -131,6 +131,8 @@ the selected observations. } \examples{ library(tibble) +library(dplyr) +library(lubridate) adqs <- tribble( ~USUBJID, ~PARAMCD, ~AVALC, ~ADY, @@ -179,6 +181,129 @@ derive_extreme_event( PARAM = "Worst Sleeping Problems" ) ) + +# derive confirmed best overall response +adsl <- tribble( +~USUBJID, ~TRTSDTC, +"1", "2020-01-01", +"2", "2019-12-12", +"3", "2019-11-11", +"4", "2019-12-30", +"5", "2020-01-01", +"6", "2020-02-02", +"7", "2020-02-02", +"8", "2020-02-01" +) \%>\% + mutate(TRTSDT = ymd(TRTSDTC)) + +adrs <- tribble( + ~USUBJID, ~ADTC, ~AVALC, + "1", "2020-01-01", "PR", + "1", "2020-02-01", "CR", + "1", "2020-02-16", "NE", + "1", "2020-03-01", "CR", + "1", "2020-04-01", "SD", + "2", "2020-01-01", "SD", + "2", "2020-02-01", "PR", + "2", "2020-03-01", "SD", + "2", "2020-03-13", "CR", + "4", "2020-01-01", "PR", + "4", "2020-03-01", "NE", + "4", "2020-04-01", "NE", + "4", "2020-05-01", "PR", + "5", "2020-01-01", "PR", + "5", "2020-01-10", "PR", + "5", "2020-01-20", "PR", + "6", "2020-02-06", "PR", + "6", "2020-02-16", "CR", + "6", "2020-03-30", "PR", + "7", "2020-02-06", "PR", + "7", "2020-02-16", "CR", + "7", "2020-04-01", "NE", + "8", "2020-02-16", "PD" +) \%>\% + mutate( + ADT = ymd(ADTC), + PARAMCD = "OVR", + PARAM = "Overall Response by Investigator" + ) \%>\% + derive_vars_merged( + dataset_add = adsl, + by_vars = exprs(USUBJID), + new_vars = exprs(TRTSDT) + ) + +derive_extreme_event( + adrs, + by_vars = exprs(USUBJID), + order = exprs(ADT), + mode = "first", + source_datasets = list(adsl = adsl), + events = list( + # CR needs to be confirmed by a second CR at least 28 days later + # at most one NE is acceptable between the two assessments + event_joined( + join_vars = exprs(AVALC, ADT), + join_type = "after", + first_cond = AVALC.join == "CR" & + ADT.join >= ADT + 28, + condition = AVALC == "CR" & + all(AVALC.join \%in\% c("CR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1, + set_values_to = exprs( + AVALC = "CR" + ) + ), + # CR needs to be confirmed by a second CR or PR at least 28 days later + # at most one NE is acceptable between the two assessments + event_joined( + join_vars = exprs(AVALC, ADT), + join_type = "after", + first_cond = AVALC.join \%in\% c("CR", "PR") & + ADT.join >= ADT + 28, + condition = AVALC == "PR" & + all(AVALC.join \%in\% c("CR", "PR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1, + set_values_to = exprs( + AVALC = "PR" + ) + ), + event( + condition = AVALC \%in\% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, + set_values_to = exprs( + AVALC = "SD" + ) + ), + event( + condition = AVALC == "PD", + set_values_to = exprs( + AVALC = "PD" + ) + ), + event( + condition = AVALC \%in\% c("CR", "PR", "SD", "NE"), + set_values_to = exprs( + AVALC = "NE" + ) + ), + # set response to MISSING for patients without records in ADRS + event( + dataset_name = "adsl", + condition = TRUE, + set_values_to = exprs( + AVALC = "MISSING" + ), + keep_vars_source = exprs(TRTSDT) + ) + ), + set_values_to = exprs( + PARAMCD = "CBOR", + PARAM = "Best Confirmed Overall Response by Investigator" + ) + ) \%>\% + filter(PARAMCD == "CBOR") + + } \seealso{ BDS-Findings Functions for adding Parameters/Records: From 1538ca159244b5ef22102c24878db02fe9cdd43b Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Wed, 19 Jul 2023 13:59:43 +0200 Subject: [PATCH 09/22] #1960 enhance_derive_extreme_event: style files --- R/derive_extreme_event.R | 147 +++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 74 deletions(-) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 7863ec936d..879bb52fe0 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -140,15 +140,15 @@ #' #' # derive confirmed best overall response #' adsl <- tribble( -#' ~USUBJID, ~TRTSDTC, -#' "1", "2020-01-01", -#' "2", "2019-12-12", -#' "3", "2019-11-11", -#' "4", "2019-12-30", -#' "5", "2020-01-01", -#' "6", "2020-02-02", -#' "7", "2020-02-02", -#' "8", "2020-02-01" +#' ~USUBJID, ~TRTSDTC, +#' "1", "2020-01-01", +#' "2", "2019-12-12", +#' "3", "2019-11-11", +#' "4", "2019-12-30", +#' "5", "2020-01-01", +#' "6", "2020-02-02", +#' "7", "2020-02-02", +#' "8", "2020-02-01" #' ) %>% #' mutate(TRTSDT = ymd(TRTSDTC)) #' @@ -190,76 +190,75 @@ #' ) #' #' derive_extreme_event( -#' adrs, -#' by_vars = exprs(USUBJID), -#' order = exprs(ADT), -#' mode = "first", -#' source_datasets = list(adsl = adsl), -#' events = list( -#' # CR needs to be confirmed by a second CR at least 28 days later -#' # at most one NE is acceptable between the two assessments -#' event_joined( -#' join_vars = exprs(AVALC, ADT), -#' join_type = "after", -#' first_cond = AVALC.join == "CR" & -#' ADT.join >= ADT + 28, -#' condition = AVALC == "CR" & -#' all(AVALC.join %in% c("CR", "NE")) & -#' count_vals(var = AVALC.join, val = "NE") <= 1, -#' set_values_to = exprs( -#' AVALC = "CR" -#' ) -#' ), -#' # CR needs to be confirmed by a second CR or PR at least 28 days later -#' # at most one NE is acceptable between the two assessments -#' event_joined( -#' join_vars = exprs(AVALC, ADT), -#' join_type = "after", -#' first_cond = AVALC.join %in% c("CR", "PR") & -#' ADT.join >= ADT + 28, -#' condition = AVALC == "PR" & -#' all(AVALC.join %in% c("CR", "PR", "NE")) & -#' count_vals(var = AVALC.join, val = "NE") <= 1, -#' set_values_to = exprs( -#' AVALC = "PR" -#' ) -#' ), -#' event( -#' condition = AVALC %in% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, -#' set_values_to = exprs( -#' AVALC = "SD" -#' ) -#' ), -#' event( -#' condition = AVALC == "PD", -#' set_values_to = exprs( -#' AVALC = "PD" -#' ) -#' ), -#' event( -#' condition = AVALC %in% c("CR", "PR", "SD", "NE"), -#' set_values_to = exprs( -#' AVALC = "NE" -#' ) -#' ), -#' # set response to MISSING for patients without records in ADRS -#' event( -#' dataset_name = "adsl", -#' condition = TRUE, -#' set_values_to = exprs( -#' AVALC = "MISSING" -#' ), -#' keep_vars_source = exprs(TRTSDT) +#' adrs, +#' by_vars = exprs(USUBJID), +#' order = exprs(ADT), +#' mode = "first", +#' source_datasets = list(adsl = adsl), +#' events = list( +#' # CR needs to be confirmed by a second CR at least 28 days later +#' # at most one NE is acceptable between the two assessments +#' event_joined( +#' join_vars = exprs(AVALC, ADT), +#' join_type = "after", +#' first_cond = AVALC.join == "CR" & +#' ADT.join >= ADT + 28, +#' condition = AVALC == "CR" & +#' all(AVALC.join %in% c("CR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1, +#' set_values_to = exprs( +#' AVALC = "CR" +#' ) +#' ), +#' # CR needs to be confirmed by a second CR or PR at least 28 days later +#' # at most one NE is acceptable between the two assessments +#' event_joined( +#' join_vars = exprs(AVALC, ADT), +#' join_type = "after", +#' first_cond = AVALC.join %in% c("CR", "PR") & +#' ADT.join >= ADT + 28, +#' condition = AVALC == "PR" & +#' all(AVALC.join %in% c("CR", "PR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1, +#' set_values_to = exprs( +#' AVALC = "PR" +#' ) +#' ), +#' event( +#' condition = AVALC %in% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, +#' set_values_to = exprs( +#' AVALC = "SD" +#' ) +#' ), +#' event( +#' condition = AVALC == "PD", +#' set_values_to = exprs( +#' AVALC = "PD" #' ) #' ), -#' set_values_to = exprs( -#' PARAMCD = "CBOR", -#' PARAM = "Best Confirmed Overall Response by Investigator" +#' event( +#' condition = AVALC %in% c("CR", "PR", "SD", "NE"), +#' set_values_to = exprs( +#' AVALC = "NE" +#' ) +#' ), +#' # set response to MISSING for patients without records in ADRS +#' event( +#' dataset_name = "adsl", +#' condition = TRUE, +#' set_values_to = exprs( +#' AVALC = "MISSING" +#' ), +#' keep_vars_source = exprs(TRTSDT) #' ) -#' ) %>% +#' ), +#' set_values_to = exprs( +#' PARAMCD = "CBOR", +#' PARAM = "Best Confirmed Overall Response by Investigator" +#' ) +#' ) %>% #' filter(PARAMCD == "CBOR") #' -#' derive_extreme_event <- function(dataset, by_vars = NULL, events, From 13ce88e22d2e3646212b4bd6ca0075a2146de7ac Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Wed, 19 Jul 2023 18:20:38 +0200 Subject: [PATCH 10/22] #1960 enhance_derive_extreme_event:add example and tests for event-specific modes --- R/derive_extreme_event.R | 54 +++++- man/derive_extreme_event.Rd | 187 ++++++++++++--------- tests/testthat/test-derive_extreme_event.R | 63 ++++++- 3 files changed, 219 insertions(+), 85 deletions(-) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 879bb52fe0..4b4cec2632 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -138,7 +138,45 @@ #' ) #' ) #' -#' # derive confirmed best overall response +#' # use different mode by event +#' adhy <- tribble( +#' ~USUBJID, ~AVISITN, ~CRIT1FL, +#' "1", 1, "Y", +#' "1", 2, "Y", +#' "2", 1, "Y", +#' "2", 2, NA_character_, +#' "2", 3, "Y", +#' "2", 4, NA_character_ +#' ) %>% +#' mutate( +#' PARAMCD = "ALKPH", +#' PARAM = "Alkaline Phosphatase (U/L)" +#' ) +#' +#' derive_extreme_event( +#' adhy, +#' by_vars = exprs(USUBJID), +#' events = list( +#' event( +#' condition = is.na(CRIT1FL), +#' set_values_to = exprs(AVALC = "N") +#' ), +#' event( +#' condition = CRIT1FL == "Y", +#' mode = "last", +#' set_values_to = exprs(AVALC = "Y") +#' ) +#' ), +#' order = exprs(AVISITN), +#' mode = "first", +#' keep_vars_source = exprs(AVISITN), +#' set_values_to = exprs( +#' PARAMCD = "ALK2", +#' PARAM = "ALKPH <= 2 times ULN" +#' ) +#' ) +#' +#' # derive confirmed best overall response (using event_joined()) #' adsl <- tribble( #' ~USUBJID, ~TRTSDTC, #' "1", "2020-01-01", @@ -318,6 +356,11 @@ derive_extreme_event <- function(dataset, } else { data_source <- source_datasets[[event$dataset_name]] } + if (is.null(event$order)) { + event_order <- order + } else { + event_order <- event$order + } if (inherits(event, "event")) { data_events <- data_source %>% group_by(!!!by_vars) %>% @@ -327,16 +370,11 @@ derive_extreme_event <- function(dataset, data_events <- filter_extreme( data_source, by_vars = by_vars, - order = event$order, + order = event_order, mode = event$mode ) } } else { - if (is.null(event$order)) { - event_order <- order - } else { - event_order <- event$order - } data_events <- filter_joined( dataset, by_vars = by_vars, @@ -355,7 +393,7 @@ derive_extreme_event <- function(dataset, data_events %>% mutate(!!tmp_event_no := index) %>% process_set_values_to(set_values_to = event$set_values_to) %>% - select(!!!event_keep_vars_source, !!!by_vars, names(event$set_values_to)) + select(!!!event_keep_vars_source, !!tmp_event_no, !!!by_vars, names(event$set_values_to)) } ) selected_records <- bind_rows(selected_records_ls) diff --git a/man/derive_extreme_event.Rd b/man/derive_extreme_event.Rd index b79467fada..c39ae2806f 100644 --- a/man/derive_extreme_event.Rd +++ b/man/derive_extreme_event.Rd @@ -182,17 +182,55 @@ derive_extreme_event( ) ) -# derive confirmed best overall response +# use different mode by event +adhy <- tribble( + ~USUBJID, ~AVISITN, ~CRIT1FL, + "1", 1, "Y", + "1", 2, "Y", + "2", 1, "Y", + "2", 2, NA_character_, + "2", 3, "Y", + "2", 4, NA_character_ +) \%>\% + mutate( + PARAMCD = "ALKPH", + PARAM = "Alkaline Phosphatase (U/L)" + ) + +derive_extreme_event( + adhy, + by_vars = exprs(USUBJID), + events = list( + event( + condition = is.na(CRIT1FL), + set_values_to = exprs(AVALC = "N") + ), + event( + condition = CRIT1FL == "Y", + mode = "last", + set_values_to = exprs(AVALC = "Y") + ) + ), + order = exprs(AVISITN), + mode = "first", + keep_vars_source = exprs(AVISITN), + set_values_to = exprs( + PARAMCD = "ALK2", + PARAM = "ALKPH <= 2 times ULN" + ) +) + +# derive confirmed best overall response (using event_joined()) adsl <- tribble( -~USUBJID, ~TRTSDTC, -"1", "2020-01-01", -"2", "2019-12-12", -"3", "2019-11-11", -"4", "2019-12-30", -"5", "2020-01-01", -"6", "2020-02-02", -"7", "2020-02-02", -"8", "2020-02-01" + ~USUBJID, ~TRTSDTC, + "1", "2020-01-01", + "2", "2019-12-12", + "3", "2019-11-11", + "4", "2019-12-30", + "5", "2020-01-01", + "6", "2020-02-02", + "7", "2020-02-02", + "8", "2020-02-01" ) \%>\% mutate(TRTSDT = ymd(TRTSDTC)) @@ -234,76 +272,75 @@ adrs <- tribble( ) derive_extreme_event( - adrs, - by_vars = exprs(USUBJID), - order = exprs(ADT), - mode = "first", - source_datasets = list(adsl = adsl), - events = list( - # CR needs to be confirmed by a second CR at least 28 days later - # at most one NE is acceptable between the two assessments - event_joined( - join_vars = exprs(AVALC, ADT), - join_type = "after", - first_cond = AVALC.join == "CR" & - ADT.join >= ADT + 28, - condition = AVALC == "CR" & - all(AVALC.join \%in\% c("CR", "NE")) & - count_vals(var = AVALC.join, val = "NE") <= 1, - set_values_to = exprs( - AVALC = "CR" - ) - ), - # CR needs to be confirmed by a second CR or PR at least 28 days later - # at most one NE is acceptable between the two assessments - event_joined( - join_vars = exprs(AVALC, ADT), - join_type = "after", - first_cond = AVALC.join \%in\% c("CR", "PR") & - ADT.join >= ADT + 28, - condition = AVALC == "PR" & - all(AVALC.join \%in\% c("CR", "PR", "NE")) & - count_vals(var = AVALC.join, val = "NE") <= 1, - set_values_to = exprs( - AVALC = "PR" - ) - ), - event( - condition = AVALC \%in\% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, - set_values_to = exprs( - AVALC = "SD" - ) - ), - event( - condition = AVALC == "PD", - set_values_to = exprs( - AVALC = "PD" - ) - ), - event( - condition = AVALC \%in\% c("CR", "PR", "SD", "NE"), - set_values_to = exprs( - AVALC = "NE" - ) - ), - # set response to MISSING for patients without records in ADRS - event( - dataset_name = "adsl", - condition = TRUE, - set_values_to = exprs( - AVALC = "MISSING" - ), - keep_vars_source = exprs(TRTSDT) + adrs, + by_vars = exprs(USUBJID), + order = exprs(ADT), + mode = "first", + source_datasets = list(adsl = adsl), + events = list( + # CR needs to be confirmed by a second CR at least 28 days later + # at most one NE is acceptable between the two assessments + event_joined( + join_vars = exprs(AVALC, ADT), + join_type = "after", + first_cond = AVALC.join == "CR" & + ADT.join >= ADT + 28, + condition = AVALC == "CR" & + all(AVALC.join \%in\% c("CR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1, + set_values_to = exprs( + AVALC = "CR" ) ), - set_values_to = exprs( - PARAMCD = "CBOR", - PARAM = "Best Confirmed Overall Response by Investigator" + # CR needs to be confirmed by a second CR or PR at least 28 days later + # at most one NE is acceptable between the two assessments + event_joined( + join_vars = exprs(AVALC, ADT), + join_type = "after", + first_cond = AVALC.join \%in\% c("CR", "PR") & + ADT.join >= ADT + 28, + condition = AVALC == "PR" & + all(AVALC.join \%in\% c("CR", "PR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1, + set_values_to = exprs( + AVALC = "PR" + ) + ), + event( + condition = AVALC \%in\% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, + set_values_to = exprs( + AVALC = "SD" + ) + ), + event( + condition = AVALC == "PD", + set_values_to = exprs( + AVALC = "PD" + ) + ), + event( + condition = AVALC \%in\% c("CR", "PR", "SD", "NE"), + set_values_to = exprs( + AVALC = "NE" + ) + ), + # set response to MISSING for patients without records in ADRS + event( + dataset_name = "adsl", + condition = TRUE, + set_values_to = exprs( + AVALC = "MISSING" + ), + keep_vars_source = exprs(TRTSDT) ) - ) \%>\% + ), + set_values_to = exprs( + PARAMCD = "CBOR", + PARAM = "Best Confirmed Overall Response by Investigator" + ) +) \%>\% filter(PARAMCD == "CBOR") - } \seealso{ BDS-Findings Functions for adding Parameters/Records: diff --git a/tests/testthat/test-derive_extreme_event.R b/tests/testthat/test-derive_extreme_event.R index bb754bd645..b779e5f721 100644 --- a/tests/testthat/test-derive_extreme_event.R +++ b/tests/testthat/test-derive_extreme_event.R @@ -278,8 +278,67 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { ) }) -## Test 4: event_joined() is handled correctly ---- -test_that("derive_extreme_records Test 4: event_joined() is handled correctly", { +## Test 4: event-specific mode ---- +test_that("derive_extreme_records Test 4: event-specific mode", { + adhy <- tibble::tribble( + ~USUBJID, ~AVISITN, ~CRIT1FL, + "1", 1, "Y", + "1", 2, "Y", + "2", 1, "Y", + "2", 2, NA_character_, + "2", 3, "Y", + "2", 4, NA_character_ + ) %>% + mutate( + PARAMCD = "ALKPH", + PARAM = "Alkaline Phosphatase (U/L)" + ) + + actual <- derive_extreme_event( + adhy, + by_vars = exprs(USUBJID), + events = list( + event( + condition = is.na(CRIT1FL), + set_values_to = exprs(AVALC = "N") + ), + event( + condition = CRIT1FL == "Y", + mode = "last", + set_values_to = exprs(AVALC = "Y") + ) + ), + order = exprs(AVISITN), + mode = "first", + keep_vars_source = exprs(AVISITN), + set_values_to = exprs( + PARAMCD = "ALK2", + PARAM = "ALKPH <= 2 times ULN" + ) + ) + + expected <- bind_rows( + adhy, + tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 2, "Y", + "2", 2, "N" + ) %>% + mutate( + PARAMCD = "ALK2", + PARAM = "ALKPH <= 2 times ULN" + ) + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN", "PARAMCD") + ) +}) + +## Test 5: event_joined() is handled correctly ---- +test_that("derive_extreme_records Test 5: event_joined() is handled correctly", { adsl <- tibble::tribble( ~USUBJID, ~TRTSDTC, "1", "2020-01-01", From 293121201622581a7338b379b87bd54fad2af174 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Thu, 20 Jul 2023 16:56:10 +0200 Subject: [PATCH 11/22] #1960 enhance_derive_extreme_event: add ignore_event_order argument --- NEWS.md | 2 + R/derive_extreme_event.R | 25 +++++++++-- man/derive_extreme_event.Rd | 10 +++++ tests/testthat/test-derive_extreme_event.R | 52 ++++++++++++++++++++++ 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index d6394a2bc9..7ca9dce685 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,8 @@ - The `mode` and `order` field were added to `event()`. They allow to select the first or last observation per by group if there are multiple observation fulfilling the event condition. + + - The `ignore_event_order` argument was added. ## Breaking Changes - The following functions, which were deprecated in previous `{admiral}` versions, have been removed: (#1950) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 4b4cec2632..692ab881db 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -39,6 +39,15 @@ #' A named list of datasets is expected. The `dataset_name` field of `event()` #' and `event_joined()` refers to the dataset provided in the list. #' +#' @param ignore_event_order Ignore event order +#' +#' If the argument is set to `TRUE`, all events defined by `events` are +#' considered as equipollent. If there is more than one observation per by +#' group the first or last (with respect to `mode` and `order`) is select +#' without taking the order of the events into account. +#' +#' *Permitted Values:* `TRUE`, `FALSE` +#' #' @param keep_vars_source Variables to keep from the source dataset #' #' For each event the specified variables are kept from the selected @@ -303,6 +312,7 @@ derive_extreme_event <- function(dataset, order, mode, source_datasets = NULL, + ignore_event_order = FALSE, check_type = "warning", set_values_to, keep_vars_source = exprs(everything())) { @@ -333,6 +343,7 @@ derive_extreme_event <- function(dataset, ) } + assert_logical_scalar(ignore_event_order) check_type <- assert_character_scalar( check_type, @@ -345,7 +356,11 @@ derive_extreme_event <- function(dataset, # Create new observations ## Create a dataset (selected_records) from `events` event_index <- as.list(seq_along(events)) - tmp_event_no <- get_new_tmp_var(dataset, prefix = "tmp_event_no") + if (ignore_event_order) { + tmp_event_no <- NULL + } else { + tmp_event_no <- get_new_tmp_var(dataset, prefix = "tmp_event_no") + } selected_records_ls <- map2( events, @@ -390,8 +405,10 @@ derive_extreme_event <- function(dataset, } else { event_keep_vars_source <- event$keep_vars_source } + if (!ignore_event_order) { + data_events <- mutate(data_events, !!tmp_event_no := index) + } data_events %>% - mutate(!!tmp_event_no := index) %>% process_set_values_to(set_values_to = event$set_values_to) %>% select(!!!event_keep_vars_source, !!tmp_event_no, !!!by_vars, names(event$set_values_to)) } @@ -404,7 +421,7 @@ derive_extreme_event <- function(dataset, derive_var_obs_number( new_var = !!tmp_obs, order = order, - by_vars = expr_c(by_vars, expr(!!tmp_event_no)), + by_vars = expr_c(by_vars, tmp_event_no), check_type = check_type ) @@ -417,7 +434,7 @@ derive_extreme_event <- function(dataset, new_obs <- selected_records %>% filter_extreme( by_vars = by_vars, - order = expr_c(expr(!!tmp_event_no), tmp_obs_expr), + order = expr_c(tmp_event_no, tmp_obs_expr), mode = "first", check_type = check_type ) %>% diff --git a/man/derive_extreme_event.Rd b/man/derive_extreme_event.Rd index c39ae2806f..81fce5057e 100644 --- a/man/derive_extreme_event.Rd +++ b/man/derive_extreme_event.Rd @@ -11,6 +11,7 @@ derive_extreme_event( order, mode, source_datasets = NULL, + ignore_event_order = FALSE, check_type = "warning", set_values_to, keep_vars_source = exprs(everything()) @@ -60,6 +61,15 @@ sorting by \code{order}. A named list of datasets is expected. The \code{dataset_name} field of \code{event()} and \code{event_joined()} refers to the dataset provided in the list.} +\item{ignore_event_order}{Ignore event order + +If the argument is set to \code{TRUE}, all events defined by \code{events} are +considered as equipollent. If there is more than one observation per by +group the first or last (with respect to \code{mode} and \code{order}) is select +without taking the order of the events into account. + +\emph{Permitted Values:} \code{TRUE}, \code{FALSE}} + \item{check_type}{Check uniqueness? If \code{"warning"} or \code{"error"} is specified, the specified message is issued diff --git a/tests/testthat/test-derive_extreme_event.R b/tests/testthat/test-derive_extreme_event.R index b779e5f721..122720dd21 100644 --- a/tests/testthat/test-derive_extreme_event.R +++ b/tests/testthat/test-derive_extreme_event.R @@ -510,3 +510,55 @@ test_that("derive_extreme_records Test 5: event_joined() is handled correctly", keys = c("USUBJID", "PARAMCD", "ADT") ) }) + +## Test 6: ignore_event_order ---- +test_that("derive_extreme_records Test 6: ignore_event_order", { + adrs <- tibble::tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "CR" + ) %>% + mutate(PARAMCD = "OVR") + + actual <- derive_extreme_event( + adrs, + by_vars = exprs(USUBJID), + order = exprs(AVISITN), + mode = "first", + events = list( + event_joined( + join_vars = exprs(AVALC), + join_type = "after", + first_cond = AVALC.join == "CR", + condition = AVALC == "CR", + set_values_to = exprs(AVALC = "Y") + ), + event_joined( + join_vars = exprs(AVALC), + join_type = "after", + first_cond = AVALC.join %in% c("CR", "PR"), + condition = AVALC == "PR", + set_values_to = exprs(AVALC = "Y") + ) + ), + ignore_event_order = TRUE, + set_values_to = exprs( + PARAMCD = "CRSP" + ) + ) + + expected <- bind_rows( + adrs, + tibble::tribble( + ~USUBJID, ~AVISITN, ~AVALC, ~PARAMCD, + "1", 1, "Y", "CRSP" + ) + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "PARAMCD", "AVISITN") + ) +}) From f0b474e6347db8c8bae453fb0ee0091cb94202d6 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Thu, 20 Jul 2023 17:42:26 +0200 Subject: [PATCH 12/22] #1960 enhance_derive_extreme_event: fix spelling --- R/derive_extreme_event.R | 6 +++--- man/derive_extreme_event.Rd | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 692ab881db..d08f6f3762 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -42,9 +42,9 @@ #' @param ignore_event_order Ignore event order #' #' If the argument is set to `TRUE`, all events defined by `events` are -#' considered as equipollent. If there is more than one observation per by -#' group the first or last (with respect to `mode` and `order`) is select -#' without taking the order of the events into account. +#' considered equivalent. If there is more than one observation per by group +#' the first or last (with respect to `mode` and `order`) is select without +#' taking the order of the events into account. #' #' *Permitted Values:* `TRUE`, `FALSE` #' diff --git a/man/derive_extreme_event.Rd b/man/derive_extreme_event.Rd index 81fce5057e..2efd8e2a4d 100644 --- a/man/derive_extreme_event.Rd +++ b/man/derive_extreme_event.Rd @@ -64,9 +64,9 @@ and \code{event_joined()} refers to the dataset provided in the list.} \item{ignore_event_order}{Ignore event order If the argument is set to \code{TRUE}, all events defined by \code{events} are -considered as equipollent. If there is more than one observation per by -group the first or last (with respect to \code{mode} and \code{order}) is select -without taking the order of the events into account. +considered equivalent. If there is more than one observation per by group +the first or last (with respect to \code{mode} and \code{order}) is select without +taking the order of the events into account. \emph{Permitted Values:} \code{TRUE}, \code{FALSE}} From 36759c2b812d53aa9cfc0a4ef689c6401794725a Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Thu, 20 Jul 2023 18:27:49 +0200 Subject: [PATCH 13/22] #1960 enhance_derive_extreme_event: use correct input dataset --- R/derive_extreme_event.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index d08f6f3762..7cf042852d 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -391,7 +391,7 @@ derive_extreme_event <- function(dataset, } } else { data_events <- filter_joined( - dataset, + data_source, by_vars = by_vars, join_vars = event$join_vars, join_type = event$join_type, From eada7ba34c486e0fb91852131b3bd4643b73fffb Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Tue, 25 Jul 2023 16:47:17 +0200 Subject: [PATCH 14/22] #1960 enhance_derive_extreme_event: improve printing of event objects --- R/user_utils.R | 19 ++++++++++++++++--- tests/testthat/test-user_utils.R | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/R/user_utils.R b/R/user_utils.R index 7eea0632c8..c5a093b834 100644 --- a/R/user_utils.R +++ b/R/user_utils.R @@ -327,16 +327,29 @@ print_named_list <- function(list, indent = 0) { print(list[[name]]) } else if (is.list(list[[name]])) { cat(strrep(" ", indent), name, ":\n", sep = "") - print_named_list(list[[name]], indent = indent + 2) + if (is_named(list[[name]])) { + print_named_list(list[[name]], indent = indent + 2) + } else { + for (item in list[[name]]) { + if (is.character(item)) { + chr_val <- dquote(item) + } else if (is_expression(item)) { + chr_val <- format(item) + } else { + chr_val <- item + } + cat(strrep(" ", indent + 2), paste0(chr_val, collapse = "\n"), "\n", sep = "") + } + } } else { if (is.character(list[[name]])) { chr_val <- dquote(list[[name]]) } else if (is_expression(list[[name]])) { - chr_val <- as_label(list[[name]]) + chr_val <- format(list[[name]]) } else { chr_val <- list[[name]] } - cat(strrep(" ", indent), name, ": ", chr_val, "\n", sep = "") + cat(strrep(" ", indent), name, ": ", paste0(chr_val, collapse = "\n"), "\n", sep = "") } } } diff --git a/tests/testthat/test-user_utils.R b/tests/testthat/test-user_utils.R index 66a8ed037d..f3a6da034f 100644 --- a/tests/testthat/test-user_utils.R +++ b/tests/testthat/test-user_utils.R @@ -137,7 +137,7 @@ test_that("print.source Test 13: `source` objects are printed as intended", { "dataset_name: \"ae\"", "filter: NULL", "date: AESTDTC", - "censor: 0L", + "censor: 0", "set_values_to:", " EVENTDESC: \"AE\"", " SRCDOM: \"AE\"", From 1843eada5def9b299949b28018de58da7cad3ac4 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Thu, 17 Aug 2023 18:14:04 +0200 Subject: [PATCH 15/22] #1960 enhance_derive_extreme_event: rename keep_vars_source to keep_source_vars --- NEWS.md | 4 +-- R/derive_extreme_event.R | 30 +++++++++++----------- man/derive_extreme_event.Rd | 10 ++++---- man/event.Rd | 4 +-- man/event_joined.Rd | 4 +-- tests/testthat/test-derive_extreme_event.R | 6 ++--- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/NEWS.md b/NEWS.md index 08ae57533c..11401d6006 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,8 +22,8 @@ `dataset_name` field to `event()`. It can be used to define events based on a different dataset than the input dataset. - - The `keep_vars_source` argument was added to the function and the - `keep_vars_source` field to `event()`. It allows to select which variables + - The `keep_source_vars` argument was added to the function and the + `keep_source_vars` field to `event()`. It allows to select which variables should be kept for the selected observations. - The `mode` and `order` field were added to `event()`. They allow to select diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 7cf042852d..d72973cd58 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -48,7 +48,7 @@ #' #' *Permitted Values:* `TRUE`, `FALSE` #' -#' @param keep_vars_source Variables to keep from the source dataset +#' @param keep_source_vars Variables to keep from the source dataset #' #' For each event the specified variables are kept from the selected #' observations. The variables specified for `by_vars` and created by @@ -73,7 +73,7 @@ #' #' 1. The variables specified by the `set_values_to` field of the event #' are added to the selected observations. -#' 1. Only the variables specified for the `keep_vars_source` field of the +#' 1. Only the variables specified for the `keep_source_vars` field of the #' event, and the by variables (`by_vars`) and the variables created by #' `set_values_to` are kept. #' 1. For each group (with respect to the variables specified for the @@ -178,7 +178,7 @@ #' ), #' order = exprs(AVISITN), #' mode = "first", -#' keep_vars_source = exprs(AVISITN), +#' keep_source_vars = exprs(AVISITN), #' set_values_to = exprs( #' PARAMCD = "ALK2", #' PARAM = "ALKPH <= 2 times ULN" @@ -296,7 +296,7 @@ #' set_values_to = exprs( #' AVALC = "MISSING" #' ), -#' keep_vars_source = exprs(TRTSDT) +#' keep_source_vars = exprs(TRTSDT) #' ) #' ), #' set_values_to = exprs( @@ -315,7 +315,7 @@ derive_extreme_event <- function(dataset, ignore_event_order = FALSE, check_type = "warning", set_values_to, - keep_vars_source = exprs(everything())) { + keep_source_vars = exprs(everything())) { # Check input parameters assert_vars(by_vars, optional = TRUE) assert_list_of(events, "event_def") @@ -351,7 +351,7 @@ derive_extreme_event <- function(dataset, case_sensitive = FALSE ) assert_varval_list(set_values_to) - keep_vars_source <- assert_expr_list(keep_vars_source) + keep_source_vars <- assert_expr_list(keep_source_vars) # Create new observations ## Create a dataset (selected_records) from `events` @@ -400,17 +400,17 @@ derive_extreme_event <- function(dataset, filter = !!event$condition ) } - if (is.null(event$keep_vars_source)) { - event_keep_vars_source <- keep_vars_source + if (is.null(event$keep_source_vars)) { + event_keep_source_vars <- keep_source_vars } else { - event_keep_vars_source <- event$keep_vars_source + event_keep_source_vars <- event$keep_source_vars } if (!ignore_event_order) { data_events <- mutate(data_events, !!tmp_event_no := index) } data_events %>% process_set_values_to(set_values_to = event$set_values_to) %>% - select(!!!event_keep_vars_source, !!tmp_event_no, !!!by_vars, names(event$set_values_to)) + select(!!!event_keep_source_vars, !!tmp_event_no, !!!by_vars, names(event$set_values_to)) } ) selected_records <- bind_rows(selected_records_ls) @@ -477,7 +477,7 @@ derive_extreme_event <- function(dataset, #' PARAM = "Worst Sleeping Problems")`. The values can be a symbol, a #' character string, a numeric value, `NA` or an expression. #' -#' @param keep_vars_source Variables to keep from the source dataset +#' @param keep_source_vars Variables to keep from the source dataset #' #' The specified variables are kept for the selected observations. The #' variables specified for `by_vars` (of `derive_extreme_event()`) and created @@ -500,7 +500,7 @@ event <- function(dataset_name = NULL, mode = NULL, order = NULL, set_values_to = NULL, - keep_vars_source = NULL) { + keep_source_vars = NULL) { out <- list( dataset_name = assert_character_scalar(dataset_name, optional = TRUE), condition = assert_filter_cond(enexpr(condition), optional = TRUE), @@ -516,7 +516,7 @@ event <- function(dataset_name = NULL, named = TRUE, optional = TRUE ), - keep_vars_source = assert_expr_list(keep_vars_source, optional = TRUE) + keep_source_vars = assert_expr_list(keep_source_vars, optional = TRUE) ) class(out) <- c("event", "event_def", "source", "list") out @@ -595,7 +595,7 @@ event_joined <- function(dataset_name = NULL, join_type, first_cond = NULL, set_values_to = NULL, - keep_vars_source = NULL) { + keep_source_vars = NULL) { out <- list( dataset_name = assert_character_scalar(dataset_name, optional = TRUE), condition = assert_filter_cond(enexpr(condition), optional = TRUE), @@ -612,7 +612,7 @@ event_joined <- function(dataset_name = NULL, named = TRUE, optional = TRUE ), - keep_vars_source = assert_expr_list(keep_vars_source, optional = TRUE) + keep_source_vars = assert_expr_list(keep_source_vars, optional = TRUE) ) class(out) <- c("event_joined", "event_def", "source", "list") out diff --git a/man/derive_extreme_event.Rd b/man/derive_extreme_event.Rd index 2efd8e2a4d..b9841bef65 100644 --- a/man/derive_extreme_event.Rd +++ b/man/derive_extreme_event.Rd @@ -14,7 +14,7 @@ derive_extreme_event( ignore_event_order = FALSE, check_type = "warning", set_values_to, - keep_vars_source = exprs(everything()) + keep_source_vars = exprs(everything()) ) } \arguments{ @@ -92,7 +92,7 @@ A list of variable name-value pairs is expected. symbol, a numeric value, an expression, or \code{NA}, e.g., \code{exprs(PARAMCD = "TDOSE", PARCAT1 = "OVERALL")}. }} -\item{keep_vars_source}{Variables to keep from the source dataset +\item{keep_source_vars}{Variables to keep from the source dataset For each event the specified variables are kept from the selected observations. The variables specified for \code{by_vars} and created by @@ -125,7 +125,7 @@ If the event is of class \code{event_joined}, \code{filter_joined()} is called t select the observations. \item The variables specified by the \code{set_values_to} field of the event are added to the selected observations. -\item Only the variables specified for the \code{keep_vars_source} field of the +\item Only the variables specified for the \code{keep_source_vars} field of the event, and the by variables (\code{by_vars}) and the variables created by \code{set_values_to} are kept. } @@ -223,7 +223,7 @@ derive_extreme_event( ), order = exprs(AVISITN), mode = "first", - keep_vars_source = exprs(AVISITN), + keep_source_vars = exprs(AVISITN), set_values_to = exprs( PARAMCD = "ALK2", PARAM = "ALKPH <= 2 times ULN" @@ -341,7 +341,7 @@ derive_extreme_event( set_values_to = exprs( AVALC = "MISSING" ), - keep_vars_source = exprs(TRTSDT) + keep_source_vars = exprs(TRTSDT) ) ), set_values_to = exprs( diff --git a/man/event.Rd b/man/event.Rd index 292b73becb..368fb4bc31 100644 --- a/man/event.Rd +++ b/man/event.Rd @@ -10,7 +10,7 @@ event( mode = NULL, order = NULL, set_values_to = NULL, - keep_vars_source = NULL + keep_source_vars = NULL ) } \arguments{ @@ -40,7 +40,7 @@ first or last observation if \code{mode} is specified. to be set for the event, e.g. \code{exprs(PARAMCD = "WSP", PARAM = "Worst Sleeping Problems")}. The values can be a symbol, a character string, a numeric value, \code{NA} or an expression.} -\item{keep_vars_source}{Variables to keep from the source dataset +\item{keep_source_vars}{Variables to keep from the source dataset The specified variables are kept for the selected observations. The variables specified for \code{by_vars} (of \code{derive_extreme_event()}) and created diff --git a/man/event_joined.Rd b/man/event_joined.Rd index c54d060bcc..e80a420d14 100644 --- a/man/event_joined.Rd +++ b/man/event_joined.Rd @@ -12,7 +12,7 @@ event_joined( join_type, first_cond = NULL, set_values_to = NULL, - keep_vars_source = NULL + keep_source_vars = NULL ) } \arguments{ @@ -59,7 +59,7 @@ observations are removed.} to be set for the event, e.g. \code{exprs(PARAMCD = "WSP", PARAM = "Worst Sleeping Problems")}. The values can be a symbol, a character string, a numeric value, \code{NA} or an expression.} -\item{keep_vars_source}{Variables to keep from the source dataset +\item{keep_source_vars}{Variables to keep from the source dataset The specified variables are kept for the selected observations. The variables specified for \code{by_vars} (of \code{derive_extreme_event()}) and created diff --git a/tests/testthat/test-derive_extreme_event.R b/tests/testthat/test-derive_extreme_event.R index 122720dd21..426f1f8382 100644 --- a/tests/testthat/test-derive_extreme_event.R +++ b/tests/testthat/test-derive_extreme_event.R @@ -262,7 +262,7 @@ test_that("derive_extreme_records Test 3: `source_datasets` works", { set_values_to = exprs( AVALC = "MISSING" ), - keep_vars_source = exprs(TRTSDT) + keep_source_vars = exprs(TRTSDT) ) ), set_values_to = exprs( @@ -310,7 +310,7 @@ test_that("derive_extreme_records Test 4: event-specific mode", { ), order = exprs(AVISITN), mode = "first", - keep_vars_source = exprs(AVISITN), + keep_source_vars = exprs(AVISITN), set_values_to = exprs( PARAMCD = "ALK2", PARAM = "ALKPH <= 2 times ULN" @@ -468,7 +468,7 @@ test_that("derive_extreme_records Test 5: event_joined() is handled correctly", set_values_to = exprs( AVALC = "MISSING" ), - keep_vars_source = exprs(TRTSDT) + keep_source_vars = exprs(TRTSDT) ) ), set_values_to = exprs( From cc103c57bd2c0a8ccc918ba82db8e0eb1047a142 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Fri, 18 Aug 2023 11:06:42 +0200 Subject: [PATCH 16/22] #1960 enhance_derive_extreme_event: add description field --- NEWS.md | 3 +++ R/derive_extreme_event.R | 33 ++++++++++++++++++++++++++------- man/derive_extreme_event.Rd | 20 +++++++++++++++----- man/event.Rd | 8 +++++++- man/event_joined.Rd | 8 +++++++- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/NEWS.md b/NEWS.md index 11401d6006..25b7d80a55 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,6 +31,9 @@ fulfilling the event condition. - The `ignore_event_order` argument was added. + + - The `description` field was added to `event()`. It can be used to provide + a description of the event in plain language. ## Breaking Changes - The following functions, which were deprecated in previous `{admiral}` versions, have been removed: (#1950) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index d72973cd58..1a26e1dc5a 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -92,6 +92,8 @@ #' @family der_prm_bds_findings #' @keywords der_prm_bds_findings #' +#' @seealso [event()], [event_joined()] +#' #' @export #' #' @examples @@ -243,9 +245,11 @@ #' mode = "first", #' source_datasets = list(adsl = adsl), #' events = list( -#' # CR needs to be confirmed by a second CR at least 28 days later -#' # at most one NE is acceptable between the two assessments #' event_joined( +#' description = paste( +#' "CR needs to be confirmed by a second CR at least 28 days later", +#' "at most one NE is acceptable between the two assessments" +#' ), #' join_vars = exprs(AVALC, ADT), #' join_type = "after", #' first_cond = AVALC.join == "CR" & @@ -257,9 +261,11 @@ #' AVALC = "CR" #' ) #' ), -#' # CR needs to be confirmed by a second CR or PR at least 28 days later -#' # at most one NE is acceptable between the two assessments #' event_joined( +#' description = paste( +#' "CR needs to be confirmed by a second CR or PR at least 28 days later,", +#' "at most one NE is acceptable between the two assessments" +#' ), #' join_vars = exprs(AVALC, ADT), #' join_type = "after", #' first_cond = AVALC.join %in% c("CR", "PR") & @@ -272,6 +278,10 @@ #' ) #' ), #' event( +#' description = paste( +#' "CR, PR, or SD are considered as SD if occurring at least 28", +#' "after treatment start" +#' ), #' condition = AVALC %in% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, #' set_values_to = exprs( #' AVALC = "SD" @@ -289,8 +299,8 @@ #' AVALC = "NE" #' ) #' ), -#' # set response to MISSING for patients without records in ADRS #' event( +#' description = "set response to MISSING for patients without records in ADRS", #' dataset_name = "adsl", #' condition = TRUE, #' set_values_to = exprs( @@ -487,6 +497,11 @@ derive_extreme_event <- function(dataset, #' a symbol or a tidyselect expression, e.g., `exprs(VISIT, VISITNUM, #' starts_with("RS"))`. #' +#' @param description Description of the event +#' +#' The description does not affect the derivations where the event is used. It +#' is intended for documentation only. +#' #' @keywords source_specifications #' @family source_specifications #' @@ -500,8 +515,10 @@ event <- function(dataset_name = NULL, mode = NULL, order = NULL, set_values_to = NULL, - keep_source_vars = NULL) { + keep_source_vars = NULL, + description = NULL) { out <- list( + description = assert_character_scalar(description, optional = TRUE), dataset_name = assert_character_scalar(dataset_name, optional = TRUE), condition = assert_filter_cond(enexpr(condition), optional = TRUE), mode = assert_character_scalar( @@ -595,8 +612,10 @@ event_joined <- function(dataset_name = NULL, join_type, first_cond = NULL, set_values_to = NULL, - keep_source_vars = NULL) { + keep_source_vars = NULL, + description = NULL) { out <- list( + description = assert_character_scalar(description, optional = TRUE), dataset_name = assert_character_scalar(dataset_name, optional = TRUE), condition = assert_filter_cond(enexpr(condition), optional = TRUE), order = assert_expr_list(order, optional = TRUE), diff --git a/man/derive_extreme_event.Rd b/man/derive_extreme_event.Rd index b9841bef65..03d9217e4e 100644 --- a/man/derive_extreme_event.Rd +++ b/man/derive_extreme_event.Rd @@ -288,9 +288,11 @@ derive_extreme_event( mode = "first", source_datasets = list(adsl = adsl), events = list( - # CR needs to be confirmed by a second CR at least 28 days later - # at most one NE is acceptable between the two assessments event_joined( + description = paste( + "CR needs to be confirmed by a second CR at least 28 days later", + "at most one NE is acceptable between the two assessments" + ), join_vars = exprs(AVALC, ADT), join_type = "after", first_cond = AVALC.join == "CR" & @@ -302,9 +304,11 @@ derive_extreme_event( AVALC = "CR" ) ), - # CR needs to be confirmed by a second CR or PR at least 28 days later - # at most one NE is acceptable between the two assessments event_joined( + description = paste( + "CR needs to be confirmed by a second CR or PR at least 28 days later,", + "at most one NE is acceptable between the two assessments" + ), join_vars = exprs(AVALC, ADT), join_type = "after", first_cond = AVALC.join \%in\% c("CR", "PR") & @@ -317,6 +321,10 @@ derive_extreme_event( ) ), event( + description = paste( + "CR, PR, or SD are considered as SD if occurring at least 28", + "after treatment start" + ), condition = AVALC \%in\% c("CR", "PR", "SD") & ADT >= TRTSDT + 28, set_values_to = exprs( AVALC = "SD" @@ -334,8 +342,8 @@ derive_extreme_event( AVALC = "NE" ) ), - # set response to MISSING for patients without records in ADRS event( + description = "set response to MISSING for patients without records in ADRS", dataset_name = "adsl", condition = TRUE, set_values_to = exprs( @@ -353,6 +361,8 @@ derive_extreme_event( } \seealso{ +\code{\link[=event]{event()}}, \code{\link[=event_joined]{event_joined()}} + BDS-Findings Functions for adding Parameters/Records: \code{\link{default_qtc_paramcd}()}, \code{\link{derive_expected_records}()}, diff --git a/man/event.Rd b/man/event.Rd index 368fb4bc31..aeabb110d1 100644 --- a/man/event.Rd +++ b/man/event.Rd @@ -10,7 +10,8 @@ event( mode = NULL, order = NULL, set_values_to = NULL, - keep_source_vars = NULL + keep_source_vars = NULL, + description = NULL ) } \arguments{ @@ -48,6 +49,11 @@ by \code{set_values_to} are always kept. \emph{Permitted Values}: A list of expressions where each element is a symbol or a tidyselect expression, e.g., \code{exprs(VISIT, VISITNUM, starts_with("RS"))}.} + +\item{description}{Description of the event + +The description does not affect the derivations where the event is used. It +is intended for documentation only.} } \value{ An object of class \code{event} diff --git a/man/event_joined.Rd b/man/event_joined.Rd index e80a420d14..0778b14ae8 100644 --- a/man/event_joined.Rd +++ b/man/event_joined.Rd @@ -12,7 +12,8 @@ event_joined( join_type, first_cond = NULL, set_values_to = NULL, - keep_source_vars = NULL + keep_source_vars = NULL, + description = NULL ) } \arguments{ @@ -67,6 +68,11 @@ by \code{set_values_to} are always kept. \emph{Permitted Values}: A list of expressions where each element is a symbol or a tidyselect expression, e.g., \code{exprs(VISIT, VISITNUM, starts_with("RS"))}.} + +\item{description}{Description of the event + +The description does not affect the derivations where the event is used. It +is intended for documentation only.} } \value{ An object of class \code{event_joined} From 4b912966651c617ff6ad8cfd4d852477bcfc106a Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Wed, 30 Aug 2023 16:22:32 +0200 Subject: [PATCH 17/22] #1960 enhance_derive_extreme_event: address QC comments --- NEWS.md | 5 +++++ R/derive_extreme_event.R | 10 ++++++---- tests/testthat/_snaps/user_utils.md | 12 ++++++++++++ tests/testthat/test-user_utils.R | 25 +++++++++++++++++++------ 4 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 tests/testthat/_snaps/user_utils.md diff --git a/NEWS.md b/NEWS.md index 19d13febca..94ca9157e8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,11 @@ ## New Features +- `event_joined()` events were added. They can be specified for the `events` +argument in `derive_extreme_event()`. This allows to define events based on more +than one observation, e.g., events which need to be confirmed by a second +assessment. (#1960) + ## Updates of Existing Functions - The functions `derive_param_bmi()` and `derive_param_bsa()` are updated to have the option of producing more values at visits when only weight is collected (#1228). - The functions `derive_var_age_years()` and `compute_age_years()` are updated to return an `NA` age in the case that the age unit is missing. (#2001) The argument `unit` for `derive_vars_aage()` is also changed to `age_unit` for consistency between these age-related functions. (#2025) diff --git a/R/derive_extreme_event.R b/R/derive_extreme_event.R index 1a26e1dc5a..aa4d01038c 100644 --- a/R/derive_extreme_event.R +++ b/R/derive_extreme_event.R @@ -3,7 +3,7 @@ #' Add the first available record from `events` for each by group as new #' records, all variables of the selected observation are kept. It can be used #' for selecting the extreme observation from a series of user-defined events. -#' This distinguish `derive_extreme_event()` from `derive_extreme_records()`, +#' This distinguishes `derive_extreme_event()` from `derive_extreme_records()`, #' where extreme records are derived based on certain order of existing #' variables. #' @@ -149,7 +149,7 @@ #' ) #' ) #' -#' # use different mode by event +#' # Use different mode by event #' adhy <- tribble( #' ~USUBJID, ~AVISITN, ~CRIT1FL, #' "1", 1, "Y", @@ -187,7 +187,9 @@ #' ) #' ) #' -#' # derive confirmed best overall response (using event_joined()) +#' # Derive confirmed best overall response (using event_joined()) +#' # CR - complete response, PR - partial response, SD - stable disease +#' # NE - not evaluable, PD - progressive disease #' adsl <- tribble( #' ~USUBJID, ~TRTSDTC, #' "1", "2020-01-01", @@ -263,7 +265,7 @@ #' ), #' event_joined( #' description = paste( -#' "CR needs to be confirmed by a second CR or PR at least 28 days later,", +#' "PR needs to be confirmed by a second CR or PR at least 28 days later,", #' "at most one NE is acceptable between the two assessments" #' ), #' join_vars = exprs(AVALC, ADT), diff --git a/tests/testthat/_snaps/user_utils.md b/tests/testthat/_snaps/user_utils.md new file mode 100644 index 0000000000..6242fdd888 --- /dev/null +++ b/tests/testthat/_snaps/user_utils.md @@ -0,0 +1,12 @@ +# print_named_list Test 18: named list with unamed list + + Code + print_named_list(list(list_item = list("Hello World!", expr(universe), list(42)), + another_one = ymd("2020-02-02"))) + Output + list_item: + "Hello World!" + universe + 42 + another_one: 2020-02-02 + diff --git a/tests/testthat/test-user_utils.R b/tests/testthat/test-user_utils.R index f3a6da034f..48ea59655e 100644 --- a/tests/testthat/test-user_utils.R +++ b/tests/testthat/test-user_utils.R @@ -37,7 +37,7 @@ test_that("convert_blanks_to_na Test 3: blank strings are turned into `NA` insid ## Test 4: `convert_blanks_to_na.list` produces a lists ---- -test_that("convert_blanks_to_na.list Test 4: `convert_blanks_to_na.list` produces a lists", { +test_that("convert_blanks_to_na Test 4: `convert_blanks_to_na.list` produces a lists", { x <- c("", "", "") expected_output <- lapply(x, convert_blanks_to_na) actual_output <- convert_blanks_to_na.list(x) @@ -46,15 +46,16 @@ test_that("convert_blanks_to_na.list Test 4: `convert_blanks_to_na.list` produce }) # Test 5: convert_na_to_blanks Test 5---- -test_that("convert_na_to_blanks Test 5: `NA` strings are turned into blank ", { +## Test 5: `NA` strings are turned into blank ---- +test_that("convert_blanks_to_na Test 5: `NA` strings are turned into blank ", { expect_identical( convert_na_to_blanks(c("a", NA, "b")), c("a", "", "b") ) }) -## Test 6: attributes are preserved when converting `NA` to blanks ---- -test_that("convert_na_to_blanks Test 6: attributes are preserved when converting `NA` to blanks", { +## Test 6: attributes are preserved when converting `NA` to blanks ---- +test_that("convert_blanks_to_na Test 6: attributes are preserved when converting `NA` to blanks", { input <- structure(letters, names = rev(letters), label = "Letters") input[c(1, 9, 23)] <- NA_character_ output <- convert_na_to_blanks(input) @@ -64,7 +65,7 @@ test_that("convert_na_to_blanks Test 6: attributes are preserved when converting }) ## Test 7: `NA` are turned into blank strings inside data frames ---- -test_that("convert_na_to_blanks Test 7: `NA` are turned into blank strings inside data frames", { +test_that("convert_blanks_to_na Test 7: `NA` are turned into blank strings inside data frames", { input <- tibble::tibble( a = structure(c("a", "b", NA, "c"), label = "A"), b = structure(c(1, NA, 21, 9), label = "B"), @@ -99,7 +100,7 @@ test_that("negate_vars Test 9: negate_vars returns list of negated variables", { }) ## Test 10: negate_vars returns NULL if input is NULL ---- -test_that("negate_vars Test 6: negate_vars returns NULL if input is NULL", { +test_that("negate_vars Test 10: negate_vars returns NULL if input is NULL", { expect_identical(negate_vars(NULL), NULL) }) @@ -198,7 +199,9 @@ test_that("print.source Test 15: `source` objects containing `data.frame`", { ) }) +# print_named_list ---- ## Test 16 print_named_list ---- +## Test 16: named list ---- test_that("print_named_list Test 16: named list", { expect_identical( capture.output(print_named_list(list(a = 1, b = 2))), @@ -219,3 +222,13 @@ test_that("print_named_list Test 17: unnamed list", { ) ) }) + +## Test 18: named list with unamed list ---- +test_that("print_named_list Test 18: named list with unamed list", { + expect_snapshot( + print_named_list(list( + list_item = list("Hello World!", expr(universe), list(42)), + another_one = ymd("2020-02-02")) + ) + ) +}) From 2e5309d9ca74a889c1ae02ec598e16899a47dc69 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Wed, 30 Aug 2023 16:23:23 +0200 Subject: [PATCH 18/22] #1960 enhance_derive_extreme_event: update man pages --- man/derive_extreme_event.Rd | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/man/derive_extreme_event.Rd b/man/derive_extreme_event.Rd index 03d9217e4e..21b4e90564 100644 --- a/man/derive_extreme_event.Rd +++ b/man/derive_extreme_event.Rd @@ -109,7 +109,7 @@ added as new observations. Add the first available record from \code{events} for each by group as new records, all variables of the selected observation are kept. It can be used for selecting the extreme observation from a series of user-defined events. -This distinguish \code{derive_extreme_event()} from \code{derive_extreme_records()}, +This distinguishes \code{derive_extreme_event()} from \code{derive_extreme_records()}, where extreme records are derived based on certain order of existing variables. } @@ -192,7 +192,7 @@ derive_extreme_event( ) ) -# use different mode by event +# Use different mode by event adhy <- tribble( ~USUBJID, ~AVISITN, ~CRIT1FL, "1", 1, "Y", @@ -230,7 +230,9 @@ derive_extreme_event( ) ) -# derive confirmed best overall response (using event_joined()) +# Derive confirmed best overall response (using event_joined()) +# CR - complete response, PR - partial response, SD - stable disease +# NE - not evaluable, PD - progressive disease adsl <- tribble( ~USUBJID, ~TRTSDTC, "1", "2020-01-01", @@ -306,7 +308,7 @@ derive_extreme_event( ), event_joined( description = paste( - "CR needs to be confirmed by a second CR or PR at least 28 days later,", + "PR needs to be confirmed by a second CR or PR at least 28 days later,", "at most one NE is acceptable between the two assessments" ), join_vars = exprs(AVALC, ADT), From dcf2d7a2f49fffcc0466cf4b9c6b166ea9d5cafe Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Wed, 30 Aug 2023 16:29:20 +0200 Subject: [PATCH 19/22] #1960 enhance_derive_extreme_event: remove docs/pkgdown.yml --- docs/pkgdown.yml | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 docs/pkgdown.yml diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml deleted file mode 100644 index 57e14ca733..0000000000 --- a/docs/pkgdown.yml +++ /dev/null @@ -1,25 +0,0 @@ -pandoc: 3.1.1 -pkgdown: 2.0.7 -pkgdown_sha: ~ -articles: - admiral: admiral.html - adsl: adsl.html - bds_exposure: bds_exposure.html - bds_finding: bds_finding.html - bds_tte: bds_tte.html - contribution_model: contribution_model.html - faq: faq.html - generic: generic.html - higher_order: higher_order.html - hys_law: hys_law.html - imputation: imputation.html - lab_grading: lab_grading.html - occds: occds.html - pk_adnca: pk_adnca.html - queries_dataset: queries_dataset.html - questionnaires: questionnaires.html - visits_periods: visits_periods.html -last_built: 2023-08-15T10:41Z -urls: - reference: https://pharmaverse.github.io/admiral/cran-release/reference/ - article: https://pharmaverse.github.io/admiral/cran-release/articles/ From 65f1c78ca5865fa630feb89577fdb77b174092a7 Mon Sep 17 00:00:00 2001 From: "Bundfuss, Stefan {MDBB~Basel}" Date: Wed, 30 Aug 2023 16:37:40 +0200 Subject: [PATCH 20/22] #1960 enhance_derive_extreme_event: style files --- tests/testthat/test-user_utils.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-user_utils.R b/tests/testthat/test-user_utils.R index 48ea59655e..25a0f7ab13 100644 --- a/tests/testthat/test-user_utils.R +++ b/tests/testthat/test-user_utils.R @@ -228,7 +228,7 @@ test_that("print_named_list Test 18: named list with unamed list", { expect_snapshot( print_named_list(list( list_item = list("Hello World!", expr(universe), list(42)), - another_one = ymd("2020-02-02")) - ) + another_one = ymd("2020-02-02") + )) ) }) From 78240523f1b1480751e8531ea2bebdc93b7a615a Mon Sep 17 00:00:00 2001 From: Zelos Zhu Date: Wed, 30 Aug 2023 18:33:45 +0000 Subject: [PATCH 21/22] chore: #1960 properly formatted testthat --- tests/testthat/test-user_utils.R | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/testthat/test-user_utils.R b/tests/testthat/test-user_utils.R index 25a0f7ab13..a3ef1d427c 100644 --- a/tests/testthat/test-user_utils.R +++ b/tests/testthat/test-user_utils.R @@ -36,8 +36,9 @@ test_that("convert_blanks_to_na Test 3: blank strings are turned into `NA` insid }) +# convert_blanks_to_na.list ---- ## Test 4: `convert_blanks_to_na.list` produces a lists ---- -test_that("convert_blanks_to_na Test 4: `convert_blanks_to_na.list` produces a lists", { +test_that("convert_blanks_to_na.list Test 4: `convert_blanks_to_na.list` produces a lists", { x <- c("", "", "") expected_output <- lapply(x, convert_blanks_to_na) actual_output <- convert_blanks_to_na.list(x) @@ -45,9 +46,9 @@ test_that("convert_blanks_to_na Test 4: `convert_blanks_to_na.list` produces a l expect_equal(expected_output, actual_output) }) -# Test 5: convert_na_to_blanks Test 5---- +# convert_na_to_blanks ---- ## Test 5: `NA` strings are turned into blank ---- -test_that("convert_blanks_to_na Test 5: `NA` strings are turned into blank ", { +test_that("convert_na_to_blanks Test 5: `NA` strings are turned into blank ", { expect_identical( convert_na_to_blanks(c("a", NA, "b")), c("a", "", "b") @@ -55,7 +56,7 @@ test_that("convert_blanks_to_na Test 5: `NA` strings are turned into blank ", { }) ## Test 6: attributes are preserved when converting `NA` to blanks ---- -test_that("convert_blanks_to_na Test 6: attributes are preserved when converting `NA` to blanks", { +test_that("convert_na_to_blanks Test 6: attributes are preserved when converting `NA` to blanks", { input <- structure(letters, names = rev(letters), label = "Letters") input[c(1, 9, 23)] <- NA_character_ output <- convert_na_to_blanks(input) @@ -64,8 +65,9 @@ test_that("convert_blanks_to_na Test 6: attributes are preserved when converting expect_identical(names(output), rev(letters)) }) +# convert_na_to_blanks.data.frame ---- ## Test 7: `NA` are turned into blank strings inside data frames ---- -test_that("convert_blanks_to_na Test 7: `NA` are turned into blank strings inside data frames", { +test_that("convert_na_to_blanks.data.frame Test 7: `NA` are turned into blank strings inside data frames", { input <- tibble::tibble( a = structure(c("a", "b", NA, "c"), label = "A"), b = structure(c(1, NA, 21, 9), label = "B"), @@ -200,7 +202,6 @@ test_that("print.source Test 15: `source` objects containing `data.frame`", { }) # print_named_list ---- -## Test 16 print_named_list ---- ## Test 16: named list ---- test_that("print_named_list Test 16: named list", { expect_identical( From ab3b8cfb4f7ada90ef0330ba03cad6d9713cc60a Mon Sep 17 00:00:00 2001 From: Zelos Zhu Date: Wed, 30 Aug 2023 19:21:29 +0000 Subject: [PATCH 22/22] chore: #1960 fix lint --- tests/testthat/test-user_utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-user_utils.R b/tests/testthat/test-user_utils.R index a3ef1d427c..5f6d44b060 100644 --- a/tests/testthat/test-user_utils.R +++ b/tests/testthat/test-user_utils.R @@ -67,7 +67,7 @@ test_that("convert_na_to_blanks Test 6: attributes are preserved when converting # convert_na_to_blanks.data.frame ---- ## Test 7: `NA` are turned into blank strings inside data frames ---- -test_that("convert_na_to_blanks.data.frame Test 7: `NA` are turned into blank strings inside data frames", { +test_that("convert_na_to_blanks.data.frame Test 7: `NA` are turned into blank strings inside data frames", { # nolint input <- tibble::tibble( a = structure(c("a", "b", NA, "c"), label = "A"), b = structure(c(1, NA, 21, 9), label = "B"),