# Name: ADPC
#
# Label: Pharmacokinetics Concentrations Analysis Dataset
#
# Description: Based on simulated data, create ADPC analysis dataset
# The dataset format is also suitable for Non-compartmental analysis (ADNCA)
#
# Input: pc, ex, vs, adsl
library(admiral)
library(dplyr)
library(lubridate)
library(stringr)
library(pharmaversesdtm) # Contains example datasets from the CDISC pilot project or simulated
# ---- Load source datasets ----
# Use e.g. haven::read_sas to read in .sas7bdat, or other suitable functions
# as needed and assign to the variables below.
# For illustration purposes read in admiral test data
# Load PC, EX, VS and ADSL
pc <- pharmaversesdtm::pc
ex <- pharmaversesdtm::ex
vs <- pharmaversesdtm::vs
adsl <- admiral::admiral_adsl
# When SAS datasets are imported into R using haven::read_sas(), missing
# character values from SAS appear as "" characters in R, instead of appearing
# as NA values. Further details can be obtained via the following link:
# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values # nolint
ex <- convert_blanks_to_na(ex)
pc <- convert_blanks_to_na(pc)
vs <- convert_blanks_to_na(vs)
# ---- Lookup tables ----
param_lookup <- tibble::tribble(
~PCTESTCD, ~PARAMCD, ~PARAM, ~PARAMN,
"XAN", "XAN", "Pharmacokinetic concentration of Xanomeline", 1,
"DOSE", "DOSE", "Xanomeline Patch Dose", 2,
)
# ---- Derivations ----
# Get list of ADSL vars required for derivations
adsl_vars <- exprs(TRTSDT, TRTSDTM, TRT01P, TRT01A)
pc_dates <- pc %>%
# Join ADSL with PC (need TRTSDT for ADY derivation)
derive_vars_merged(
dataset_add = adsl,
new_vars = adsl_vars,
by_vars = exprs(STUDYID, USUBJID)
) %>%
# Derive analysis date/time
# Impute missing time to 00:00:00
derive_vars_dtm(
new_vars_prefix = "A",
dtc = PCDTC,
time_imputation = "00:00:00"
) %>%
# Derive dates and times from date/times
derive_vars_dtm_to_dt(exprs(ADTM)) %>%
derive_vars_dtm_to_tm(exprs(ADTM)) %>%
derive_vars_dy(reference_date = TRTSDT, source_vars = exprs(ADT)) %>%
# Derive event ID and nominal relative time from first dose (NFRLT)
mutate(
EVID = 0,
DRUG = PCTEST,
NFRLT = if_else(PCTPTNUM < 0, 0, PCTPTNUM), .after = USUBJID
)
# ---- Get dosing information ----
ex_dates <- ex %>%
derive_vars_merged(
dataset_add = adsl,
new_vars = adsl_vars,
by_vars = exprs(STUDYID, USUBJID)
) %>%
# Keep records with nonzero dose
filter(EXDOSE > 0) %>%
# Add time and set missing end date to start date
# Impute missing time to 00:00:00
# Note all times are missing for dosing records in this example data
# Derive Analysis Start and End Dates
derive_vars_dtm(
new_vars_prefix = "AST",
dtc = EXSTDTC,
time_imputation = "00:00:00"
) %>%
derive_vars_dtm(
new_vars_prefix = "AEN",
dtc = EXENDTC,
time_imputation = "00:00:00"
) %>%
# Derive event ID and nominal relative time from first dose (NFRLT)
mutate(
EVID = 1,
NFRLT = case_when(
VISITDY == 1 ~ 0,
TRUE ~ 24 * VISITDY
)
) %>%
# Set missing end dates to start date
mutate(AENDTM = case_when(
is.na(AENDTM) ~ ASTDTM,
TRUE ~ AENDTM
)) %>%
# Derive dates from date/times
derive_vars_dtm_to_dt(exprs(ASTDTM)) %>%
derive_vars_dtm_to_dt(exprs(AENDTM))
# ---- Expand dosing records between start and end dates ----
# Updated function includes nominal_time parameter
ex_exp <- ex_dates %>%
create_single_dose_dataset(
dose_freq = EXDOSFRQ,
start_date = ASTDT,
start_datetime = ASTDTM,
end_date = AENDT,
end_datetime = AENDTM,
nominal_time = NFRLT,
lookup_table = dose_freq_lookup,
lookup_column = CDISC_VALUE,
keep_source_vars = exprs(
STUDYID, USUBJID, EVID, EXDOSFRQ, EXDOSFRM,
NFRLT, EXDOSE, EXDOSU, EXTRT, ASTDT, ASTDTM, AENDT, AENDTM,
VISIT, VISITNUM, VISITDY,
TRT01A, TRT01P, DOMAIN, EXSEQ, !!!adsl_vars
)
) %>%
# Derive AVISIT based on nominal relative time
# Derive AVISITN to nominal time in whole days using integer division
# Define AVISIT based on nominal day
mutate(
AVISITN = NFRLT %/% 24 + 1,
AVISIT = paste("Day", AVISITN),
ADTM = ASTDTM,
DRUG = EXTRT
) %>%
# Derive dates and times from datetimes
derive_vars_dtm_to_dt(exprs(ADTM)) %>%
derive_vars_dtm_to_tm(exprs(ADTM)) %>%
derive_vars_dtm_to_tm(exprs(ASTDTM)) %>%
derive_vars_dtm_to_tm(exprs(AENDTM)) %>%
derive_vars_dy(reference_date = TRTSDT, source_vars = exprs(ADT))
# ---- Find first dose per treatment per subject ----
# ---- Join with ADPC data and keep only subjects with dosing ----
adpc_first_dose <- pc_dates %>%
derive_vars_merged(
dataset_add = ex_exp,
filter_add = (EXDOSE > 0 & !is.na(ADTM)),
new_vars = exprs(FANLDTM = ADTM),
order = exprs(ADTM, EXSEQ),
mode = "first",
by_vars = exprs(STUDYID, USUBJID, DRUG)
) %>%
filter(!is.na(FANLDTM)) %>%
# Derive AVISIT based on nominal relative time
# Derive AVISITN to nominal time in whole days using integer division
# Define AVISIT based on nominal day
mutate(
AVISITN = NFRLT %/% 24 + 1,
AVISIT = paste("Day", AVISITN),
)
# ---- Find previous dose ----
adpc_prev <- adpc_first_dose %>%
derive_vars_joined(
dataset_add = ex_exp,
by_vars = exprs(USUBJID),
order = exprs(ADTM),
new_vars = exprs(
ADTM_prev = ADTM, EXDOSE_prev = EXDOSE, AVISIT_prev = AVISIT,
AENDTM_prev = AENDTM
),
join_vars = exprs(ADTM),
join_type = "all",
filter_add = NULL,
filter_join = ADTM > ADTM.join,
mode = "last",
check_type = "none"
)
# ---- Find next dose ----
adpc_next <- adpc_prev %>%
derive_vars_joined(
dataset_add = ex_exp,
by_vars = exprs(USUBJID),
order = exprs(ADTM),
new_vars = exprs(
ADTM_next = ADTM, EXDOSE_next = EXDOSE, AVISIT_next = AVISIT,
AENDTM_next = AENDTM
),
join_vars = exprs(ADTM),
join_type = "all",
filter_add = NULL,
filter_join = ADTM <= ADTM.join,
mode = "first",
check_type = "none"
)
# ---- Find previous nominal time ----
adpc_nom_prev <- adpc_next %>%
derive_vars_joined(
dataset_add = ex_exp,
by_vars = exprs(USUBJID),
order = exprs(NFRLT),
new_vars = exprs(NFRLT_prev = NFRLT),
join_vars = exprs(NFRLT),
join_type = "all",
filter_add = NULL,
filter_join = NFRLT > NFRLT.join,
mode = "last",
check_type = "none"
)
# ---- Find next nominal time ----
adpc_nom_next <- adpc_nom_prev %>%
derive_vars_joined(
dataset_add = ex_exp,
by_vars = exprs(USUBJID),
order = exprs(NFRLT),
new_vars = exprs(NFRLT_next = NFRLT),
join_vars = exprs(NFRLT),
join_type = "all",
filter_add = NULL,
filter_join = NFRLT <= NFRLT.join,
mode = "first",
check_type = "none"
)
# ---- Combine ADPC and EX data ----
# Derive Relative Time Variables
adpc_arrlt <- bind_rows(adpc_nom_next, ex_exp) %>%
group_by(USUBJID, DRUG) %>%
mutate(
FANLDTM = min(FANLDTM, na.rm = TRUE),
min_NFRLT = min(NFRLT_prev, na.rm = TRUE),
maxdate = max(ADT[EVID == 0], na.rm = TRUE), .after = USUBJID
) %>%
arrange(USUBJID, ADTM) %>%
ungroup() %>%
filter(ADT <= maxdate) %>%
# Derive Actual Relative Time from First Dose (AFRLT)
derive_vars_duration(
new_var = AFRLT,
start_date = FANLDTM,
end_date = ADTM,
out_unit = "hours",
floor_in = FALSE,
add_one = FALSE
) %>%
# Derive Actual Relative Time from Reference Dose (ARRLT)
derive_vars_duration(
new_var = ARRLT,
start_date = ADTM_prev,
end_date = ADTM,
out_unit = "hours",
floor_in = FALSE,
add_one = FALSE
) %>%
# Derive Actual Relative Time from Next Dose (AXRLT not kept)
derive_vars_duration(
new_var = AXRLT,
start_date = ADTM_next,
end_date = ADTM,
out_unit = "hours",
floor_in = FALSE,
add_one = FALSE
) %>%
mutate(
ARRLT = case_when(
EVID == 1 ~ 0,
is.na(ARRLT) ~ AXRLT,
TRUE ~ ARRLT
),
# Derive Reference Dose Date
PCRFTDTM = case_when(
EVID == 1 ~ ADTM,
is.na(ADTM_prev) ~ ADTM_next,
TRUE ~ ADTM_prev
)
) %>%
# Derive dates and times from datetimes
derive_vars_dtm_to_dt(exprs(FANLDTM)) %>%
derive_vars_dtm_to_tm(exprs(FANLDTM)) %>%
derive_vars_dtm_to_dt(exprs(PCRFTDTM)) %>%
derive_vars_dtm_to_tm(exprs(PCRFTDTM))
# Derive Nominal Relative Time from Reference Dose (NRRLT)
adpc_nrrlt <- adpc_arrlt %>%
mutate(
NRRLT = case_when(
EVID == 1 ~ 0,
is.na(NFRLT_prev) ~ NFRLT - min_NFRLT,
TRUE ~ NFRLT - NFRLT_prev
),
NXRLT = case_when(
EVID == 1 ~ 0,
TRUE ~ NFRLT - NFRLT_next
)
)
# ---- Derive Analysis Variables ----
# Derive ATPTN, ATPT, ATPTREF, ABLFL and BASETYPE
# Derive planned dose DOSEP, actual dose DOSEA and units
# Derive PARAMCD and relative time units
# Derive AVAL, AVALU and AVALCAT1
adpc_aval <- adpc_nrrlt %>%
mutate(
PARCAT1 = PCSPEC,
ATPTN = case_when(
EVID == 1 ~ 0,
TRUE ~ PCTPTNUM
),
ATPT = case_when(
EVID == 1 ~ "Dose",
TRUE ~ PCTPT
),
ATPTREF = case_when(
EVID == 1 ~ AVISIT,
is.na(AVISIT_prev) ~ AVISIT_next,
TRUE ~ AVISIT_prev
),
# Derive BASETYPE
BASETYPE = paste(ATPTREF, "Baseline"),
# Derive Actual Dose
DOSEA = case_when(
EVID == 1 ~ EXDOSE,
is.na(EXDOSE_prev) ~ EXDOSE_next,
TRUE ~ EXDOSE_prev
),
# Derive Planned Dose
DOSEP = case_when(
TRT01P == "Xanomeline High Dose" ~ 81,
TRT01P == "Xanomeline Low Dose" ~ 54
),
DOSEU = "mg",
) %>%
# Derive relative time units
mutate(
FRLTU = "h",
RRLTU = "h",
# Derive PARAMCD
PARAMCD = coalesce(PCTESTCD, "DOSE"),
ALLOQ = PCLLOQ,
# Derive AVAL
AVAL = case_when(
EVID == 1 ~ EXDOSE,
TRUE ~ PCSTRESN
),
AVALU = case_when(
EVID == 1 ~ EXDOSU,
TRUE ~ PCSTRESU
),
AVALCAT1 = if_else(PCSTRESC == "<BLQ", PCSTRESC, prettyNum(signif(AVAL, digits = 3))),
) %>%
# Add SRCSEQ
mutate(
SRCDOM = DOMAIN,
SRCVAR = "SEQ",
SRCSEQ = coalesce(PCSEQ, EXSEQ)
)
# ---- Create DTYPE imputation
adpc_lloq <- adpc_aval %>%
derive_extreme_records(
dataset_add = adpc_aval,
by_vars = exprs(USUBJID, PARAMCD, PARCAT1, AVISITN, AVISIT, ADTM, PCSEQ),
order = exprs(ADTM, BASETYPE, EVID, ATPTN, PARCAT1),
mode = "last",
filter_add = PCSTRESC == "<BLQ" & is.na(AVAL),
set_values_to = exprs(
AVAL = ALLOQ * .5,
DTYPE = "HALFLLOQ"
)
)
# ---- Create DTYPE copy records ----
dtype <- adpc_lloq %>%
filter(NFRLT > 0 & NXRLT == 0 & EVID == 0 & !is.na(AVISIT_next)) %>%
select(-PCRFTDT, -PCRFTTM) %>%
# Re-derive variables in for DTYPE copy records
mutate(
ATPTREF = AVISIT_next,
ARRLT = AXRLT,
NRRLT = NXRLT,
PCRFTDTM = ADTM_next,
DOSEA = EXDOSE_next,
BASETYPE = paste(AVISIT_next, "Baseline"),
ATPT = "Pre-dose",
ATPTN = -0.5,
DTYPE = case_when(
is.na(DTYPE) ~ "COPY",
DTYPE == "HALFLLOQ" ~ "COPY/HALFLLOQ"
)
) %>%
derive_vars_dtm_to_dt(exprs(PCRFTDTM)) %>%
derive_vars_dtm_to_tm(exprs(PCRFTDTM))
# ---- Combine original records and DTYPE copy records ----
adpc_dtype <- bind_rows(adpc_lloq, dtype) %>%
arrange(STUDYID, USUBJID, BASETYPE, ADTM, NFRLT) %>%
mutate(
# Derive baseline flag for pre-dose records
ABLFL = case_when(
ATPT == "Pre-dose" & !is.na(AVAL) ~ "Y",
TRUE ~ NA_character_
),
# Derive MRRLT, ANL01FL and ANL02FL
MRRLT = if_else(ARRLT < 0, 0, ARRLT),
ANL01FL = "Y",
ANL02FL = if_else(is.na(DTYPE), "Y", NA_character_)
)
# ---- Derive BASE and Calculate Change from Baseline ----
adpc_base <- adpc_dtype %>%
derive_var_base(
by_vars = exprs(STUDYID, USUBJID, PARAMCD, PARCAT1, BASETYPE),
source_var = AVAL,
new_var = BASE,
filter = ABLFL == "Y"
)
# Calculate CHG for post-baseline records
# The decision on how to populate pre-baseline and baseline values of CHG is left to producer choice
adpc_chg <- restrict_derivation(
adpc_base,
derivation = derive_var_chg,
filter = AVISITN > 0
)
# ---- Add ASEQ ----
adpc_aseq <- adpc_chg %>%
# Calculate ASEQ
derive_var_obs_number(
new_var = ASEQ,
by_vars = exprs(STUDYID, USUBJID),
order = exprs(ADTM, BASETYPE, EVID, AVISITN, ATPTN, PARCAT1, DTYPE),
check_type = "error"
) %>%
# Remove temporary variables
select(
-DOMAIN, -PCSEQ, -starts_with("min"),
-starts_with("max"), -starts_with("EX"), -ends_with("next"),
-ends_with("prev"), -DRUG, -EVID, -AXRLT, -NXRLT, -VISITDY
) %>%
# Derive PARAM and PARAMN
derive_vars_merged(dataset_add = select(param_lookup, -PCTESTCD), by_vars = exprs(PARAMCD))
#---- Derive additional baselines from VS ----
adpc_baselines <- adpc_aseq %>%
derive_vars_merged(
dataset_add = vs,
filter_add = VSTESTCD == "HEIGHT",
by_vars = exprs(STUDYID, USUBJID),
new_vars = exprs(HTBL = VSSTRESN, HTBLU = VSSTRESU)
) %>%
derive_vars_merged(
dataset_add = vs,
filter_add = VSTESTCD == "WEIGHT" & VSBLFL == "Y",
by_vars = exprs(STUDYID, USUBJID),
new_vars = exprs(WTBL = VSSTRESN, WTBLU = VSSTRESU)
) %>%
mutate(
BMIBL = compute_bmi(height = HTBL, weight = WTBL),
BMIBLU = "kg/m^2"
)
# ---- Add all ADSL variables ----
# Add all ADSL variables
adpc <- adpc_baselines %>%
derive_vars_merged(
dataset_add = select(adsl, !!!negate_vars(adsl_vars)),
by_vars = exprs(STUDYID, USUBJID)
)
# Final Steps, Select final variables and Add labels
# This process will be based on your metadata, no example given for this reason
# ...
# ---- Save output ----
# Change to whichever directory you want to save the dataset in
dir <- tools::R_user_dir("admiral_templates_data", which = "cache")
if (!file.exists(dir)) {
# Create the folder
dir.create(dir, recursive = TRUE, showWarnings = FALSE)
}
save(adpc, file = file.path(dir, "adpc.rda"), compress = "bzip2")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.