diff --git a/NAMESPACE b/NAMESPACE index 8e9b12453..9bab92b1c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -100,6 +100,7 @@ export(derive_var_trtdurd) export(derive_var_trtemfl) export(derive_vars_aage) export(derive_vars_atc) +export(derive_vars_cat) export(derive_vars_computed) export(derive_vars_crit_flag) export(derive_vars_dt) diff --git a/NEWS.md b/NEWS.md index 07b2f5c5e..7bb95dc68 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,8 @@ ## New Features +- New function `derive_vars_cat()` for deriving pairs of variables or more, e.g. +`AVALCATx` & `AVALCAxN`. (#2480) - New function `derive_vars_crit_flag()` for deriving criterion flag variables (`CRITy`, `CRITyFL`, `CRITyFLN`). (#2468) diff --git a/R/derive_vars_cat.R b/R/derive_vars_cat.R new file mode 100644 index 000000000..7e0c12aab --- /dev/null +++ b/R/derive_vars_cat.R @@ -0,0 +1,258 @@ +#' Derive Categorization Variables Like `AVALCATy` and `AVALCAyN` +#' @param dataset +#' `r roxygen_param_dataset(expected_vars = c("by_vars", "definition"))` +#' @param definition List of expressions created by `exprs()`. +#' Must be in rectangular format and specified using the same syntax as when creating +#' a `tibble` using the `tribble()` function. +#' The `definition` object will be converted to a `tibble` using `tribble()` inside this function. +#' +#' Must contain: +#' - the column `condition` which will be converted to a logical expression and +#' will be used on the `dataset` input. +#' - at least one additional column with the new column name and +#' the category value(s) used by the logical expression. +#' - the column specified in `by_vars` (if `by_vars` is specified) +#' +#' e.g. if `by_vars` is not specified: +#' +#' ```{r} +#' #| eval: false +#' exprs(~condition, ~AVALCAT1, ~AVALCA1N, +#' AVAL >= 140, ">=140 cm", 1, +#' AVAL < 140, "<140 cm", 2) +#' ``` +#' +#' e.g. if `by_vars` is specified as `exprs(VSTEST)`: +#' +#' ```{r} +#' #| eval: false +#' exprs(~VSTEST, ~condition, ~AVALCAT1, ~AVALCA1N, +#' "Height", AVAL >= 140, ">=140 cm", 1, +#' "Height", AVAL < 140, "<140 cm", 2) +#' ``` +#' +#' @param by_vars list of expressions with one element. `NULL` by default. +#' Allows for specifying by groups, e.g. `exprs(PARAMCD)`. +#' Variable must be present in both `dataset` and `definition`. +#' The conditions in `definition` are applied only to those records that match `by_vars`. +#' The categorization variables are set to `NA` for records +#' not matching any of the by groups in `definition`. +#' +#' +#' @details +#' If conditions are overlapping, the row order of `definitions` must be carefully considered. +#' The **first** match will determine the category. +#' i.e. if +#' +#' `AVAL = 155` +#' +#' and the `definition` is: +#' +#' ```{r} +#' #| eval: false +#' definition <- exprs( +#' ~VSTEST, ~condition, ~AVALCAT1, ~AVALCA1N, +#' "Height", AVAL > 170, ">170 cm", 1, +#' "Height", AVAL <= 170, "<=170 cm", 2, +#' "Height", AVAL <= 160, "<=160 cm", 3 +#' ) +#' ``` +#' then `AVALCAT1` will be `"<=170 cm"`, as this is the first match for `AVAL`. +#' If you specify: +#' +#' ```{r} +#' #| eval: false +#' definition <- exprs( +#' ~VSTEST, ~condition, ~AVALCAT1, ~AVALCA1N, +#' "Height", AVAL <= 160, "<=160 cm", 3, +#' "Height", AVAL <= 170, "<=170 cm", 2, +#' "Height", AVAL > 170, ">170 cm", 1 +#' ) +#' ``` +#' +#' Then `AVAL <= 160` will lead to `AVALCAT1 == "<=160 cm"`, +#' `AVAL` in-between `160` and `170` will lead to `AVALCAT1 == "<=170 cm"`, +#' and `AVAL <= 170` will lead to `AVALCAT1 == ">170 cm"`. +#' +#' However, we suggest to be more explicit when defining the `condition`, to avoid overlap. +#' In this case, the middle condition should be: +#' `AVAL <= 170 & AVAL > 160` +#' +#' @return The input dataset with the new variables defined in `definition` added +#' @family der_gen +#' @keywords der_gen +#' @export +#' +#' @examples +#' library(dplyr) +#' library(tibble) +#' +#' advs <- tibble::tribble( +#' ~USUBJID, ~VSTEST, ~AVAL, +#' "01-701-1015", "Height", 147.32, +#' "01-701-1015", "Weight", 53.98, +#' "01-701-1023", "Height", 162.56, +#' "01-701-1023", "Weight", NA, +#' "01-701-1028", "Height", NA, +#' "01-701-1028", "Weight", NA, +#' "01-701-1033", "Height", 175.26, +#' "01-701-1033", "Weight", 88.45 +#' ) +#' +#' definition <- exprs( +#' ~condition, ~AVALCAT1, ~AVALCA1N, ~NEWCOL, +#' VSTEST == "Height" & AVAL > 160, ">160 cm", 1, "extra1", +#' VSTEST == "Height" & AVAL <= 160, "<=160 cm", 2, "extra2" +#' ) +#' derive_vars_cat( +#' dataset = advs, +#' definition = definition +#' ) +#' +#' # Using by_vars: +#' definition2 <- exprs( +#' ~VSTEST, ~condition, ~AVALCAT1, ~AVALCA1N, +#' "Height", AVAL > 160, ">160 cm", 1, +#' "Height", AVAL <= 160, "<=160 cm", 2, +#' "Weight", AVAL > 70, ">70 kg", 1, +#' "Weight", AVAL <= 70, "<=70 kg", 2 +#' ) +#' +#' derive_vars_cat( +#' dataset = advs, +#' definition = definition2, +#' by_vars = exprs(VSTEST) +#' ) +#' +#' # With three conditions: +#' definition3 <- exprs( +#' ~VSTEST, ~condition, ~AVALCAT1, ~AVALCA1N, +#' "Height", AVAL > 170, ">170 cm", 1, +#' "Height", AVAL <= 170 & AVAL > 160, "<=170 cm", 2, +#' "Height", AVAL <= 160, "<=160 cm", 3 +#' ) +#' +#' derive_vars_cat( +#' dataset = advs, +#' definition = definition3, +#' by_vars = exprs(VSTEST) +#' ) +#' +#' # Let's derive both the MCRITyML and the MCRITyMN variables +#' adlb <- tibble::tribble( +#' ~USUBJID, ~PARAM, ~AVAL, ~AVALU, ~ANRHI, +#' "01-701-1015", "ALT", 150, "U/L", 40, +#' "01-701-1023", "ALT", 70, "U/L", 40, +#' "01-701-1036", "ALT", 130, "U/L", 40, +#' "01-701-1048", "ALT", 30, "U/L", 40, +#' "01-701-1015", "AST", 50, "U/L", 35 +#' ) +#' +#' definition_mcrit <- exprs( +#' ~PARAM, ~condition, ~MCRIT1ML, ~MCRIT1MN, +#' "ALT", AVAL <= ANRHI, "<=ANRHI", 1, +#' "ALT", ANRHI < AVAL & AVAL <= 3 * ANRHI, ">1-3*ANRHI", 2, +#' "ALT", 3 * ANRHI < AVAL, ">3*ANRHI", 3 +#' ) +#' +#' adlb %>% +#' derive_vars_cat( +#' definition = definition_mcrit, +#' by_vars = exprs(PARAM) +#' ) +derive_vars_cat <- function(dataset, + definition, + by_vars = NULL) { + assert_expr_list(definition) + assert_vars(by_vars, optional = TRUE) + if (length(by_vars) > 1) { + cli_abort("{.arg by_vars} must contain just one variable, e.g. {.code exprs(PARAMCD)}") + } + + assert_data_frame(dataset, + required_vars = c( + admiraldev::extract_vars(definition) %>% unique(), + by_vars + ) + ) + + # transform definition to tibble + names(definition) <- NULL + definition <- tryCatch( + { + tibble::tribble(!!!definition) + }, + error = function(e) { + # Catch the error and append your own message + cli_abort( + c( + paste( + "Failed to convert {.arg definition} to {.cls tibble}.", + "{.arg definition} should be specified similarly to how you would", + "specify a {.cls tibble} using the {.fun tibble::tribble} function so it", + "can be converted to {.cls tibble} using {.fun tibble::tribble}." + ), + e$message + ) + ) + } + ) + assert_data_frame(definition, required_vars = c(exprs(condition), by_vars)) + if (!is.null(by_vars)) { + # add condition + definition <- definition %>% + mutate( + condition = extend_condition(as.character(condition), + as.character(by_vars), + is = !!sym(as.character(by_vars)) + ) %>% + parse_exprs() + ) %>% + select(-by_vars[[1]]) + } + + # extract new variable names and conditions + new_col_names <- names(definition)[!names(definition) == "condition"] + condition <- definition[["condition"]] + + # warn if new variables already exist + if (any(new_col_names %in% names(dataset))) { + cli_warn(paste( + "Column(s) in {.arg definition} already exist in {.arg dataset}.", + "Did you forget to specify {.arg by_vars},", + "or are you rerunning your code?" + )) + } + + # (re)apply the function for each new variable name and iteratively derive the categories + new_dataset <- reduce(new_col_names, function(.data, col_name) { + # extract conditions + values <- definition[[col_name]] + + .data %>% + mutate(!!sym(col_name) := eval(rlang::call2( + "case_when", + !!!map2(condition, values, ~ expr(!!.x ~ !!.y)) + ))) + }, .init = dataset) + + return(new_dataset) +} + +#' Extend a condition string by adding a new condition based on a variable and its value +#' +#' This internal helper function extends a condition string by appending a new condition +#' that checks if a variable equals a specific value. +#' +#' @param cond A character string representing an existing condition. +#' @param var A character string representing the name of the variable to check. +#' @param is A character string representing the value the variable should be equal to. +#' +#' @return A character string representing the extended condition. +#' @examples +#' # Extend an existing condition to include a check for 'AGE == "30"' +#' admiral:::extend_condition("SEX == 'M'", "AGE", "30") +#' @keywords internal +extend_condition <- function(cond, var, is) { + paste(cond, " & ", var, " == '", is, "'", sep = "") +} diff --git a/inst/templates/ad_adeg.R b/inst/templates/ad_adeg.R index ea0983e9e..b9ed092c0 100644 --- a/inst/templates/ad_adeg.R +++ b/inst/templates/ad_adeg.R @@ -31,67 +31,45 @@ eg <- convert_blanks_to_na(eg) # Assign PARAMCD, PARAM, and PARAMN param_lookup <- tibble::tribble( - ~EGTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, - "ECGINT", "EGINTP", "ECG Interpretation", 1, - "HR", "HR", "Heart Rate (beats/min)", 2, - "RR", "RR", "RR Duration (msec)", 3, - "RRR", "RRR", "RR Duration Rederived (msec)", 4, - "QT", "QT", "QT Duration (msec)", 10, - "QTCBR", "QTCBR", "QTcB - Bazett's Correction Formula Rederived (msec)", 11, - "QTCFR", "QTCFR", "QTcF - Fridericia's Correction Formula Rederived (msec)", 12, - "QTLCR", "QTLCR", "QTlc - Sagie's Correction Formula Rederived (msec)", 13, + ~EGTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, + "ECGINT", "EGINTP", "ECG Interpretation", 1, + "HR", "HR", "Heart Rate (beats/min)", 2, + "RR", "RR", "RR Duration (msec)", 3, + "RRR", "RRR", "RR Duration Rederived (msec)", 4, + "QT", "QT", "QT Duration (msec)", 10, + "QTCBR", "QTCBR", "QTcB - Bazett's Correction Formula Rederived (msec)", 11, + "QTCFR", "QTCFR", "QTcF - Fridericia's Correction Formula Rederived (msec)", 12, + "QTLCR", "QTLCR", "QTlc - Sagie's Correction Formula Rederived (msec)", 13, ) range_lookup <- tibble::tribble( ~PARAMCD, ~ANRLO, ~ANRHI, - "EGINTP", NA, NA, - "HR", 40, 100, - "RR", 600, 1500, - "QT", 350, 450, - "RRR", 600, 1500, - "QTCBR", 350, 450, - "QTCFR", 350, 450, - "QTLCR", 350, 450, + "EGINTP", NA, NA, + "HR", 40, 100, + "RR", 600, 1500, + "QT", 350, 450, + "RRR", 600, 1500, + "QTCBR", 350, 450, + "QTCFR", 350, 450, + "QTLCR", 350, 450 ) -# ASSIGN AVALCAT1 -avalcat_lookup <- tibble::tribble( - ~AVALCA1N, ~AVALCAT1, - 1, "<= 450 msec", - 2, ">450<=480 msec", - 3, ">480<=500 msec", - 4, ">500 msec" +# Assign AVALCAx +avalcax_lookup <- exprs( + ~condition, ~AVALCAT1, ~AVALCA1N, + startsWith(PARAMCD, "QT") & AVAL <= 450, "<= 450 msec", 1, + startsWith(PARAMCD, "QT") & AVAL > 450 & AVAL <= 480, ">450<=480 msec", 2, + startsWith(PARAMCD, "QT") & AVAL > 480 & AVAL <= 500, ">480<=500 msec", 3, + startsWith(PARAMCD, "QT") & AVAL > 500, ">500 msec", 4 ) - -# ASSIGN CHGCAT1 -chgcat_lookup <- tibble::tribble( - ~CHGCAT1N, ~CHGCAT1, - 1, "<= 30 msec", - 2, ">30<=60 msec", - 3, ">60 msec" +# Assign CHGCAx +chgcax_lookup <- exprs( + ~condition, ~CHGCAT1, ~CHGCAT1N, + startsWith(PARAMCD, "QT") & CHG <= 30, "<= 30 msec", 1, + startsWith(PARAMCD, "QT") & CHG > 30 & CHG <= 60, ">30<=60 msec", 2, + startsWith(PARAMCD, "QT") & CHG > 60, ">60 msec", 3 ) -# Here are some examples of how you can create your own functions that -# operates on vectors, which can be used in `mutate()`. Info then used for -# lookup table -format_avalca1n <- function(paramcd, aval) { - case_when( - str_detect(paramcd, "QT") & aval <= 450 ~ 1, - str_detect(paramcd, "QT") & aval > 450 & aval <= 480 ~ 2, - str_detect(paramcd, "QT") & aval > 480 & aval <= 500 ~ 3, - str_detect(paramcd, "QT") & aval > 500 ~ 4 - ) -} - -format_chgcat1n <- function(paramcd, chg) { - case_when( - str_detect(paramcd, "QT") & chg <= 30 ~ 1, - str_detect(paramcd, "QT") & chg > 30 & chg <= 60 ~ 2, - str_detect(paramcd, "QT") & chg > 60 ~ 3 - ) -} - - # Derivations ---- # Get list of ADSL vars required for derivations @@ -316,14 +294,13 @@ adeg <- adeg %>% check_type = "error" ) %>% # Derive AVALCA1N and AVALCAT1 - mutate(AVALCA1N = format_avalca1n(param = PARAMCD, aval = AVAL)) %>% - derive_vars_merged( - dataset_add = avalcat_lookup, - by_vars = exprs(AVALCA1N) + derive_vars_cat( + definition = avalcax_lookup ) %>% # Derive CHGCAT1N and CHGCAT1 - mutate(CHGCAT1N = format_chgcat1n(param = PARAMCD, chg = CHG)) %>% - derive_vars_merged(dataset_add = chgcat_lookup, by_vars = exprs(CHGCAT1N)) %>% + derive_vars_cat( + definition = chgcax_lookup + ) %>% # Derive PARAM and PARAMN derive_vars_merged( dataset_add = select(param_lookup, -EGTESTCD), diff --git a/inst/templates/ad_adex.R b/inst/templates/ad_adex.R index ca7bb17e3..ac0f980ca 100644 --- a/inst/templates/ad_adex.R +++ b/inst/templates/ad_adex.R @@ -257,42 +257,42 @@ adex <- adex %>% # Assign PARAMCD, PARAM, and PARAMN # ---- Lookup tables ---- param_lookup <- tibble::tribble( - ~PARAMCD, ~PARAM, ~PARAMN, - "DURD", "Study drug duration during constant dosing interval (days)", 1, - "DOSE", "Dose administered during constant dosing interval (mg)", 2, - "PLDOSE", "Planned dose during constant dosing interval (mg)", 3, - "ADJ", "Dose adjusted during constant dosing interval", 4, - "ADJAE", "Dose adjusted due to AE during constant dosing interval", 5, - "TDURD", "Overall duration (days)", 7, - "TDOSE", "Total dose administered (mg)", 8, - "AVDDSE", "Average daily dose administered (mg/mg)", 10, - "TPDOSE", "Total planned dose (mg)", 11, - "TADJ", "Dose adjusted during study", 13, - "TADJAE", "Dose adjusted during study due to AE", 14, - "PDURD", "Overall duration in W2-W24 (days)", 19, - "PDOSE", "Total dose administered in W2-W2 (mg)4", 20, - "PPDOSE", "Total planned dose in W2-W24 (mg)", 21, - "PAVDDSE", "Average daily dose administered in W2-W24 (mg)", 23, - "PADJ", "Dose adjusted during W2-W24", 24, - "PADJAE", "Dose adjusted in W2-W24 due to AE", 25, - "TDOSINT", "Overall dose intensity (%)", 90, - "PDOSINT", "W2-24 dose intensity (%)", 91 + ~PARAMCD, ~PARAM, ~PARAMN, + "DURD", "Study drug duration during constant dosing interval (days)", 1, + "DOSE", "Dose administered during constant dosing interval (mg)", 2, + "PLDOSE", "Planned dose during constant dosing interval (mg)", 3, + "ADJ", "Dose adjusted during constant dosing interval", 4, + "ADJAE", "Dose adjusted due to AE during constant dosing interval", 5, + "TDURD", "Overall duration (days)", 7, + "TDOSE", "Total dose administered (mg)", 8, + "AVDDSE", "Average daily dose administered (mg/mg)", 10, + "TPDOSE", "Total planned dose (mg)", 11, + "TADJ", "Dose adjusted during study", 13, + "TADJAE", "Dose adjusted during study due to AE", 14, + "PDURD", "Overall duration in W2-W24 (days)", 19, + "PDOSE", "Total dose administered in W2-W2 (mg)4", 20, + "PPDOSE", "Total planned dose in W2-W24 (mg)", 21, + "PAVDDSE", "Average daily dose administered in W2-W24 (mg)", 23, + "PADJ", "Dose adjusted during W2-W24", 24, + "PADJAE", "Dose adjusted in W2-W24 due to AE", 25, + "TDOSINT", "Overall dose intensity (%)", 90, + "PDOSINT", "W2-24 dose intensity (%)", 91 ) -# User defined functions ---- -# Derive AVALCAT1 -# Here are some examples of how you can create your own functions that -# operates on vectors, which can be used in `mutate()`. -format_avalcat1 <- function(param, aval) { - case_when( - param %in% c("TDURD", "PDURD") & aval < 30 & !is.na(aval) ~ "< 30 days", - param %in% c("TDURD", "PDURD") & aval >= 30 & aval < 90 ~ ">= 30 and < 90 days", - param %in% c("TDURD", "PDURD") & aval >= 90 ~ ">=90 days", - param %in% c("TDOSE", "PDOSE") & aval < 100 & !is.na(aval) ~ "< 100 mg", - param %in% c("TDOSE", "PDOSE") & aval >= 100 ~ ">= 100 mg", - TRUE ~ NA_character_ - ) -} +# Assign AVALCATx +avalcax_lookup <- exprs( + ~PARAMCD, ~condition, ~AVALCAT1, + "TDURD", AVAL >= 90, ">= 90 days", + "TDURD", AVAL >= 30 & AVAL < 90, ">= 30 and < 90 days", + "TDURD", AVAL < 30, "< 30 days", + "PDURD", AVAL >= 90, ">= 90 days", + "PDURD", AVAL >= 30 & AVAL < 90, ">= 30 and < 90 days", + "PDURD", AVAL < 30, "< 30 days", + "TDOSE", AVAL < 100, "< 100 mg", + "TDOSE", AVAL >= 100, ">= 100 mg", + "PDOSE", AVAL < 100, "< 100 mg", + "PDOSE", AVAL >= 100, ">= 100 mg" +) adex <- adex %>% # Add PARAMN and PARAM, AVALU @@ -301,7 +301,10 @@ adex <- adex %>% by_vars = exprs(PARAMCD) ) %>% # Derive AVALCATx - mutate(AVALCAT1 = format_avalcat1(param = PARAMCD, aval = AVAL)) %>% + derive_vars_cat( + definition = avalcax_lookup, + by_vars = exprs(PARAMCD) + ) %>% # Calculate ASEQ derive_var_obs_number( new_var = ASEQ, diff --git a/inst/templates/ad_adlb.R b/inst/templates/ad_adlb.R index b04807ec8..d6ad0f5ec 100644 --- a/inst/templates/ad_adlb.R +++ b/inst/templates/ad_adlb.R @@ -29,54 +29,54 @@ lb <- convert_blanks_to_na(lb) # Assign PARAMCD, PARAM, and PARAMN param_lookup <- tibble::tribble( - ~LBTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, - "ALB", "ALB", "Albumin (g/L)", 1, - "ALP", "ALKPH", "Alkaline Phosphatase (U/L)", 2, - "ALT", "ALT", "Alanine Aminotransferase (U/L)", 3, - "ANISO", "ANISO", "Anisocytes", 4, - "AST", "AST", "Aspartate Aminotransferase (U/L)", 5, - "BASO", "BASO", "Basophils Abs (10^9/L)", 6, - "BASOLE", "BASOLE", "Basophils/Leukocytes (FRACTION)", 7, - "BILI", "BILI", "Bilirubin (umol/L)", 8, - "BUN", "BUN", "Blood Urea Nitrogen (mmol/L)", 9, - "CA", "CA", "Calcium (mmol/L)", 10, - "CHOL", "CHOLES", "Cholesterol (mmol/L)", 11, - "CK", "CK", "Creatinine Kinase (U/L)", 12, - "CL", "CL", "Chloride (mmol/L)", 13, - "COLOR", "COLOR", "Color", 14, - "CREAT", "CREAT", "Creatinine (umol/L)", 15, - "EOS", "EOS", "Eosinophils (10^9/L)", 16, - "EOSLE", "EOSLE", "Eosinophils/Leukocytes (FRACTION)", 17, - "GGT", "GGT", "Gamma Glutamyl Transferase (U/L)", 18, - "GLUC", "GLUC", "Glucose (mmol/L)", 19, - "HBA1C", "HBA1C", "Hemoglobin A1C (1)", 20, - "HCT", "HCT", "Hematocrit (1)", 21, - "HGB", "HGB", "Hemoglobin (mmol/L)", 22, - "K", "POTAS", "Potassium (mmol/L)", 23, - "KETONES", "KETON", "Ketones", 24, - "LYM", "LYMPH", "Lymphocytes Abs (10^9/L)", 25, - "LYMLE", "LYMPHLE", "Lymphocytes/Leukocytes (FRACTION)", 26, - "MACROCY", "MACROC", "Macrocytes", 27, - "MCH", "MCH", "Ery. Mean Corpuscular Hemoglobin (fmol(Fe))", 28, - "MCHC", "MCHC", "Ery. Mean Corpuscular HGB Concentration (mmol/L)", 29, - "MCV", "MCV", "Ery. Mean Corpuscular Volume (f/L)", 30, - "MICROCY", "MICROC", "Microcytes", 31, - "MONO", "MONO", "Monocytes (10^9/L)", 32, - "MONOLE", "MONOLE", "Monocytes/Leukocytes (FRACTION)", 33, - "PH", "PH", "pH", 34, - "PHOS", "PHOS", "Phosphate (mmol/L)", 35, - "PLAT", "PLAT", "Platelet (10^9/L)", 36, - "POIKILO", "POIKIL", "Poikilocytes", 37, - "POLYCHR", "POLYCH", "Polychromasia", 38, - "PROT", "PROT", "Protein (g/L)", 39, - "RBC", "RBC", "Erythrocytes (TI/L)", 40, - "SODIUM", "SODIUM", "Sodium (mmol/L)", 41, - "SPGRAV", "SPGRAV", "Specific Gravity", 42, - "TSH", "TSH", "Thyrotropin (mU/L)", 43, - "URATE", "URATE", "Urate (umol/L)", 44, - "UROBIL", "UROBIL", "Urobilinogen", 45, - "VITB12", "VITB12", "Vitamin B12 (pmol/L)", 46, - "WBC", "WBC", "Leukocytes (10^9/L)", 47 + ~LBTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, + "ALB", "ALB", "Albumin (g/L)", 1, + "ALP", "ALKPH", "Alkaline Phosphatase (U/L)", 2, + "ALT", "ALT", "Alanine Aminotransferase (U/L)", 3, + "ANISO", "ANISO", "Anisocytes", 4, + "AST", "AST", "Aspartate Aminotransferase (U/L)", 5, + "BASO", "BASO", "Basophils Abs (10^9/L)", 6, + "BASOLE", "BASOLE", "Basophils/Leukocytes (FRACTION)", 7, + "BILI", "BILI", "Bilirubin (umol/L)", 8, + "BUN", "BUN", "Blood Urea Nitrogen (mmol/L)", 9, + "CA", "CA", "Calcium (mmol/L)", 10, + "CHOL", "CHOLES", "Cholesterol (mmol/L)", 11, + "CK", "CK", "Creatinine Kinase (U/L)", 12, + "CL", "CL", "Chloride (mmol/L)", 13, + "COLOR", "COLOR", "Color", 14, + "CREAT", "CREAT", "Creatinine (umol/L)", 15, + "EOS", "EOS", "Eosinophils (10^9/L)", 16, + "EOSLE", "EOSLE", "Eosinophils/Leukocytes (FRACTION)", 17, + "GGT", "GGT", "Gamma Glutamyl Transferase (U/L)", 18, + "GLUC", "GLUC", "Glucose (mmol/L)", 19, + "HBA1C", "HBA1C", "Hemoglobin A1C (1)", 20, + "HCT", "HCT", "Hematocrit (1)", 21, + "HGB", "HGB", "Hemoglobin (mmol/L)", 22, + "K", "POTAS", "Potassium (mmol/L)", 23, + "KETONES", "KETON", "Ketones", 24, + "LYM", "LYMPH", "Lymphocytes Abs (10^9/L)", 25, + "LYMLE", "LYMPHLE", "Lymphocytes/Leukocytes (FRACTION)", 26, + "MACROCY", "MACROC", "Macrocytes", 27, + "MCH", "MCH", "Ery. Mean Corpuscular Hemoglobin (fmol(Fe))", 28, + "MCHC", "MCHC", "Ery. Mean Corpuscular HGB Concentration (mmol/L)", 29, + "MCV", "MCV", "Ery. Mean Corpuscular Volume (f/L)", 30, + "MICROCY", "MICROC", "Microcytes", 31, + "MONO", "MONO", "Monocytes (10^9/L)", 32, + "MONOLE", "MONOLE", "Monocytes/Leukocytes (FRACTION)", 33, + "PH", "PH", "pH", 34, + "PHOS", "PHOS", "Phosphate (mmol/L)", 35, + "PLAT", "PLAT", "Platelet (10^9/L)", 36, + "POIKILO", "POIKIL", "Poikilocytes", 37, + "POLYCHR", "POLYCH", "Polychromasia", 38, + "PROT", "PROT", "Protein (g/L)", 39, + "RBC", "RBC", "Erythrocytes (TI/L)", 40, + "SODIUM", "SODIUM", "Sodium (mmol/L)", 41, + "SPGRAV", "SPGRAV", "Specific Gravity", 42, + "TSH", "TSH", "Thyrotropin (mU/L)", 43, + "URATE", "URATE", "Urate (umol/L)", 44, + "UROBIL", "UROBIL", "Urobilinogen", 45, + "VITB12", "VITB12", "Vitamin B12 (pmol/L)", 46, + "WBC", "WBC", "Leukocytes (10^9/L)", 47 ) @@ -241,25 +241,25 @@ adlb <- adlb %>% # ATOXDSCL and ATOXDSCH hold terms defined by NCI-CTCAEv4. # See (https://pharmaverse.github.io/admiral/articles/lab_grading.html#implement_ctcv4) grade_lookup <- tibble::tribble( - ~PARAMCD, ~ATOXDSCL, ~ATOXDSCH, - "ALB", "Hypoalbuminemia", NA_character_, - "ALKPH", NA_character_, "Alkaline phosphatase increased", - "ALT", NA_character_, "Alanine aminotransferase increased", - "AST", NA_character_, "Aspartate aminotransferase increased", - "BILI", NA_character_, "Blood bilirubin increased", - "CA", "Hypocalcemia", "Hypercalcemia", - "CHOLES", NA_character_, "Cholesterol high", - "CK", NA_character_, "CPK increased", - "CREAT", NA_character_, "Creatinine increased", - "GGT", NA_character_, "GGT increased", - "GLUC", "Hypoglycemia", "Hyperglycemia", - "HGB", "Anemia", "Hemoglobin increased", - "POTAS", "Hypokalemia", "Hyperkalemia", - "LYMPH", "CD4 lymphocytes decreased", NA_character_, - "PHOS", "Hypophosphatemia", NA_character_, - "PLAT", "Platelet count decreased", NA_character_, - "SODIUM", "Hyponatremia", "Hypernatremia", - "WBC", "White blood cell decreased", "Leukocytosis", + ~PARAMCD, ~ATOXDSCL, ~ATOXDSCH, + "ALB", "Hypoalbuminemia", NA_character_, + "ALKPH", NA_character_, "Alkaline phosphatase increased", + "ALT", NA_character_, "Alanine aminotransferase increased", + "AST", NA_character_, "Aspartate aminotransferase increased", + "BILI", NA_character_, "Blood bilirubin increased", + "CA", "Hypocalcemia", "Hypercalcemia", + "CHOLES", NA_character_, "Cholesterol high", + "CK", NA_character_, "CPK increased", + "CREAT", NA_character_, "Creatinine increased", + "GGT", NA_character_, "GGT increased", + "GLUC", "Hypoglycemia", "Hyperglycemia", + "HGB", "Anemia", "Hemoglobin increased", + "POTAS", "Hypokalemia", "Hyperkalemia", + "LYMPH", "CD4 lymphocytes decreased", NA_character_, + "PHOS", "Hypophosphatemia", NA_character_, + "PLAT", "Platelet count decreased", NA_character_, + "SODIUM", "Hyponatremia", "Hypernatremia", + "WBC", "White blood cell decreased", "Leukocytosis", ) # Assign grade criteria diff --git a/inst/templates/ad_adpc.R b/inst/templates/ad_adpc.R index 850bf9808..0678b7e30 100644 --- a/inst/templates/ad_adpc.R +++ b/inst/templates/ad_adpc.R @@ -41,18 +41,6 @@ param_lookup <- tibble::tribble( "DOSE", "DOSE", "Xanomeline Patch Dose", 2, ) -# ---- User defined functions ---- - -# Here is an example of how you can create your own function that -# operates on vectors, which can be used in `mutate`. -format_avalcat1n <- function(param, aval) { - case_when( - param == "PKCONC" & aval < 1 ~ 1, - param == "PKCONC" & aval >= 1 ~ 2, - T ~ NA_real_ - ) -} - # ---- Derivations ---- # Get list of ADSL vars required for derivations diff --git a/inst/templates/ad_adpp.R b/inst/templates/ad_adpp.R index 544c976ae..a95f7e524 100644 --- a/inst/templates/ad_adpp.R +++ b/inst/templates/ad_adpp.R @@ -31,52 +31,40 @@ pp <- convert_blanks_to_na(pp) # Lookup tables ---- param_lookup <- tibble::tribble( - ~PPTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, - "AUCALL", "AUCALL", "AUC All", 1, - "AUCIFO", "AUCIFO", "AUC Infinity Obs", 2, - "AUCIFOD", "AUCIFOD", "AUC Infinity Obs Norm by Dose", 3, - "AUCINT", "AUCINT", "AUC from T1 to T2", 4, - "AUCLST", "AUCLST", "AUC to Last Nonzero Conc", 5, - "AUCPEO", "AUCPEO", "AUC %Extrapolation Obs", 6, - "CEND", "CEND", "Concentration at the end of the infusion", 7, # non Cdisc Term - "CLO", "CLO", "Total CL Obs", 8, - "CLST", "CLST", "Last Nonzero Conc", 9, - "CMAX", "CMAX", "Max Conc", 10, - "CMAXD", "CMAXD", "Max Conc Norm by Dose", 11, - "CSF", "CSF", "CSF to Plasma Ratio", 12, # non Cdisc Term - "LAMZ", "LAMZ", "Lambda z", 13, - "LAMZHL", "LAMZHL", "Half-Life Lambda z", 14, - "LAMZNPT", "LAMZNPT", "Number of Points for Lambda z", 15, - "R2ADJ", "R2ADJ", "R Squared Adjusted", 16, - "TCEND", "TCEND", "Time of CEND", 17, # non Cdisc Term - "TLST", "TLST", "Time of Last Nonzero Conc", 18, - "TMAX", "TMAX", "Time of CMAX", 19, - "VSSO", "VSSO", "Vol Dist Steady State Obs", 20, - "RCAMINT", "RCAMINT", "Ae", 21, - "RENALCL", "RENALCL", "CLR", 22 + ~PPTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, + "AUCALL", "AUCALL", "AUC All", 1, + "AUCIFO", "AUCIFO", "AUC Infinity Obs", 2, + "AUCIFOD", "AUCIFOD", "AUC Infinity Obs Norm by Dose", 3, + "AUCINT", "AUCINT", "AUC from T1 to T2", 4, + "AUCLST", "AUCLST", "AUC to Last Nonzero Conc", 5, + "AUCPEO", "AUCPEO", "AUC %Extrapolation Obs", 6, + "CEND", "CEND", "Concentration at the end of the infusion", 7, # non Cdisc Term + "CLO", "CLO", "Total CL Obs", 8, + "CLST", "CLST", "Last Nonzero Conc", 9, + "CMAX", "CMAX", "Max Conc", 10, + "CMAXD", "CMAXD", "Max Conc Norm by Dose", 11, + "CSF", "CSF", "CSF to Plasma Ratio", 12, # non Cdisc Term + "LAMZ", "LAMZ", "Lambda z", 13, + "LAMZHL", "LAMZHL", "Half-Life Lambda z", 14, + "LAMZNPT", "LAMZNPT", "Number of Points for Lambda z", 15, + "R2ADJ", "R2ADJ", "R Squared Adjusted", 16, + "TCEND", "TCEND", "Time of CEND", 17, # non Cdisc Term + "TLST", "TLST", "Time of Last Nonzero Conc", 18, + "TMAX", "TMAX", "Time of CMAX", 19, + "VSSO", "VSSO", "Vol Dist Steady State Obs", 20, + "RCAMINT", "RCAMINT", "Ae", 21, + "RENALCL", "RENALCL", "CLR", 22 ) -# ASSIGN AVALCAT1 -avalcat_lookup <- tibble::tribble( - ~PARAMCD, ~AVALCA1N, ~AVALCAT1, - "AUCALL", 1, "< 19", - "AUCALL", 2, ">= 19" +# Assign AVALCATx +avalcax_lookup <- exprs( + ~PARAMCD, ~condition, ~AVALCAT1, ~AVALCA1N, + "AUCALL", AVAL < 19, "<19", 1, + "AUCALL", AVAL >= 19, ">=19", 2 ) attr(param_lookup$PPTESTCD, "label") <- "Parameter Short Name" -# User defined functions ---- - -# Here are some examples of how you can create your own functions that -# operates on vectors, which can be used in `mutate`. -format_avalcat1n <- function(param, aval) { - case_when( - param == "AUCALL" & aval < 19 ~ 1, - param == "AUCALL" & aval >= 19 ~ 2, - TRUE ~ NA_real_ - ) -} - # Derivations ---- # Get list of ADSL vars required for derivations @@ -137,9 +125,10 @@ adpp_avisit <- adpp_aval %>% TRTA = TRT01A ) %>% ## Derive AVALCA1N and AVALCAT1 ---- - mutate(AVALCA1N = format_avalcat1n(param = PARAMCD, aval = AVAL)) %>% - derive_vars_merged(dataset_add = avalcat_lookup, by_vars = exprs(PARAMCD, AVALCA1N)) - + derive_vars_cat( + definition = avalcax_lookup, + by_vars = exprs(PARAMCD) + ) # Add all ADSL variables adpp <- adpp_avisit %>% derive_vars_merged( diff --git a/inst/templates/ad_advs.R b/inst/templates/ad_advs.R index b68ddcc39..5a6151ee1 100644 --- a/inst/templates/ad_advs.R +++ b/inst/templates/ad_advs.R @@ -29,16 +29,16 @@ vs <- convert_blanks_to_na(vs) # Assign PARAMCD, PARAM, and PARAMN param_lookup <- tibble::tribble( - ~VSTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, - "SYSBP", "SYSBP", "Systolic Blood Pressure (mmHg)", 1, - "DIABP", "DIABP", "Diastolic Blood Pressure (mmHg)", 2, - "PULSE", "PULSE", "Pulse Rate (beats/min)", 3, - "WEIGHT", "WEIGHT", "Weight (kg)", 4, - "HEIGHT", "HEIGHT", "Height (cm)", 5, - "TEMP", "TEMP", "Temperature (C)", 6, - "MAP", "MAP", "Mean Arterial Pressure (mmHg)", 7, - "BMI", "BMI", "Body Mass Index(kg/m^2)", 8, - "BSA", "BSA", "Body Surface Area(m^2)", 9 + ~VSTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, + "SYSBP", "SYSBP", "Systolic Blood Pressure (mmHg)", 1, + "DIABP", "DIABP", "Diastolic Blood Pressure (mmHg)", 2, + "PULSE", "PULSE", "Pulse Rate (beats/min)", 3, + "WEIGHT", "WEIGHT", "Weight (kg)", 4, + "HEIGHT", "HEIGHT", "Height (cm)", 5, + "TEMP", "TEMP", "Temperature (C)", 6, + "MAP", "MAP", "Mean Arterial Pressure (mmHg)", 7, + "BMI", "BMI", "Body Mass Index(kg/m^2)", 8, + "BSA", "BSA", "Body Surface Area(m^2)", 9 ) attr(param_lookup$VSTESTCD, "label") <- "Vital Signs Test Short Name" @@ -46,28 +46,18 @@ attr(param_lookup$VSTESTCD, "label") <- "Vital Signs Test Short Name" # Assign ANRLO/HI, A1LO/HI range_lookup <- tibble::tribble( ~PARAMCD, ~ANRLO, ~ANRHI, ~A1LO, ~A1HI, - "SYSBP", 90, 130, 70, 140, - "DIABP", 60, 80, 40, 90, - "PULSE", 60, 100, 40, 110, - "TEMP", 36.5, 37.5, 35, 38 + "SYSBP", 90, 130, 70, 140, + "DIABP", 60, 80, 40, 90, + "PULSE", 60, 100, 40, 110, + "TEMP", 36.5, 37.5, 35, 38 ) -# ASSIGN AVALCAT1 -avalcat_lookup <- tibble::tribble( - ~PARAMCD, ~AVALCA1N, ~AVALCAT1, - "HEIGHT", 1, ">100 cm", - "HEIGHT", 2, "<= 100 cm" -) - -# User defined functions ---- -# Here are some examples of how you can create your own functions that -# operates on vectors, which can be used in `mutate()`. -format_avalcat1n <- function(param, aval) { - case_when( - param == "HEIGHT" & aval > 140 ~ 1, - param == "HEIGHT" & aval <= 140 ~ 2 - ) -} +# Assign AVALCATx +avalcax_lookup <- exprs( + ~PARAMCD, ~condition, ~AVALCAT1, ~AVALCA1N, + "HEIGHT", AVAL > 100, ">100 cm", 1, + "HEIGHT", AVAL <= 100, "<=100 cm", 2 +) # Derivations ---- @@ -283,13 +273,16 @@ advs <- advs %>% order = exprs(PARAMCD, ADT, AVISITN, VISITNUM, ATPTN, DTYPE), check_type = "error" ) %>% - # Derive AVALCA1N and AVALCAT1 - mutate(AVALCA1N = format_avalcat1n(param = PARAMCD, aval = AVAL)) %>% - derive_vars_merged(dataset_add = avalcat_lookup, by_vars = exprs(PARAMCD, AVALCA1N)) %>% + # Define condition and categories using derive_vars_cat + derive_vars_cat( + definition = avalcax_lookup, + by_vars = exprs(PARAMCD) + ) %>% # Derive PARAM and PARAMN derive_vars_merged(dataset_add = select(param_lookup, -VSTESTCD), by_vars = exprs(PARAMCD)) + # Add all ADSL variables advs <- advs %>% derive_vars_merged( diff --git a/man/derive_var_extreme_flag.Rd b/man/derive_var_extreme_flag.Rd index 7deac8668..933f5f0af 100644 --- a/man/derive_var_extreme_flag.Rd +++ b/man/derive_var_extreme_flag.Rd @@ -268,6 +268,7 @@ General Derivation Functions for all ADaMs that returns variable appended to dat \code{\link{derive_var_merged_summary}()}, \code{\link{derive_var_obs_number}()}, \code{\link{derive_var_relative_flag}()}, +\code{\link{derive_vars_cat}()}, \code{\link{derive_vars_computed}()}, \code{\link{derive_vars_joined}()}, \code{\link{derive_vars_merged}()}, diff --git a/man/derive_var_joined_exist_flag.Rd b/man/derive_var_joined_exist_flag.Rd index 1a3d9da15..9334f60eb 100644 --- a/man/derive_var_joined_exist_flag.Rd +++ b/man/derive_var_joined_exist_flag.Rd @@ -460,6 +460,7 @@ General Derivation Functions for all ADaMs that returns variable appended to dat \code{\link{derive_var_merged_summary}()}, \code{\link{derive_var_obs_number}()}, \code{\link{derive_var_relative_flag}()}, +\code{\link{derive_vars_cat}()}, \code{\link{derive_vars_computed}()}, \code{\link{derive_vars_joined}()}, \code{\link{derive_vars_merged}()}, diff --git a/man/derive_var_merged_ef_msrc.Rd b/man/derive_var_merged_ef_msrc.Rd index 538921116..551c7f7c2 100644 --- a/man/derive_var_merged_ef_msrc.Rd +++ b/man/derive_var_merged_ef_msrc.Rd @@ -194,6 +194,7 @@ General Derivation Functions for all ADaMs that returns variable appended to dat \code{\link{derive_var_merged_summary}()}, \code{\link{derive_var_obs_number}()}, \code{\link{derive_var_relative_flag}()}, +\code{\link{derive_vars_cat}()}, \code{\link{derive_vars_computed}()}, \code{\link{derive_vars_joined}()}, \code{\link{derive_vars_merged}()}, diff --git a/man/derive_var_merged_exist_flag.Rd b/man/derive_var_merged_exist_flag.Rd index 70a307167..ffe8701d4 100644 --- a/man/derive_var_merged_exist_flag.Rd +++ b/man/derive_var_merged_exist_flag.Rd @@ -152,6 +152,7 @@ General Derivation Functions for all ADaMs that returns variable appended to dat \code{\link{derive_var_merged_summary}()}, \code{\link{derive_var_obs_number}()}, \code{\link{derive_var_relative_flag}()}, +\code{\link{derive_vars_cat}()}, \code{\link{derive_vars_computed}()}, \code{\link{derive_vars_joined}()}, \code{\link{derive_vars_merged}()}, diff --git a/man/derive_var_merged_summary.Rd b/man/derive_var_merged_summary.Rd index e5ce37aa4..532fd263c 100644 --- a/man/derive_var_merged_summary.Rd +++ b/man/derive_var_merged_summary.Rd @@ -160,6 +160,7 @@ General Derivation Functions for all ADaMs that returns variable appended to dat \code{\link{derive_var_merged_exist_flag}()}, \code{\link{derive_var_obs_number}()}, \code{\link{derive_var_relative_flag}()}, +\code{\link{derive_vars_cat}()}, \code{\link{derive_vars_computed}()}, \code{\link{derive_vars_joined}()}, \code{\link{derive_vars_merged}()}, diff --git a/man/derive_var_obs_number.Rd b/man/derive_var_obs_number.Rd index b727cc7f7..8e4f3a544 100644 --- a/man/derive_var_obs_number.Rd +++ b/man/derive_var_obs_number.Rd @@ -103,6 +103,7 @@ General Derivation Functions for all ADaMs that returns variable appended to dat \code{\link{derive_var_merged_exist_flag}()}, \code{\link{derive_var_merged_summary}()}, \code{\link{derive_var_relative_flag}()}, +\code{\link{derive_vars_cat}()}, \code{\link{derive_vars_computed}()}, \code{\link{derive_vars_joined}()}, \code{\link{derive_vars_merged}()}, diff --git a/man/derive_var_relative_flag.Rd b/man/derive_var_relative_flag.Rd index 4fc22c056..6f2b70988 100644 --- a/man/derive_var_relative_flag.Rd +++ b/man/derive_var_relative_flag.Rd @@ -179,6 +179,7 @@ General Derivation Functions for all ADaMs that returns variable appended to dat \code{\link{derive_var_merged_exist_flag}()}, \code{\link{derive_var_merged_summary}()}, \code{\link{derive_var_obs_number}()}, +\code{\link{derive_vars_cat}()}, \code{\link{derive_vars_computed}()}, \code{\link{derive_vars_joined}()}, \code{\link{derive_vars_merged}()}, diff --git a/man/derive_vars_cat.Rd b/man/derive_vars_cat.Rd new file mode 100644 index 000000000..808c33392 --- /dev/null +++ b/man/derive_vars_cat.Rd @@ -0,0 +1,185 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_vars_cat.R +\name{derive_vars_cat} +\alias{derive_vars_cat} +\title{Derive Categorization Variables Like \code{AVALCATy} and \code{AVALCAyN}} +\usage{ +derive_vars_cat(dataset, definition, by_vars = NULL) +} +\arguments{ +\item{dataset}{Input dataset + +The variables specified by the \code{by_vars} and \code{definition} arguments are expected to be in the dataset.} + +\item{definition}{List of expressions created by \code{exprs()}. +Must be in rectangular format and specified using the same syntax as when creating +a \code{tibble} using the \code{tribble()} function. +The \code{definition} object will be converted to a \code{tibble} using \code{tribble()} inside this function. + +Must contain: +\itemize{ +\item the column \code{condition} which will be converted to a logical expression and +will be used on the \code{dataset} input. +\item at least one additional column with the new column name and +the category value(s) used by the logical expression. +\item the column specified in \code{by_vars} (if \code{by_vars} is specified) +} + +e.g. if \code{by_vars} is not specified: + +\if{html}{\out{