pharmaverse / admiral

ADaM in R Asset Library
https://pharmaverse.github.io/admiral
Apache License 2.0
226 stars 64 forks source link

Bug: derive_var_atoxgr_dir() derives baseline CTCAE grade relative to baseline (if baseline abnormal) #2512

Open MChiabudini opened 1 month ago

MChiabudini commented 1 month ago

What happened?

In CTCAE v5 some lab parameters are graded relative to baseline if baseline was abnormal (e.g., Alanine aminotransferase increased, CTCAE grade 1: >ULN - 3.0 x ULN if baseline was normal; 1.5 - 3.0 x baseline if baseline was abnormal). In derive_var_atoxgr_dir() this definition is also applied to the baseline value itself, resulting in baseline CTCAE grade being always 0 for such lab parameters. From my perspective, only post-baseline values should be graded using baseline as reference (if baseline was abnormal). All baseline (or also pre-baseline) values should be graded based on normal ranges only, irrespective whether baseline was normal or abnormal. For example, the following baseline values for alanine aminotransferase should be graded with grade 1, and not grade 0 (compare reprex below): image

Session Information

R version 4.2.2 (2022-10-31 ucrt) Platform: x86_64-w64-mingw32/x64 (64-bit) Running under: Windows 10 x64 (build 19045)

Matrix products: default

locale: [1] LC_COLLATE=English_United States.utf8 LC_CTYPE=English_United States.utf8 LC_MONETARY=English_United States.utf8 LC_NUMERIC=C LC_TIME=English_United States.utf8

attached base packages: [1] stats graphics grDevices utils datasets methods base

other attached packages: [1] stringr_1.5.1 lubridate_1.9.3 dplyr_1.1.4 pharmaversesdtm_1.0.0 admiral_1.1.0 devtools_2.4.5 usethis_2.1.6

loaded via a namespace (and not attached): [1] Rcpp_1.0.12 compiler_4.2.2 pillar_1.9.0 later_1.3.2 urlchecker_1.0.1 profvis_0.3.7 remotes_2.5.0 tools_4.2.2 digest_0.6.35 pkgbuild_1.4.4 pkgload_1.3.4 timechange_0.3.0 [13] memoise_2.0.1 lifecycle_1.0.4 tibble_3.2.1 admiraldev_1.1.0 pkgconfig_2.0.3 rlang_1.1.4 shiny_1.7.4 cli_3.6.2 rstudioapi_0.14 fastmap_1.2.0 withr_3.0.0 hms_1.1.3
[25] generics_0.1.3 fs_1.6.4 vctrs_0.6.5 htmlwidgets_1.6.4 tidyselect_1.2.1 glue_1.7.0 R6_2.5.1 fansi_1.0.6 sessioninfo_1.2.2 tidyr_1.3.1 purrr_1.0.2 magrittr_2.0.3
[37] promises_1.3.0 ellipsis_0.3.2 htmltools_0.5.8.1 mime_0.12 xtable_1.8-4 httpuv_1.6.8 utf8_1.2.4 stringi_1.8.4 miniUI_0.1.1.1 cachem_1.1.0

Reproducible Example

Run ADLB template included in {admiral} up to line 258, change line 266 to grade_crit <- atoxgr_criteria_ctcv5, and then run lines 269 to 312:

# Assign grade criteria
# metadata atoxgr_criteria_ctcv4 used to implement NCI-CTCAEv4
# user could change to atoxgr_criteria_ctcv5 to implement NCI-CTCAEv5
# Note: Hyperglycemia and Hypophosphatemia not defined in NCI-CTCAEv5 so
# user would need to amend look-up table grade_lookup
# See (https://pharmaverse.github.io/admiral/articles/lab_grading.html#implement_ctcv5)
grade_crit <- atoxgr_criteria_ctcv5

# Add ATOXDSCL and ATOXDSCH
adlb <- adlb %>%
  derive_vars_merged(
    dataset_add = grade_lookup,
    by_vars = exprs(PARAMCD)
  ) %>%
  # Derive toxicity grade for low values ATOXGRL

  derive_var_atoxgr_dir(
    meta_criteria = grade_crit,
    new_var = ATOXGRL,
    tox_description_var = ATOXDSCL,
    criteria_direction = "L",
    get_unit_expr = extract_unit(PARAM)
  ) %>%
  # Derive toxicity grade for low values ATOXGRH
  # default metadata atoxgr_criteria_ctcv4 used
  derive_var_atoxgr_dir(
    meta_criteria = grade_crit,
    new_var = ATOXGRH,
    tox_description_var = ATOXDSCH,
    criteria_direction = "H",
    get_unit_expr = extract_unit(PARAM)
  ) %>%
  # (Optional) derive overall grade ATOXGR (combining ATOXGRL and ATOXGRH)
  derive_var_atoxgr() %>%
  # Derive baseline toxicity grade for low values BTOXGRL
  derive_var_base(
    by_vars = exprs(STUDYID, USUBJID, PARAMCD, BASETYPE),
    source_var = ATOXGRL,
    new_var = BTOXGRL
  ) %>%
  # Derive baseline toxicity grade for high values BTOXGRH
  derive_var_base(
    by_vars = exprs(STUDYID, USUBJID, PARAMCD, BASETYPE),
    source_var = ATOXGRH,
    new_var = BTOXGRH
  ) %>%
  # Derive baseline toxicity grade for for overall grade BTOXGR
  derive_var_base(
    by_vars = exprs(STUDYID, USUBJID, PARAMCD, BASETYPE),
    source_var = ATOXGR,
    new_var = BTOXGR
  )

# Filter output to identify critical records (Alanine aminotransferase increased as example)
chk_ctcae_baseline <- adlb %>%  
  filter(PARAMCD == "ALT" & ABLFL == "Y" & ANRIND == "HIGH") %>% 
  select(USUBJID, PARAMCD, PARAM, AVAL, ANRLO, ANRHI, ABLFL, ANRIND, ATOXDSCH, ATOXGRH)

Check baseline ATOXGRH for baseline values above ULN for lab parameters where CTCAE grading is relative to baseline in case baseline was abnormal, e.g. Alanine aminotransferase increased image

bms63 commented 1 month ago

Thanks @MChiabudini for your feedback! :)

@millerg23 do you mind taking a look at this please? I actually thought we took this into consideration when making this function.

millerg23 commented 1 month ago

These lab tests are problematic with the way the criteria is worded. However I think we are being conservative here, as if BASELINE was abnormal, and we graded the BASELINE as grade 1 (as we compared with ULN), and then say at Visit 1, the lab value was exactly the same, it would be graded 0, which may give impression that the treatment has made a difference to the grade, when in fact the lab value remained the same after treatment. In Roche, we decided not to grade at baseline at all for these lab tests, and added a line of code in our template scripts to handle this.

bms63 commented 1 month ago

Should we note this in the function documentation and recommended a way to address?

MChiabudini commented 1 month ago

These lab tests are problematic with the way the criteria is worded. However I think we are being conservative here, as if BASELINE was abnormal, and we graded the BASELINE as grade 1 (as we compared with ULN), and then say at Visit 1, the lab value was exactly the same, it would be graded 0, which may give impression that the treatment has made a difference to the grade, when in fact the lab value remained the same after treatment. In Roche, we decided not to grade at baseline at all for these lab tests, and added a line of code in our template scripts to handle this.

I agree on that. Also to me it seems odd to grade an unchanged post-baseline value with grade 0 if BASELINE was graded as grade 1. Out of curiosity, if you don't grade at baseline at all, how would you handle such cases for shift tables? Not display shift tables using CTCAE grades at all for such parameters?