diff --git a/.gitignore b/.gitignore index b529a99..f6f2e47 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,9 @@ # RStudio files .Rproj.user/ +# helper files for interchange data between scripts +data-raw/tu_help_data.rds + # produced vignettes vignettes/*.html vignettes/*.pdf diff --git a/NEWS.md b/NEWS.md index 9db8c6a..e01932b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,4 +4,7 @@ - Ophthalmology variants of `ex` and `qs` SDTM datasets added. (#15) - Migrate data and function `get_terms()` from `admiral.test`. (#1, #49) + - Oncology datasets `tu_onco_recist`, `tr_onco_recist`, and `rs_onco_recist` + using RECIST 1.1 response criteria. The datasets contain just a few patients. + They are intended for vignettes and examples of ADaM datasets creation. (#33) diff --git a/R/data.R b/R/data.R index 0e00253..49e270c 100644 --- a/R/data.R +++ b/R/data.R @@ -128,6 +128,32 @@ #' @author Gopi Vegesna "rs_onco" +#' Tumor Results Dataset (RECIST 1.1) +#' +#' A SDTM TR dataset using RECIST 1.1. The dataset contains just a few patients. +#' It is intended for vignettes and examples of ADaM dataset creation. +#' +#' @author Stefan Bundfuss +"tr_onco_recist" + +#' Tumor Identification Dataset (RECIST 1.1) +#' +#' A SDTM TU dataset using RECIST 1.1. The dataset contains just a few patients. +#' It is intended for vignettes and examples of ADaM dataset creation. +#' +#' @author Stefan Bundfuss +"tu_onco_recist" + +#' Disease Response Dataset (RECIST 1.1) +#' +#' A SDTM RS dataset using RECIST 1.1. The dataset contains just a few patients. +#' It is intended for vignettes and examples of ADaM dataset creation. +#' +#' @source The dataset is derived from \code{tr_onco_recist}. +#' +#' @author Stefan Bundfuss +"rs_onco_recist" + #' Supplemental Adverse Events Dataset #' #' A SDTM SUPPAE dataset from the CDISC pilot project diff --git a/data-raw/rs_onco_recist.R b/data-raw/rs_onco_recist.R new file mode 100644 index 0000000..1dc81ce --- /dev/null +++ b/data-raw/rs_onco_recist.R @@ -0,0 +1,98 @@ +# ATTENTION: tr_onco_recist.R and tu_onco_recist.R must be run before this script +library(admiral) + +data("tu_onco_recist") +data("tr_onco_recist") + +tu <- tu_onco_recist + +# add location to tr +tr <- derive_vars_merged( + tr_onco_recist, + dataset_add = tu, + by_vars = exprs(USUBJID, TREVAL = TUEVAL, TREVALID = TUEVALID, TRLNKID = TULNKID), + new_vars = exprs(TRLOC = TULOC) +) + +# select tr results to consider: +tr <- tr %>% + filter( + TRTESTCD == "LDIAM" & TRLOC != "LYMPH NODE" | + TRTESTCD == "LPERP" & TRLOC == "LYMPH NODE" | + TRTESTCD == "TUMSTATE" + ) %>% + # flag complete response by tumor + mutate( + CRFL = if_else( + TRTESTCD == "LDIAM" & TRSTRESN == 0 | + TRTESTCD == "LPERP" & TRSTRESN < 10 | + TRTESTCD == "TUMSTATE" & TRSTRESC == "ABSENT", + TRUE, + FALSE + ) + ) + +# derive sums of diameters +sums <- tr %>% + group_by(STUDYID, USUBJID, TREVAL, TREVALID, TRACPTFL, VISITNUM, VISIT, TRDTC) %>% + summarise( + TRSTRESN = sum(TRSTRESN), + CRFL = all(CRFL), + IDS = paste(sort(TRLNKID), collapse = ", "), + NTFL = all(substr(TRLNKID, 1, 1) == "N") + ) %>% + mutate(TRTESTCD = "SUMDIAM") %>% + ungroup() + +sums <- derive_vars_merged( + sums, + dataset_add = sums, + filter_add = VISIT == "SCREENING", + by_vars = exprs(USUBJID, TREVAL, TREVALID), + new_vars = exprs(BASE = TRSTRESN, BASEIDS = IDS) +) +sums <- derive_vars_joined( + sums, + dataset_add = sums, + by_vars = exprs(USUBJID), + order = exprs(TRSTRESN), + new_vars = exprs(NADIR = TRSTRESN), + join_vars = exprs(VISITNUM), + filter_add = BASEIDS == IDS, + filter_join = VISITNUM > VISITNUM.join, + mode = "first", + check_type = "none" +) + +# derive responses +rs_onco_recist <- sums %>% + mutate( + DOMAIN = "RS", + .before = STUDYID + ) %>% + mutate( + RSTESTCD = "OVRLRESP", + RSTEST = "Overall Response", + RSORRES = case_when( + CRFL & IDS == BASEIDS ~ "CR", + TRSTRESN - NADIR >= 5 & TRSTRESN / NADIR >= 1.2 ~ "PD", + TRSTRESN / BASE <= 0.7 & IDS == BASEIDS ~ "PR", + !is.na(TRSTRESN) & IDS == BASEIDS ~ "SD", + NTFL ~ "NON-CR/NON-PD", + TRUE ~ "NE" + ), + RSSTRESC = RSORRES, + RSEVAL = TREVAL, + RSEVALID = TREVALID, + RSACPTFL = TRACPTFL, + RSDTC = TRDTC + ) %>% + select(-starts_with("TR"), -BASE, -BASEIDS, -NADIR, -IDS, -CRFL, -NTFL) %>% + filter(VISIT != "SCREENING") %>% + derive_var_obs_number( + by_vars = exprs(USUBJID), + new_var = RSSEQ, + order = exprs(VISITNUM, RSEVAL, RSEVALID) + ) + +usethis::use_data(rs_onco_recist, overwrite = TRUE) diff --git a/data-raw/tr_onco_recist.R b/data-raw/tr_onco_recist.R new file mode 100644 index 0000000..8ed7464 --- /dev/null +++ b/data-raw/tr_onco_recist.R @@ -0,0 +1,304 @@ +library(tibble) +library(dplyr) +library(lubridate) +library(admiraldev) +library(admiral) + +# create tumor results to be used for RS +tr <- tribble( + ~SUBJNR, ~TRLNKID, ~TRTESTCD, ~TRORRES, ~VISITNUM, + # BOR = CR, CBOR = SD + "1", "T01", "LDIAM", "21", 1, + "1", "T02", "LPERP", "32", 1, + "1", "T03", "LDIAM", "24", 1, + "1", "T04", "LDIAM", "19", 1, + # SD + "1", "T01", "LDIAM", "20", 2, + "1", "T02", "LPERP", "34", 2, + "1", "T03", "LDIAM", "24", 2, + "1", "T04", "LDIAM", "18", 2, + # NE + "1", "T01", "LDIAM", "20", 3, + "1", "T04", "LDIAM", "18", 3, + # CR + "1", "T01", "LDIAM", "0", 4, + "1", "T02", "LPERP", "7", 4, + "1", "T03", "LDIAM", "0", 4, + "1", "T04", "LDIAM", "0", 4, + + # BOR = PD, CBOR = PD + "2", "T01", "LDIAM", "16", 1, + "2", "T02", "LDIAM", "22", 1, + "2", "T03", "LDIAM", "21", 1, + "2", "T04", "LDIAM", "17", 1, + "2", "T05", "LDIAM", "18", 1, + # SD + "2", "T01", "LDIAM", "16", 2, + "2", "T02", "LDIAM", "21", 2, + "2", "T03", "LDIAM", "19", 2, + "2", "T04", "LDIAM", "17", 2, + "2", "T05", "LDIAM", "18", 2, + # PD + "2", "T02", "LDIAM", "34", 3, + "2", "T03", "LDIAM", "41", 3, + "2", "T04", "LDIAM", "26", 3, + "2", "T05", "LDIAM", "9", 3, + # SD + "2", "T01", "LDIAM", "17", 4, + "2", "T02", "LDIAM", "23", 4, + "2", "T03", "LDIAM", "20", 4, + "2", "T04", "LDIAM", "25", 4, + "2", "T05", "LDIAM", "7", 4, + + # BOR = NON-CR/NON-PD, CBOR = NON-CR/NON-PD + "3", "NT01", "TUMSTATE", "PRESENT", 1, + "3", "NT02", "TUMSTATE", "PRESENT", 1, + "3", "NT03", "TUMSTATE", "PRESENT", 1, + # NON-CR/NON-PD + "3", "NT01", "TUMSTATE", "PRESENT", 2, + "3", "NT02", "TUMSTATE", "ABSENT", 2, + "3", "NT03", "TUMSTATE", "PRESENT", 2, + # NON-CR/NON-PD + "3", "NT01", "TUMSTATE", "PRESENT", 3, + "3", "NT02", "TUMSTATE", "ABSENT", 3, + "3", "NT03", "TUMSTATE", "ABSENT", 3, + + # BOR = NE, CBOR = NE + "4", "NT01", "TUMSTATE", "PRESENT", 1, + "4", "NT02", "TUMSTATE", "PRESENT", 1, + # NON-CR/NON-PD + "4", "NT01", "TUMSTATE", "ABSENT", 2, + "4", "NT02", "TUMSTATE", "PRESENT", 2, + + # BOR = CR CBOR = PR + "5", "T01", "LPERP", "31", 1, + "5", "T02", "LDIAM", "42", 1, + "5", "T03", "LPERP", "17", 1, + # SD + "5", "T01", "LPERP", "31", 2, + "5", "T02", "LDIAM", "34", 2, + "5", "T03", "LPERP", "9", 2, + # PR + "5", "T01", "LPERP", "19", 3, + "5", "T02", "LDIAM", "21", 3, + "5", "T03", "LPERP", "4", 3, + # CR + "5", "T01", "LPERP", "7", 4, + "5", "T02", "LDIAM", "0", 4, + "5", "T03", "LPERP", "3", 4, + # BOR = PR CBOR = PR + "6", "T01", "LDIAM", "27", 1, + "6", "T02", "LDIAM", "51", 1, + # SD + "6", "T01", "LDIAM", "24", 2, + "6", "T02", "LDIAM", "48", 2, + # PR + "6", "T01", "LDIAM", "15", 3, + "6", "T02", "LDIAM", "23", 3, + # NE + "6", "T01", "LDIAM", "14", 4, + # PR + "6", "T01", "LDIAM", "14", 5, + "6", "T02", "LDIAM", "19", 5, + + # BOR = SD CBOR = SD + "7", "T01", "LDIAM", "35", 1, + "7", "T02", "LDIAM", "22", 1, + "7", "T03", "LPERP", "33", 1, + # SD + "7", "T01", "LDIAM", "31", 2, + "7", "T02", "LDIAM", "24", 2, + "7", "T03", "LPERP", "33", 2, + # SD + "7", "T01", "LDIAM", "37", 3, + "7", "T02", "LDIAM", "28", 3, + "7", "T03", "LPERP", "31", 3, + # PD + "7", "T01", "LDIAM", "42", 4, + "7", "T02", "LDIAM", "39", 4, + "7", "T03", "LPERP", "43", 4, + # BOR = SD CBOR = SD + "8", "T01", "LDIAM", "19", 1, + "8", "T02", "LDIAM", "21", 1, + "8", "T03", "LDIAM", "20", 1, + # PR + "8", "T01", "LDIAM", "15", 2, + "8", "T02", "LDIAM", "14", 2, + "8", "T03", "LDIAM", "13", 2, + # CR + "8", "T01", "LDIAM", "0", 3, + "8", "T02", "LDIAM", "0", 3, + "8", "T03", "LDIAM", "0", 3, + # PD + "8", "T01", "LDIAM", "5", 4, + "8", "T02", "LDIAM", "0", 4, + "8", "T03", "LDIAM", "0", 4, +) %>% + mutate( + basicfl = "Y" + ) + +# complete TRTESTCDs such that LDIAM and LPERP are present for all tumors +suppress_warning( + tr_compl <- tr %>% + mutate( + diff_percent = (as.numeric(SUBJNR) + as.numeric(substr(TRLNKID, 3, 3)) + VISITNUM) %% 11, + TRORRES = case_match( + TRTESTCD, + "LDIAM" ~ as.character(as.numeric(TRORRES) * (1 - diff_percent / 100)), + "LPERP" ~ as.character(as.numeric(TRORRES) * (1 + diff_percent / 100)), + .default = TRORRES + ), + TRTESTCD = case_match( + TRTESTCD, + "LDIAM" ~ "LPERP", + "LPERP" ~ "LDIAM", + .default = TRTESTCD + ) + ) %>% + select(-basicfl), + regexpr = "NAs introduced by coercion" +) + +tr <- bind_rows(tr, tr_compl) + +# add results for radiologist 1 +suppress_warning( + tr_radio1 <- tr %>% + mutate( + diff_percent = (as.numeric(SUBJNR) + as.numeric(substr(TRLNKID, 3, 3)) + VISITNUM) %% 7 - 3, + TRORRES = if_else( + TRTESTCD %in% c("LDIAM", "LPERP"), + as.character(as.numeric(TRORRES) * (1 + diff_percent / 100)), + TRORRES + ), + TREVALID = "RADIOLOGIST 1" + ), + regexpr = "NAs introduced by coercion" +) + +# add results for radiologist 2 +suppress_warning( + tr_radio2 <- tr %>% + mutate( + diff_percent = (as.numeric(SUBJNR) + as.numeric(substr(TRLNKID, 3, 3)) + VISITNUM + 3) %% 7 - 3, + TRORRES = if_else( + TRTESTCD %in% c("LDIAM", "LPERP"), + as.character(as.numeric(TRORRES) * (1 + diff_percent / 100)), + TRORRES + ), + TREVALID = "RADIOLOGIST 2" + ), + regexpr = "NAs introduced by coercion" +) + +tr <- bind_rows(tr, tr_radio1, tr_radio2) %>% + select(-diff_percent) %>% + mutate( + TRGRPID = if_else( + substr(TRLNKID, 1, 1) == "T", + "TARGET", + "NON-TARGET" + ), + .before = TRLNKID + ) %>% + mutate( + TREVAL = if_else(is.na(TREVALID), "INVESTIGATOR", "INDEPENDENT ASSESSOR"), + .before = TREVALID, + ) %>% + mutate( + best_assessor = (as.numeric(SUBJNR) + VISITNUM) %% 2 + 1, + TRACPTFL = if_else( + TREVALID == paste("RADIOLOGIST", best_assessor), + "Y", + NA_character_ + ), + .after = TREVALID + ) %>% + select(-best_assessor) + +# add date (TRDTC) +data("dm") +tr <- tr %>% + mutate( + USUBJID = case_match( + SUBJNR, + "1" ~ "01-701-1015", + "2" ~ "01-701-1028", + "3" ~ "01-701-1034", + "4" ~ "01-701-1097", + "5" ~ "01-701-1115", + "6" ~ "01-701-1118", + "7" ~ "01-701-1130", + "8" ~ "01-701-1133" + ), + .before = SUBJNR + ) %>% + derive_vars_merged( + dataset_add = dm, + by_vars = exprs(USUBJID), + new_vars = exprs(RFSTDT = convert_dtc_to_dt(RFSTDTC)) + ) %>% + mutate( + TRDTC = format_ISO8601(RFSTDT + 21 * (VISITNUM - 1)), + TRDTC = if_else( + SUBJNR == "1" & VISITNUM == 3, + substr(TRDTC, 1, 7), + TRDTC + ) + ) +suppress_warning( + tr <- tr %>% + mutate( + DOMAIN = "TR", + STUDYID = "CDISCPILOT01", + .before = USUBJID + ) %>% + mutate( + TRTEST = case_match( + TRTESTCD, + "LDIAM" ~ "Longest Diameter", + "LPERP" ~ "Longest Perpendicular", + "TUMSTATE" ~ "Tumor State" + ), + .after = TRTESTCD + ) %>% + mutate( + VISIT = case_match( + VISITNUM, + 1 ~ "SCREENING", + 2 ~ "WEEK 3", + 3 ~ "WEEK 6", + 4 ~ "WEEK 9", + 5 ~ "WEEK 12" + ), + .after = VISITNUM + ) %>% + mutate( + TRORRESU = if_else(TRTESTCD %in% c("LDIAM", "LPERP"), "mm", NA_character_), + TRSTRESC = TRORRES, + TRSTRESN = as.double(TRSTRESC), + TRSTRESU = TRORRESU, + .after = TRORRES + ), + regexpr = "NAs introduced by coercion" +) + +tr <- derive_var_obs_number( + tr, + by_vars = exprs(USUBJID), + new_var = TRSEQ, + order = exprs(VISITNUM, TREVAL, TREVALID) +) + +# store basic tumor results for creation of TU +# (LPERP -> TULOC = "LYMPH NODE", LDIAM -> something else) + +tr_screen <- tr %>% + filter(VISITNUM == 1 & basicfl == "Y") %>% + select(STUDYID, USUBJID, SUBJNR, TRLNKID, TRTESTCD, VISIT, VISITNUM, TREVAL, TREVALID, TRACPTFL) +saveRDS(tr_screen, file = "data-raw/tu_help_data.rds") + +tr_onco_recist <- select(tr, -SUBJNR, -basicfl, -RFSTDT) + +usethis::use_data(tr_onco_recist, overwrite = TRUE) diff --git a/data-raw/tu_onco_recist.R b/data-raw/tu_onco_recist.R new file mode 100644 index 0000000..e935bf3 --- /dev/null +++ b/data-raw/tu_onco_recist.R @@ -0,0 +1,51 @@ +# ATTENTION: tr_onco_recist.R must be run before this script + +library(stringr) +library(dplyr) + + +# read tr_screen created by tr_onco_recist.R +tr_screen <- readRDS("data-raw/tu_help_data.rds") + +locations <- c( + "ABDOMINAL CAVITY", "ADRENAL GLAND", "BLADDER", + "BODY", "BONE", "BREAST", "CHEST", "COLON", + "ESOPHAGUS", "HEAD AND NECK", "ANUS", "BILE DUCT", + "BRAIN", "GALL BLADDER", "HEAD", "HEART", + "KIDNEY", "LIVER", "LUNG", "MEDIASTINUM", + "PLEURAL EFFUSION", "SOFT TISSUE", "PERIANAL REGION", + "PROSTATE GLAND" +) +tu <- tr_screen %>% + mutate( + TULOC = if_else( + TRTESTCD == "LPERP", + "LYMPH NODE", + locations[as.numeric(SUBJNR) + as.numeric(substr(TRLNKID, str_length(TRLNKID), str_length(TRLNKID)))] + ), + TUTESTCD = "TUMIDENT", + TUTEST = "Tumore Identification" + ) + +tu_onco_recist <- tu %>% + mutate( + DOMAIN = "TU", + .before = STUDYID + ) %>% + mutate( + TUORRES = if_else(substr(TRLNKID, 1, 1) == "T", "TARGET", "NON-TARGET"), + TUSTRESC = TUORRES, + TUMETHOD = "CT SCAN", + TULNKID = TRLNKID, + TUEVAL = TREVAL, + TUEVALID = TREVALID, + TUACPTFL = TRACPTFL + ) %>% + select(-starts_with("TR"), -SUBJNR) %>% + derive_var_obs_number( + by_vars = exprs(USUBJID), + new_var = TUSEQ, + order = exprs(TUEVAL, TUEVALID) + ) + +usethis::use_data(tu_onco_recist, overwrite = TRUE) diff --git a/data/rs_onco_recist.rda b/data/rs_onco_recist.rda new file mode 100644 index 0000000..0590a80 Binary files /dev/null and b/data/rs_onco_recist.rda differ diff --git a/data/tr_onco_recist.rda b/data/tr_onco_recist.rda new file mode 100644 index 0000000..0313c1e Binary files /dev/null and b/data/tr_onco_recist.rda differ diff --git a/data/tu_onco_recist.rda b/data/tu_onco_recist.rda new file mode 100644 index 0000000..eb3398b Binary files /dev/null and b/data/tu_onco_recist.rda differ diff --git a/inst/WORDLIST b/inst/WORDLIST index a63d02b..0a9acd6 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -2,6 +2,7 @@ ADEG ADaM AE AELAT +Bundfuss CDISC Contestí DM @@ -17,6 +18,7 @@ NEI Pharmacokinetic Pharmacokinetics Pharmaverse +RECIST Rodríguez SDG SDTM diff --git a/man/rs_onco_recist.Rd b/man/rs_onco_recist.Rd new file mode 100644 index 0000000..4a43efe --- /dev/null +++ b/man/rs_onco_recist.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{rs_onco_recist} +\alias{rs_onco_recist} +\title{Disease Response Dataset (RECIST 1.1)} +\format{ +An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 66 rows and 14 columns. +} +\source{ +The dataset is derived from \code{tr_onco_recist}. +} +\usage{ +rs_onco_recist +} +\description{ +A SDTM RS dataset using RECIST 1.1. The dataset contains just a few patients. +It is intended for vignettes and examples of ADaM dataset creation. +} +\author{ +Stefan Bundfuss +} +\keyword{datasets} diff --git a/man/tr_onco_recist.Rd b/man/tr_onco_recist.Rd new file mode 100644 index 0000000..9fc4c05 --- /dev/null +++ b/man/tr_onco_recist.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{tr_onco_recist} +\alias{tr_onco_recist} +\title{Tumor Results Dataset (RECIST 1.1)} +\format{ +An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 546 rows and 19 columns. +} +\usage{ +tr_onco_recist +} +\description{ +A SDTM TR dataset using RECIST 1.1. The dataset contains just a few patients. +It is intended for vignettes and examples of ADaM dataset creation. +} +\author{ +Stefan Bundfuss +} +\keyword{datasets} diff --git a/man/tu_onco_recist.Rd b/man/tu_onco_recist.Rd new file mode 100644 index 0000000..eeda135 --- /dev/null +++ b/man/tu_onco_recist.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{tu_onco_recist} +\alias{tu_onco_recist} +\title{Tumor Identification Dataset (RECIST 1.1)} +\format{ +An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 75 rows and 16 columns. +} +\usage{ +tu_onco_recist +} +\description{ +A SDTM TU dataset using RECIST 1.1. The dataset contains just a few patients. +It is intended for vignettes and examples of ADaM dataset creation. +} +\author{ +Stefan Bundfuss +} +\keyword{datasets}