This article describes creating a Pharmacokinetics (PK) Non-compartmental analysis (NCA) ADaM (ADNCA/ADPC) or a Population PK ADaM (ADPPK). The first part of the article describes the NCA file creation while the second part describes Population PK. This initial steps for both files are very similar and could be combined in one script if desired.
The Non-compartmental analysis (NCA) ADaM uses the CDISC
Implementation Guide (https://www.cdisc.org/standards/foundational/adam/adamig-non-compartmental-analysis-input-data-v1-0).
This example presented uses underlying EX and
PC domains where the EX and PC
domains represent data as collected and the ADPC ADaM is
output. However, the example can be applied to situations where an
EC domain is used as input instead of EX
and/or ADNCA or another ADaM is created.
One of the important aspects of the dataset is the derivation of relative timing variables. These variables consist of nominal and actual times, and refer to the time from first dose or time from most recent reference dose. The reference dose for pre-dose records may be the upcoming dose. The CDISC Implementation Guide makes use of duplicated records for analysis, which allows the same record to be used both with respect to the previous dose and the next upcoming dose. This is illustrated later in this vignette.
Here are the relative time variables we will use. These correspond to the names in the CDISC Implementation Guide.
| Variable | Variable Label |
|---|---|
| NFRLT | Nom. Rel. Time from Analyte First Dose |
| AFRLT | Act. Rel. Time from Analyte First Dose |
| NRRLT | Nominal Rel. Time from Ref. Dose |
| ARRLT | Actual Rel. Time from Ref. Dose |
| MRRLT | Modified Rel. Time from Ref. Dose |
Note: All examples assume CDISC SDTM and/or ADaM format as input unless otherwise specified.
ADPC Programming WorkflowTo start, all data frames needed for the creation of
ADPC should be read into the environment. This will be a
company specific process. Some of the data frames needed may be
PC, EX, and ADSL.
Additional domains such as VS and LB may be
used for additional baseline variables if needed. These may come from
either the SDTM or ADaM source.
For the purpose of example, the CDISC Pilot SDTM and ADaM
datasets—which are included in {pharmaversesdtm}—are
used.
library(dplyr, warn.conflicts = FALSE)
library(admiral)
library(pharmaversesdtm)
library(lubridate)
library(stringr)
library(tibble)
data("admiral_adsl")
data("ex")
data("pc")
data("vs")
data("lb")
adsl <- admiral_adsl
ex <- convert_blanks_to_na(ex)
# Load PC
pc <- convert_blanks_to_na(pc)
# Load VS for baseline height and weight
vs <- convert_blanks_to_na(vs)
# Load LB for baseline lab values
lb <- convert_blanks_to_na(lb) %>%
filter(LBBLFL == "Y")
# ---- Lookup tables ----
param_lookup <- tibble::tribble(
~PCTESTCD, ~PARAMCD, ~PARAM, ~PARAMN,
"XAN", "XAN", "Pharmacokinetic concentration of Xanomeline", 1,
"DOSE", "DOSE", "Xanomeline Patch Dose", 2,
)At this step, it may be useful to join ADSL to your
PC and EX domains as well. Only the
ADSL variables used for derivations are selected at this
step. The rest of the relevant ADSL variables will be added
later.
In this case we will keep TRTSDT/TRTSDTM
for day derivation and TRT01P/TRT01A for
planned and actual treatments.
In this segment we will use derive_vars_merged() to join
the ADSL variables and the following {admiral}
functions to derive analysis dates, times and days:
derive_vars_dtm(), derive_vars_dtm_to_dt(),
derive_vars_dtm_to_tm(), derive_vars_dy(). We
will also create NFRLT for PC data based on
PCTPTNUM. We will create an event ID (EVID) of
0 for concentration records and 1 for dosing records. This is a
traditional variable that will provide a handy tool to identify records
but will be dropped from the final dataset in this example.
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
)| USUBJID | PCTEST | ADTM | VISIT | PCTPT | NFRLT |
|---|---|---|---|---|---|
| 01-701-1028 | XANOMELINE | 2013-07-18 23:30:00 | BASELINE | Pre-dose | 0.00 |
| 01-701-1028 | XANOMELINE | 2013-07-19 00:05:00 | BASELINE | 5 Min Post-dose | 0.08 |
| 01-701-1028 | XANOMELINE | 2013-07-19 00:30:00 | BASELINE | 30 Min Post-dose | 0.50 |
| 01-701-1028 | XANOMELINE | 2013-07-19 01:00:00 | BASELINE | 1h Post-dose | 1.00 |
| 01-701-1028 | XANOMELINE | 2013-07-19 01:30:00 | BASELINE | 1.5h Post-dose | 1.50 |
| 01-701-1028 | XANOMELINE | 2013-07-19 02:00:00 | BASELINE | 2h Post-dose | 2.00 |
| 01-701-1028 | XANOMELINE | 2013-07-19 04:00:00 | BASELINE | 4h Post-dose | 4.00 |
| 01-701-1028 | XANOMELINE | 2013-07-19 06:00:00 | BASELINE | 6h Post-dose | 6.00 |
| 01-701-1028 | XANOMELINE | 2013-07-19 08:00:00 | BASELINE | 8h Post-dose | 8.00 |
| 01-701-1028 | XANOMELINE | 2013-07-19 12:00:00 | BASELINE | 12h Post-dose | 12.00 |
Next we will also join ADSL data with EX
and derive dates/times. This section uses the {admiral}
functions derive_vars_merged(),
derive_vars_dtm(), and
derive_vars_dtm_to_dt(). Time is imputed to 00:00:00 here
for reasons specific to the sample data. Other imputation times may be
used based on study details. Here we create NFRLT for
EX data based on VISITDY using the formula
(VISITDY - 1) * 24 using dplyr::mutate.
# ---- 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 = 24 * (VISITDY - 1), .after = USUBJID
) %>%
# 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))| USUBJID | EXTRT | EXDOSFRQ | ASTDTM | AENDTM | VISIT | VISITDY | NFRLT |
|---|---|---|---|---|---|---|---|
| 01-701-1028 | XANOMELINE | QD | 2013-07-19 | 2013-08-01 | BASELINE | 1 | 0 |
| 01-701-1028 | XANOMELINE | QD | 2013-08-02 | 2014-01-06 | WEEK 2 | 14 | 312 |
| 01-701-1028 | XANOMELINE | QD | 2014-01-07 | 2014-01-14 | WEEK 24 | 168 | 4008 |
| 01-701-1033 | XANOMELINE | QD | 2014-03-18 | 2014-03-31 | BASELINE | 1 | 0 |
| 01-701-1442 | XANOMELINE | QD | 2013-10-26 | 2013-11-09 | BASELINE | 1 | 0 |
| 01-701-1442 | XANOMELINE | QD | 2013-11-10 | 2014-04-17 | WEEK 2 | 14 | 312 |
| 01-701-1442 | XANOMELINE | QD | 2014-04-18 | 2014-04-26 | WEEK 24 | 168 | 4008 |
| 01-714-1288 | XANOMELINE | QD | 2013-12-04 | 2013-12-17 | BASELINE | 1 | 0 |
| 01-714-1288 | XANOMELINE | QD | 2013-12-18 | 2014-05-27 | WEEK 2 | 14 | 312 |
| 01-714-1288 | XANOMELINE | QD | 2014-05-28 | 2014-06-17 | WEEK 24 | 168 | 4008 |
The function create_single_dose_dataset() can be used to
expand dosing records between the start date and end date. The nominal
time will also be expanded based on the values of EXDOSFRQ,
for example “QD” will result in nominal time being incremented by 24
hours and “BID” will result in nominal time being incremented by 12
hours. This is a new feature of
create_single_dose_dataset().
Dates and times will be derived after expansion using
derive_vars_dtm_to_dt() and
derive_vars_dtm_to_tm().
For this example study we will define analysis visit
(AVISIT) based on the nominal day value from
NFRLT and give it the format, “Day 1”, “Day 2”, “Day 3”,
etc. This is important for creating the BASETYPE variable
later. DRUG is created from EXTRT here. This
will be useful for linking treatment data with concentration data if
there are multiple drugs and/or analytes, but this variable will also be
dropped from the final dataset in this example.
# ---- Expand dosing records between start and end dates ----
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))| USUBJID | DRUG | EXDOSFRQ | ASTDTM | AENDTM | AVISIT | NFRLT |
|---|---|---|---|---|---|---|
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-19 | 2013-07-19 | Day 1 | 0 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-20 | 2013-07-20 | Day 2 | 24 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-21 | 2013-07-21 | Day 3 | 48 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-22 | 2013-07-22 | Day 4 | 72 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-23 | 2013-07-23 | Day 5 | 96 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-24 | 2013-07-24 | Day 6 | 120 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-25 | 2013-07-25 | Day 7 | 144 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-26 | 2013-07-26 | Day 8 | 168 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-27 | 2013-07-27 | Day 9 | 192 |
| 01-701-1028 | XANOMELINE | ONCE | 2013-07-28 | 2013-07-28 | Day 10 | 216 |
In this section we will find the first dose for each subject and
drug, using derive_vars_merged(). We also create an
analysis visit (AVISIT) based on NFRLT. The
first dose datetime for an analyte FANLDTM is calculated as
the minimum ADTM from the dosing records by subject and
drug.
# ---- 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)
)| USUBJID | FANLDTM | AVISIT | ADTM | PCTPT |
|---|---|---|---|---|
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-18 23:30:00 | Pre-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 00:05:00 | 5 Min Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 00:30:00 | 30 Min Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 01:00:00 | 1h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 01:30:00 | 1.5h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 02:00:00 | 2h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 04:00:00 | 4h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 06:00:00 | 6h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 08:00:00 | 8h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 12:00:00 | 12h Post-dose |
Use derive_vars_joined() to find the previous dose data.
This will join the expanded EX data with the
ADPC based on the analysis date ADTM. Note the
filter_join parameter. In addition to the date of the
previous dose (ADTM_prev), we also keep the actual dose
amount EXDOSE_prev and the analysis visit of the dose
AVISIT_prev.
# ---- 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"
)| USUBJID | VISIT | ADTM | PCTPT | ADTM_prev | EXDOSE_prev | AVISIT_prev |
|---|---|---|---|---|---|---|
| 01-701-1028 | BASELINE | 2013-07-18 23:30:00 | Pre-dose | NA | NA | NA |
| 01-701-1028 | BASELINE | 2013-07-19 00:05:00 | 5 Min Post-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 00:30:00 | 30 Min Post-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 01:00:00 | 1h Post-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 01:30:00 | 1.5h Post-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 02:00:00 | 2h Post-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 04:00:00 | 4h Post-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 06:00:00 | 6h Post-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 08:00:00 | 8h Post-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 12:00:00 | 12h Post-dose | 2013-07-19 | 54 | Day 1 |
Similarly, find next dose information using
derive_vars_joined() with the filter_join
parameter as ADTM <= ADTM.join. Here we keep the next
dose analysis date ADTM_next, the next actual dose
EXDOSE_next, and the next analysis visit
AVISIT_next.
# ---- 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"
)| USUBJID | VISIT | ADTM | PCTPT | ADTM_next | EXDOSE_next | AVISIT_next |
|---|---|---|---|---|---|---|
| 01-701-1028 | BASELINE | 2013-07-18 23:30:00 | Pre-dose | 2013-07-19 | 54 | Day 1 |
| 01-701-1028 | BASELINE | 2013-07-19 00:05:00 | 5 Min Post-dose | 2013-07-20 | 54 | Day 2 |
| 01-701-1028 | BASELINE | 2013-07-19 00:30:00 | 30 Min Post-dose | 2013-07-20 | 54 | Day 2 |
| 01-701-1028 | BASELINE | 2013-07-19 01:00:00 | 1h Post-dose | 2013-07-20 | 54 | Day 2 |
| 01-701-1028 | BASELINE | 2013-07-19 01:30:00 | 1.5h Post-dose | 2013-07-20 | 54 | Day 2 |
| 01-701-1028 | BASELINE | 2013-07-19 02:00:00 | 2h Post-dose | 2013-07-20 | 54 | Day 2 |
| 01-701-1028 | BASELINE | 2013-07-19 04:00:00 | 4h Post-dose | 2013-07-20 | 54 | Day 2 |
| 01-701-1028 | BASELINE | 2013-07-19 06:00:00 | 6h Post-dose | 2013-07-20 | 54 | Day 2 |
| 01-701-1028 | BASELINE | 2013-07-19 08:00:00 | 8h Post-dose | 2013-07-20 | 54 | Day 2 |
| 01-701-1028 | BASELINE | 2013-07-19 12:00:00 | 12h Post-dose | 2013-07-20 | 54 | Day 2 |
Use the same method to find the previous and next nominal times. Note
that here the data are sorted by nominal time rather than the actual
time. This will tell us when the previous dose and the next dose were
supposed to occur. Sometimes this will differ from the actual times in a
study. Here we keep the previous nominal dose time
NFRLT_prev and the next nominal dose time
NFRLT_next. Note that the filter_join
parameter uses the nominal relative times,
e.g. NFRLT > NFRLT.join.
# ---- 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"
)| USUBJID | NFRLT | PCTPT | NFRLT_prev | NFRLT_next |
|---|---|---|---|---|
| 01-701-1028 | 0.00 | Pre-dose | NA | 0 |
| 01-701-1028 | 0.08 | 5 Min Post-dose | 0 | 24 |
| 01-701-1028 | 0.50 | 30 Min Post-dose | 0 | 24 |
| 01-701-1028 | 1.00 | 1h Post-dose | 0 | 24 |
| 01-701-1028 | 1.50 | 1.5h Post-dose | 0 | 24 |
| 01-701-1028 | 2.00 | 2h Post-dose | 0 | 24 |
| 01-701-1028 | 4.00 | 4h Post-dose | 0 | 24 |
| 01-701-1028 | 6.00 | 6h Post-dose | 0 | 24 |
| 01-701-1028 | 8.00 | 8h Post-dose | 0 | 24 |
| 01-701-1028 | 12.00 | 12h Post-dose | 0 | 24 |
Combine PC and EX records and derive the
additional relative time variables. Often NCA data will keep both dosing
and concentration records. We will keep both here. Sometimes you will
see ADPC with only the concentration records. If this is
desired, the dosing records can be dropped before saving the final
dataset. We will use the {admiral} function
derive_vars_duration() to calculate the actual relative
time from first dose (AFRLT) and the actual relative time
from most recent dose (ARRLT). Note that we use the
parameter add_one = FALSE here. We will also create a
variable representing actual time to next dose (AXRLT)
which is not kept, but will be used when we create duplicated records
for analysis for the pre-dose records. For now, we will update missing
values of ARRLT corresponding to the pre-dose records with
AXRLT, and dosing records will be set to zero.
We also calculate the reference dates FANLDTM (First
Datetime of Dose for Analyte) and PCRFTDTM (Reference
Datetime of Dose for Analyte) and their corresponding date and time
variables.
We calculate the maximum date for concentration records and only keep the dosing records up to that date.
# ---- 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))| USUBJID | FANLDTM | AVISIT | PCTPT | AFRLT | ARRLT | AXRLT |
|---|---|---|---|---|---|---|
| 01-701-1028 | 2013-07-19 | Day 1 | Pre-dose | -0.5000000 | -0.5000000 | -0.50000 |
| 01-701-1028 | 2013-07-19 | Day 1 | NA | 0.0000000 | 0.0000000 | NA |
| 01-701-1028 | 2013-07-19 | Day 1 | 5 Min Post-dose | 0.0833333 | 0.0833333 | -23.91667 |
| 01-701-1028 | 2013-07-19 | Day 1 | 30 Min Post-dose | 0.5000000 | 0.5000000 | -23.50000 |
| 01-701-1028 | 2013-07-19 | Day 1 | 1h Post-dose | 1.0000000 | 1.0000000 | -23.00000 |
| 01-701-1028 | 2013-07-19 | Day 1 | 1.5h Post-dose | 1.5000000 | 1.5000000 | -22.50000 |
| 01-701-1028 | 2013-07-19 | Day 1 | 2h Post-dose | 2.0000000 | 2.0000000 | -22.00000 |
| 01-701-1028 | 2013-07-19 | Day 1 | 4h Post-dose | 4.0000000 | 4.0000000 | -20.00000 |
| 01-701-1028 | 2013-07-19 | Day 1 | 6h Post-dose | 6.0000000 | 6.0000000 | -18.00000 |
| 01-701-1028 | 2013-07-19 | Day 1 | 8h Post-dose | 8.0000000 | 8.0000000 | -16.00000 |
For nominal relative times we calculate NRRLT generally
as NFRLT - NFRLT_prev and NXRLT as
NFRLT - NFRLT_next.
adpc_nrrlt <- adpc_arrlt %>%
# Derive Nominal Relative Time from Reference Dose (NRRLT)
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
)
)| USUBJID | AVISIT | PCTPT | NFRLT | NRRLT | NXRLT |
|---|---|---|---|---|---|
| 01-701-1028 | Day 1 | Pre-dose | 0.00 | 0.00 | 0.00 |
| 01-701-1028 | Day 1 | NA | 0.00 | 0.00 | 0.00 |
| 01-701-1028 | Day 1 | 5 Min Post-dose | 0.08 | 0.08 | -23.92 |
| 01-701-1028 | Day 1 | 30 Min Post-dose | 0.50 | 0.50 | -23.50 |
| 01-701-1028 | Day 1 | 1h Post-dose | 1.00 | 1.00 | -23.00 |
| 01-701-1028 | Day 1 | 1.5h Post-dose | 1.50 | 1.50 | -22.50 |
| 01-701-1028 | Day 1 | 2h Post-dose | 2.00 | 2.00 | -22.00 |
| 01-701-1028 | Day 1 | 4h Post-dose | 4.00 | 4.00 | -20.00 |
| 01-701-1028 | Day 1 | 6h Post-dose | 6.00 | 6.00 | -18.00 |
| 01-701-1028 | Day 1 | 8h Post-dose | 8.00 | 8.00 | -16.00 |
Using dplyr::mutate we derive a number of analysis
variables including analysis value (AVAL), analysis time
point (ATPT) analysis timepoint reference
(ATPTREF) and baseline type (BASETYPE).
We set ATPT to PCTPT for concentration
records and to “Dose” for dosing records. The analysis timepoint
reference ATPTREF will correspond to the dosing visit. We
will use AVISIT_prev and AVISIT_next to
derive. The baseline type will be a concatenation of
ATPTREF and “Baseline” with values such as “Day 1
Baseline”, “Day 2 Baseline”, etc. The baseline flag ABLFL
will be set to “Y” for pre-dose records.
Analysis value AVAL in this example comes from
PCSTRESN for concentration records. In addition we are
including the dose value EXDOSE for dosing records and
setting BLQ (Below Limit of Quantitation) records to 0 before the first
dose and to 1/2 of LLOQ (Lower Limit of Quantitation) for records after
first dose. (Additional tests such as whether more than 1/3 of records
are BLQ may be required and are not done in this example.) We also
create a listing-ready variable AVALCAT1 which includes the
“BLQ” record indicator and formats the numeric values to three
significant digits.
We derive actual dose DOSEA based on
EXDOSE_prev and EXDOSE_next and planned dose
DOSEP based on the planned treatment TRT01P.
In addition we add the units for the dose variables and the relative
time variables.
# ---- 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(
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 baseline flag for pre-dose records
ABLFL = case_when(
ATPT == "Pre-dose" ~ "Y",
TRUE ~ NA_character_
),
# 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,
PCSTRESC == "<BLQ" & NFRLT == 0 ~ 0,
PCSTRESC == "<BLQ" & NFRLT > 0 ~ 0.5 * ALLOQ,
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)
)| USUBJID | NFRLT | AVISIT | ATPT | ABLFL | ATPTREF | AVAL | AVALCAT1 |
|---|---|---|---|---|---|---|---|
| 01-701-1028 | 0.00 | Day 1 | Pre-dose | Y | Day 1 | 0.0000000 | <BLQ |
| 01-701-1028 | 0.00 | Day 1 | Dose | NA | Day 1 | 54.0000000 | NA |
| 01-701-1028 | 0.08 | Day 1 | 5 Min Post-dose | NA | Day 1 | 0.1015662 | 0.102 |
| 01-701-1028 | 0.50 | Day 1 | 30 Min Post-dose | NA | Day 1 | 0.5469018 | 0.547 |
| 01-701-1028 | 1.00 | Day 1 | 1h Post-dose | NA | Day 1 | 0.9254654 | 0.925 |
| 01-701-1028 | 1.50 | Day 1 | 1.5h Post-dose | NA | Day 1 | 1.1875059 | 1.19 |
| 01-701-1028 | 2.00 | Day 1 | 2h Post-dose | NA | Day 1 | 1.3688894 | 1.37 |
| 01-701-1028 | 4.00 | Day 1 | 4h Post-dose | NA | Day 1 | 1.6831476 | 1.68 |
| 01-701-1028 | 6.00 | Day 1 | 6h Post-dose | NA | Day 1 | 1.7552923 | 1.76 |
| 01-701-1028 | 8.00 | Day 1 | 8h Post-dose | NA | Day 1 | 1.7718547 | 1.77 |
As mentioned above, the CDISC ADaM Implementation Guide for Non-compartmental Analysis uses duplicated records for analysis when a record needs to be used in more than one way. In this example the 24 hour post-dose record will also be used a the pre-dose record for the “Day 2” dose. In addition to 24 hour post-dose records, other situations may include pre-dose records for “Cycle 2 Day 1”, etc.
In general, we will select the records of interest and then update the relative time variables for the duplicated records. In this case we will select where the nominal relative time to next dose is zero. (Note that we do not need to duplicate the first dose record since there is no prior dose.)
DTYPE is set to “COPY” for the duplicated records and
the original PCSEQ value is retained. In this case we
change “24h Post-dose” to “Pre-dose”. ABLFL is set to “Y”
since these records will serve as baseline for the “Day 2” dose.
DOSEA is set to EXDOSE_next and
PCRFTDTM is set to ADTM_next.
# ---- Create DTYPE copy records ----
dtype <- adpc_aval %>%
filter(NFRLT > 0 & NXRLT == 0 & EVID == 0 & !is.na(AVISIT_next)) %>%
select(-PCRFTDT, -PCRFTTM) %>%
# Re-derive variables in for DTYPE copy records
mutate(
ABLFL = NA_character_,
ATPTREF = AVISIT_next,
ARRLT = AXRLT,
NRRLT = NXRLT,
PCRFTDTM = ADTM_next,
DOSEA = EXDOSE_next,
BASETYPE = paste(AVISIT_next, "Baseline"),
ATPT = "Pre-dose",
ATPTN = NFRLT,
ABLFL = "Y",
DTYPE = "COPY"
) %>%
derive_vars_dtm_to_dt(exprs(PCRFTDTM)) %>%
derive_vars_dtm_to_tm(exprs(PCRFTDTM))| USUBJID | DTYPE | ATPT | NFRLT | NRRLT | AFRLT | ARRLT | BASETYPE |
|---|---|---|---|---|---|---|---|
| 01-701-1028 | COPY | Pre-dose | 24 | 0 | 24 | 0 | Day 2 Baseline |
| 01-701-1028 | COPY | Pre-dose | 48 | 0 | 48 | 0 | Day 3 Baseline |
| 01-701-1033 | COPY | Pre-dose | 24 | 0 | 24 | 0 | Day 2 Baseline |
| 01-701-1033 | COPY | Pre-dose | 48 | 0 | 48 | 0 | Day 3 Baseline |
| 01-701-1442 | COPY | Pre-dose | 24 | 0 | 24 | 0 | Day 2 Baseline |
| 01-701-1442 | COPY | Pre-dose | 48 | 0 | 48 | 0 | Day 3 Baseline |
| 01-714-1288 | COPY | Pre-dose | 24 | 0 | 24 | 0 | Day 2 Baseline |
| 01-714-1288 | COPY | Pre-dose | 48 | 0 | 48 | 0 | Day 3 Baseline |
| 01-718-1101 | COPY | Pre-dose | 24 | 0 | 24 | 0 | Day 2 Baseline |
| 01-718-1101 | COPY | Pre-dose | 48 | 0 | 48 | 0 | Day 3 Baseline |
ADPC data with Duplicated RecordsNow the duplicated records are combined with the original records. We
also derive the modified relative time from reference dose
MRRLT. In this case, negative values of ARRLT
are set to zero.
This is also an opportunity to derive analysis flags
e.g. ANL01FL , ANL02FL etc. In this example
ANL01FL is set to “Y” for all records and
ANL02FL is set to “Y” for all records except the duplicated
records with DTYPE = “COPY”. Additional flags may be used
to select full profile records and/or to select records included in the
tables and figures, etc.
# ---- Combine original records and DTYPE copy records ----
adpc_dtype <- bind_rows(adpc_aval, dtype) %>%
arrange(STUDYID, USUBJID, BASETYPE, ADTM, NFRLT) %>%
mutate(
# Derive MRRLT, ANL01FL and ANL02FL
MRRLT = if_else(ARRLT < 0, 0, ARRLT),
ANL01FL = "Y",
ANL02FL = if_else(is.na(DTYPE), "Y", NA_character_),
)| STUDYID | USUBJID | BASETYPE | ADTM | ATPT | NFRLT | NRRLT | ARRLT | MRRLT |
|---|---|---|---|---|---|---|---|---|
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-18 23:30:00 | Pre-dose | 0.00 | 0.00 | -0.5000000 | 0.0000000 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 00:00:00 | Dose | 0.00 | 0.00 | 0.0000000 | 0.0000000 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 00:05:00 | 5 Min Post-dose | 0.08 | 0.08 | 0.0833333 | 0.0833333 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 00:30:00 | 30 Min Post-dose | 0.50 | 0.50 | 0.5000000 | 0.5000000 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 01:00:00 | 1h Post-dose | 1.00 | 1.00 | 1.0000000 | 1.0000000 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 01:30:00 | 1.5h Post-dose | 1.50 | 1.50 | 1.5000000 | 1.5000000 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 02:00:00 | 2h Post-dose | 2.00 | 2.00 | 2.0000000 | 2.0000000 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 04:00:00 | 4h Post-dose | 4.00 | 4.00 | 4.0000000 | 4.0000000 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 06:00:00 | 6h Post-dose | 6.00 | 6.00 | 6.0000000 | 6.0000000 |
| CDISCPILOT01 | 01-701-1028 | Day 1 Baseline | 2013-07-19 08:00:00 | 8h Post-dose | 8.00 | 8.00 | 8.0000000 | 8.0000000 |
ASEQThe {admiral} function derive_var_base() is
used to derive BASE and the function
derive_var_chg() is used to derive change from baseline
CHG.
We also now derive ASEQ using
derive_var_obs_number() and we drop intermediate variables
such as those ending with “_prev” and “_next”.
Finally we derive PARAM and PARAMN from a
lookup table.
# ---- Derive BASE and Calculate Change from Baseline ----
adpc_base <- adpc_dtype %>%
# Derive BASE
derive_var_base(
by_vars = exprs(STUDYID, USUBJID, PARAMCD, BASETYPE),
source_var = AVAL,
new_var = BASE,
filter = ABLFL == "Y"
)
adpc_chg <- derive_var_chg(adpc_base)
# ---- 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, DTYPE),
check_type = "error"
) %>%
# Remove temporary variables
select(
-DOMAIN, -PCSEQ, -starts_with("orig"), -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)
)| USUBJID | BASETYPE | DTYPE | AVISIT | ATPT | AVAL | NFRLT | NRRLT | AFRLT | ARRLT | BASE | CHG |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | Pre-dose | 0.0000000 | 0.00 | 0.00 | -0.5000000 | -0.5000000 | 0 | 0.0000000 |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | Dose | 54.0000000 | 0.00 | 0.00 | 0.0000000 | 0.0000000 | NA | NA |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | 5 Min Post-dose | 0.1015662 | 0.08 | 0.08 | 0.0833333 | 0.0833333 | 0 | 0.1015662 |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | 30 Min Post-dose | 0.5469018 | 0.50 | 0.50 | 0.5000000 | 0.5000000 | 0 | 0.5469018 |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | 1h Post-dose | 0.9254654 | 1.00 | 1.00 | 1.0000000 | 1.0000000 | 0 | 0.9254654 |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | 1.5h Post-dose | 1.1875059 | 1.50 | 1.50 | 1.5000000 | 1.5000000 | 0 | 1.1875059 |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | 2h Post-dose | 1.3688894 | 2.00 | 2.00 | 2.0000000 | 2.0000000 | 0 | 1.3688894 |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | 4h Post-dose | 1.6831476 | 4.00 | 4.00 | 4.0000000 | 4.0000000 | 0 | 1.6831476 |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | 6h Post-dose | 1.7552923 | 6.00 | 6.00 | 6.0000000 | 6.0000000 | 0 | 1.7552923 |
| 01-701-1028 | Day 1 Baseline | NA | Day 1 | 8h Post-dose | 1.7718547 | 8.00 | 8.00 | 8.0000000 | 8.0000000 | 0 | 1.7718547 |
Here we derive additional baseline values from VS for
baseline height HTBL and weight WTBL and
compute the body mass index (BMI) with compute_bmi(). These
values could also be obtained from ADVS if available.
Baseline lab values could also be derived from LB or
ADLB in a similar manner.
# 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"
)| USUBJID | HTBL | HTBLU | WTBL | WTBLU | BMIBL | BMIBLU | BASETYPE | ATPT | AVAL |
|---|---|---|---|---|---|---|---|---|---|
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | Pre-dose | 0.0000000 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | Dose | 54.0000000 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | 5 Min Post-dose | 0.1015662 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | 30 Min Post-dose | 0.5469018 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | 1h Post-dose | 0.9254654 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | 1.5h Post-dose | 1.1875059 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | 2h Post-dose | 1.3688894 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | 4h Post-dose | 1.6831476 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | 6h Post-dose | 1.7552923 |
| 01-701-1028 | 177.8 | cm | 99.34 | kg | 31.42394 | kg/m^2 | Day 1 Baseline | 8h Post-dose | 1.7718547 |
ADSL variablesIf needed, the other ADSL variables can now be
added:
# Add all ADSL variables
adpc <- adpc_baselines %>%
derive_vars_merged(
dataset_add = select(adsl, !!!negate_vars(adsl_vars)),
by_vars = exprs(STUDYID, USUBJID)
)Adding attributes to the ADPC file will be discussed below. We will now turn to the Population PK
example.
The Population PK Analysis Data (ADPPK) follows the CDISC
Implementation Guide (https://www.cdisc.org/standards/foundational/adam/basic-data-structure-adam-poppk-implementation-guide-v1-0).
The programming workflow for Population PK (ADPPK) Analysis Data is
similar to the NCA Programming flow with a few key differences.
Population PK models generally make use of nonlinear mixed effects
models that require numeric variables. The data used in the models will
include both dosing and concentration records, relative time variables,
and numeric covariate variables. A DV or dependent variable
is often expected. This is equivalent to the ADaM AVAL
variable and will be included in addition to AVAL for
ADPPK. The ADPPK file will not have the duplicated records for analysis
found in the NCA.
Here are the relative time variables we will use for the ADPPK data. These correspond to the names in the forthcoming CDISC Implementation Guide.
| Variable | Variable Label |
|---|---|
| NFRLT | Nominal Rel Time from First Dose |
| AFRLT | Actual Rel Time from First Dose |
| NPRLT | Nominal Rel Time from Previous Dose |
| APRLT | Actual Rel Time from Previous Dose |
The ADPPK will require the numeric Event ID
(EVID) which we defined in ADPC but did not
keep.
ADPPK Programming WorkflowADPPKThe initial programming steps for ADPPK will follow the
same sequence as the ADPC. This includes reading in the
{pharmaversesdtm} data, deriving analysis dates, defining
the nominal relative time from first dose NFRLT, and
expanding dosing records. For more detail see these steps above (Read in Data).
We will pick this up at the stage where we find the first dose for
the concentration records. We will use derive_vars_merged()
as we did for ADPC.
# ---- Find first dose per treatment per subject ----
# ---- Join with ADPC data and keep only subjects with dosing ----
adppk_first_dose <- pc_dates %>%
derive_vars_merged(
dataset_add = ex_exp,
filter_add = (!is.na(ADTM)),
new_vars = exprs(FANLDTM = ADTM, EXDOSE_first = EXDOSE),
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),
)| USUBJID | FANLDTM | AVISIT | ADTM | PCTPT |
|---|---|---|---|---|
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-18 23:30:00 | Pre-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 00:05:00 | 5 Min Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 00:30:00 | 30 Min Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 01:00:00 | 1h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 01:30:00 | 1.5h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 02:00:00 | 2h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 04:00:00 | 4h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 06:00:00 | 6h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 08:00:00 | 8h Post-dose |
| 01-701-1028 | 2013-07-19 | Day 1 | 2013-07-19 12:00:00 | 12h Post-dose |
For ADPPK we will find the previous dose with respect to
actual time and nominal time. We will use
derive_vars_joined() as we did for ADPC, but
note that we will not need to find the next dose as for
ADPC.
# ---- Find previous dose ----
adppk_prev <- adppk_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 previous nominal dose ----
adppk_nom_prev <- adppk_prev %>%
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"
)| USUBJID | VISIT | ADTM | PCTPT | ADTM_prev | NFRLT_prev |
|---|---|---|---|---|---|
| 01-701-1028 | BASELINE | 2013-07-18 23:30:00 | Pre-dose | NA | NA |
| 01-701-1028 | BASELINE | 2013-07-19 00:05:00 | 5 Min Post-dose | 2013-07-19 | 0 |
| 01-701-1028 | BASELINE | 2013-07-19 00:30:00 | 30 Min Post-dose | 2013-07-19 | 0 |
| 01-701-1028 | BASELINE | 2013-07-19 01:00:00 | 1h Post-dose | 2013-07-19 | 0 |
| 01-701-1028 | BASELINE | 2013-07-19 01:30:00 | 1.5h Post-dose | 2013-07-19 | 0 |
| 01-701-1028 | BASELINE | 2013-07-19 02:00:00 | 2h Post-dose | 2013-07-19 | 0 |
| 01-701-1028 | BASELINE | 2013-07-19 04:00:00 | 4h Post-dose | 2013-07-19 | 0 |
| 01-701-1028 | BASELINE | 2013-07-19 06:00:00 | 6h Post-dose | 2013-07-19 | 0 |
| 01-701-1028 | BASELINE | 2013-07-19 08:00:00 | 8h Post-dose | 2013-07-19 | 0 |
| 01-701-1028 | BASELINE | 2013-07-19 12:00:00 | 12h Post-dose | 2013-07-19 | 0 |
ADPPKAs we did for ADPC we will now combine PC
and EX records. We will derive the relative time variables
AFRLT (Actual Relative Time from First Dose),
APRLT (Actual Relative Time from Previous Dose), and
NPRLT (Nominal Relative Time from Previous Dose). Use
derive_vars_duration() to derive AFRLT and
APRLT. Note we defined EVID above with values
of 0 for observation records and 1 for dosing records.
# ---- Combine ADPPK and EX data ----
# Derive Relative Time Variables
adppk_aprlt <- bind_rows(adppk_nom_prev, ex_exp) %>%
group_by(USUBJID, DRUG) %>%
mutate(
FANLDTM = min(FANLDTM, na.rm = TRUE),
min_NFRLT = min(NFRLT, 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 (APRLT)
derive_vars_duration(
new_var = APRLT,
start_date = ADTM_prev,
end_date = ADTM,
out_unit = "hours",
floor_in = FALSE,
add_one = FALSE
) %>%
# Derive APRLT
mutate(
APRLT = case_when(
EVID == 1 ~ 0,
is.na(APRLT) ~ AFRLT,
TRUE ~ APRLT
),
NPRLT = case_when(
EVID == 1 ~ 0,
is.na(NFRLT_prev) ~ NFRLT - min_NFRLT,
TRUE ~ NFRLT - NFRLT_prev
)
)| USUBJID | EVID | NFRLT | AFRLT | APRLT | NPRLT |
|---|---|---|---|---|---|
| 01-701-1028 | 0 | 0.00 | -0.5000000 | -0.5000000 | 0.00 |
| 01-701-1028 | 1 | 0.00 | 0.0000000 | 0.0000000 | 0.00 |
| 01-701-1028 | 0 | 0.08 | 0.0833333 | 0.0833333 | 0.08 |
| 01-701-1028 | 0 | 0.50 | 0.5000000 | 0.5000000 | 0.50 |
| 01-701-1028 | 0 | 1.00 | 1.0000000 | 1.0000000 | 1.00 |
| 01-701-1028 | 0 | 1.50 | 1.5000000 | 1.5000000 | 1.50 |
| 01-701-1028 | 0 | 2.00 | 2.0000000 | 2.0000000 | 2.00 |
| 01-701-1028 | 0 | 4.00 | 4.0000000 | 4.0000000 | 4.00 |
| 01-701-1028 | 0 | 6.00 | 6.0000000 | 6.0000000 | 6.00 |
| 01-701-1028 | 0 | 8.00 | 8.0000000 | 8.0000000 | 8.00 |
DVThe expected analysis variable for ADPPK is
DV or dependent variable. For this example DV
is set to the numeric concentration value PCSTRESN. We will
also include AVAL equivalent to DV for
consistency with CDISC ADaM standards. MDV missing
dependent variable will also be included.
# ---- Derive Analysis Variables ----
# Derive actual dose DOSEA and planned dose DOSEP,
# Derive AVAL and DV
adppk_aval <- adppk_aprlt %>%
mutate(
# Derive Actual Dose
DOSEA = case_when(
EVID == 1 ~ EXDOSE,
is.na(EXDOSE_prev) ~ EXDOSE_first,
TRUE ~ EXDOSE_prev
),
# Derive Planned Dose
DOSEP = case_when(
TRT01P == "Xanomeline High Dose" ~ 81,
TRT01P == "Xanomeline Low Dose" ~ 54,
TRT01P == "Placebo" ~ 0
),
# Derive PARAMCD
PARAMCD = case_when(
EVID == 1 ~ "DOSE",
TRUE ~ PCTESTCD
),
ALLOQ = PCLLOQ,
# Derive CMT
CMT = case_when(
EVID == 1 ~ 1,
TRUE ~ 2
),
# Derive BLQFL/BLQFN
BLQFL = case_when(
PCSTRESC == "<BLQ" ~ "Y",
TRUE ~ "N"
),
BLQFN = case_when(
PCSTRESC == "<BLQ" ~ 1,
TRUE ~ 0
),
AMT = case_when(
EVID == 1 ~ EXDOSE,
TRUE ~ NA_real_
),
# Derive DV and AVAL
DV = PCSTRESN,
AVAL = DV,
DVL = case_when(
DV != 0 ~ log(DV),
TRUE ~ NA_real_
),
# Derive MDV
MDV = case_when(
EVID == 1 ~ 1,
is.na(DV) ~ 1,
TRUE ~ 0
),
AVALU = case_when(
EVID == 1 ~ NA_character_,
TRUE ~ PCSTRESU
),
UDTC = format_ISO8601(ADTM),
II = if_else(EVID == 1, 1, 0),
SS = if_else(EVID == 1, 1, 0)
)| USUBJID | EVID | DOSEA | AMT | NFRLT | AFRLT | CMT | DV | MDV | BLQFN |
|---|---|---|---|---|---|---|---|---|---|
| 01-701-1028 | 0 | 54 | NA | 0.00 | -0.5000000 | 2 | 0.0000000 | 0 | 1 |
| 01-701-1028 | 1 | 54 | 54 | 0.00 | 0.0000000 | 1 | NA | 1 | 0 |
| 01-701-1028 | 0 | 54 | NA | 0.08 | 0.0833333 | 2 | 0.1015662 | 0 | 0 |
| 01-701-1028 | 0 | 54 | NA | 0.50 | 0.5000000 | 2 | 0.5469018 | 0 | 0 |
| 01-701-1028 | 0 | 54 | NA | 1.00 | 1.0000000 | 2 | 0.9254654 | 0 | 0 |
| 01-701-1028 | 0 | 54 | NA | 1.50 | 1.5000000 | 2 | 1.1875059 | 0 | 0 |
| 01-701-1028 | 0 | 54 | NA | 2.00 | 2.0000000 | 2 | 1.3688894 | 0 | 0 |
| 01-701-1028 | 0 | 54 | NA | 4.00 | 4.0000000 | 2 | 1.6831476 | 0 | 0 |
| 01-701-1028 | 0 | 54 | NA | 6.00 | 6.0000000 | 2 | 1.7552923 | 0 | 0 |
| 01-701-1028 | 0 | 54 | NA | 8.00 | 8.0000000 | 2 | 1.7718547 | 0 | 0 |
ASEQ and Remove Temporary VariablesWe derive ASEQ using
derive_var_obs_number(). We add a PROJID based
on drug and include the numeric version PROJIDN, and we
drop and we drop intermediate variables such as those ending with
“_prev”.
# ---- Add ASEQ ----
adppk_aseq <- adppk_aval %>%
# Calculate ASEQ
derive_var_obs_number(
new_var = ASEQ,
by_vars = exprs(STUDYID, USUBJID),
order = exprs(AFRLT, EVID),
check_type = "error"
) %>%
# Derive PARAM and PARAMN
derive_vars_merged(dataset_add = select(param_lookup, -PCTESTCD), by_vars = exprs(PARAMCD)) %>%
mutate(
PROJID = DRUG,
PROJIDN = 1
) %>%
# Remove temporary variables
select(
-DOMAIN, -starts_with("min"), -starts_with("max"), -starts_with("EX"),
-starts_with("PC"), -ends_with("first"), -ends_with("prev"),
-ends_with("DTM"), -ends_with("DT"), -ends_with("TM"), -starts_with("VISIT"),
-starts_with("AVISIT"), -starts_with("PARAM"),
-ends_with("TMF"), -starts_with("TRT"), -starts_with("ATPT"), -DRUG
)| USUBJID | EVID | DOSEA | AMT | NFRLT | AFRLT | CMT | DV | MDV | BLQFN | ASEQ |
|---|---|---|---|---|---|---|---|---|---|---|
| 01-701-1028 | 0 | 54 | NA | 0.00 | -0.5000000 | 2 | 0.0000000 | 0 | 1 | 1 |
| 01-701-1028 | 1 | 54 | 54 | 0.00 | 0.0000000 | 1 | NA | 1 | 0 | 2 |
| 01-701-1028 | 0 | 54 | NA | 0.08 | 0.0833333 | 2 | 0.1015662 | 0 | 0 | 3 |
| 01-701-1028 | 0 | 54 | NA | 0.50 | 0.5000000 | 2 | 0.5469018 | 0 | 0 | 4 |
| 01-701-1028 | 0 | 54 | NA | 1.00 | 1.0000000 | 2 | 0.9254654 | 0 | 0 | 5 |
| 01-701-1028 | 0 | 54 | NA | 1.50 | 1.5000000 | 2 | 1.1875059 | 0 | 0 | 6 |
| 01-701-1028 | 0 | 54 | NA | 2.00 | 2.0000000 | 2 | 1.3688894 | 0 | 0 | 7 |
| 01-701-1028 | 0 | 54 | NA | 4.00 | 4.0000000 | 2 | 1.6831476 | 0 | 0 | 8 |
| 01-701-1028 | 0 | 54 | NA | 6.00 | 6.0000000 | 2 | 1.7552923 | 0 | 0 | 9 |
| 01-701-1028 | 0 | 54 | NA | 8.00 | 8.0000000 | 2 | 1.7718547 | 0 | 0 | 10 |
A key feature of Population PK modeling is the presence of numeric
covariates. We will create numeric versions of many of our standard
CDISC demographic variables including STUDYIDN,
USUBJIDN, SEXN, RACEN, and
ETHNICN.
#---- Derive Covariates ----
# Include numeric values for STUDYIDN, USUBJIDN, SEXN, RACEN etc.
covar <- adsl %>%
mutate(
STUDYIDN = as.numeric(word(USUBJID, 1, sep = fixed("-"))),
SITEIDN = as.numeric(word(USUBJID, 2, sep = fixed("-"))),
USUBJIDN = as.numeric(word(USUBJID, 3, sep = fixed("-"))),
SUBJIDN = as.numeric(SUBJID),
SEXN = case_when(
SEX == "M" ~ 1,
SEX == "F" ~ 2,
TRUE ~ 3
),
RACEN = case_when(
RACE == "AMERICAN INDIAN OR ALASKA NATIVE" ~ 1,
RACE == "ASIAN" ~ 2,
RACE == "BLACK OR AFRICAN AMERICAN" ~ 3,
RACE == "NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER" ~ 4,
RACE == "WHITE" ~ 5,
TRUE ~ 6
),
ETHNICN = case_when(
ETHNIC == "HISPANIC OR LATINO" ~ 1,
ETHNIC == "NOT HISPANIC OR LATINO" ~ 2,
TRUE ~ 3
),
ARMN = case_when(
ARM == "Placebo" ~ 0,
ARM == "Xanomeline Low Dose" ~ 1,
ARM == "Xanomeline High Dose" ~ 2,
TRUE ~ 3
),
ACTARMN = case_when(
ACTARM == "Placebo" ~ 0,
ACTARM == "Xanomeline Low Dose" ~ 1,
ACTARM == "Xanomeline High Dose" ~ 2,
TRUE ~ 3
),
COHORT = ARMN,
COHORTC = ARM,
ROUTE = unique(ex$EXROUTE),
ROUTEN = case_when(
ROUTE == "TRANSDERMAL" ~ 3,
TRUE ~ NA_real_
),
FORM = unique(ex$EXDOSFRM),
FORMN = case_when(
FORM == "PATCH" ~ 3,
TRUE ~ 4
),
COUNTRYN = case_when(
COUNTRY == "USA" ~ 1,
COUNTRY == "CAN" ~ 2,
COUNTRY == "GBR" ~ 3
),
REGION1N = COUNTRYN,
) %>%
select(
STUDYID, STUDYIDN, SITEID, SITEIDN, USUBJID, USUBJIDN,
SUBJID, SUBJIDN, AGE, SEX, SEXN, COHORT, COHORTC, ROUTE, ROUTEN,
RACE, RACEN, ETHNIC, ETHNICN, FORM, FORMN, COUNTRY, COUNTRYN,
REGION1, REGION1N
)| STUDYIDN | USUBJIDN | SITEIDN | AGE | SEXN | RACEN | COHORT | ROUTEN |
|---|---|---|---|---|---|---|---|
| 1 | 1015 | 701 | 63 | 2 | 5 | 0 | 3 |
| 1 | 1023 | 701 | 64 | 1 | 5 | 0 | 3 |
| 1 | 1028 | 701 | 71 | 1 | 5 | 2 | 3 |
| 1 | 1033 | 701 | 74 | 1 | 5 | 1 | 3 |
| 1 | 1034 | 701 | 77 | 2 | 5 | 2 | 3 |
| 1 | 1047 | 701 | 85 | 2 | 5 | 0 | 3 |
| 1 | 1057 | 701 | 59 | 2 | 5 | 3 | 3 |
| 1 | 1097 | 701 | 68 | 1 | 5 | 1 | 3 |
| 1 | 1111 | 701 | 81 | 2 | 5 | 1 | 3 |
| 1 | 1115 | 701 | 84 | 1 | 5 | 1 | 3 |
We will add additional covariates for baseline height
HTBL and weight WTBL from VS and
select baseline lab values CREATBL, ALTBL,
ASTBL and TBILBL from LB. We will
calculate BMI and BSA from height and weight using
compute_bmi() and compute_bsa(). And we will
calculate creatinine clearance CRCLBL and estimated
glomerular filtration rate (eGFR) EGFRBL using
compute_egfr() function.
#---- Derive additional baselines from VS and LB ----
labsbl <- lb %>%
filter(LBBLFL == "Y" & LBTESTCD %in% c("CREAT", "ALT", "AST", "BILI")) %>%
mutate(LBTESTCDB = paste0(LBTESTCD, "BL")) %>%
select(STUDYID, USUBJID, LBTESTCDB, LBSTRESN)
covar_vslb <- covar %>%
derive_vars_merged(
dataset_add = vs,
filter_add = VSTESTCD == "HEIGHT",
by_vars = exprs(STUDYID, USUBJID),
new_vars = exprs(HTBL = VSSTRESN)
) %>%
derive_vars_merged(
dataset_add = vs,
filter_add = VSTESTCD == "WEIGHT" & VSBLFL == "Y",
by_vars = exprs(STUDYID, USUBJID),
new_vars = exprs(WTBL = VSSTRESN)
) %>%
derive_vars_transposed(
dataset_merge = labsbl,
by_vars = exprs(STUDYID, USUBJID),
key_var = LBTESTCDB,
value_var = LBSTRESN
) %>%
mutate(
BMIBL = compute_bmi(height = HTBL, weight = WTBL),
BSABL = compute_bsa(
height = HTBL,
weight = HTBL,
method = "Mosteller"
),
# Derive CRCLBL and EGFRBL using new function
CRCLBL = compute_egfr(
creat = CREATBL, creatu = "SI", age = AGE, weight = WTBL, sex = SEX,
method = "CRCL"
),
EGFRBL = compute_egfr(
creat = CREATBL, creatu = "SI", age = AGE, weight = WTBL, sex = SEX,
method = "CKD-EPI"
)
) %>%
rename(TBILBL = BILIBL)| USUBJIDN | AGE | SEXN | HTBL | WTBL | CREATBL | ALTBL | ASTBL |
|---|---|---|---|---|---|---|---|
| 1015 | 63 | 2 | 147.32 | 54.43 | 79.56 | 27 | 40 |
| 1023 | 64 | 1 | 162.56 | 80.29 | 123.76 | 23 | 21 |
| 1028 | 71 | 1 | 177.80 | 99.34 | 123.76 | 26 | 24 |
| 1033 | 74 | 1 | 175.26 | 88.45 | 132.60 | 16 | 20 |
| 1034 | 77 | 2 | 154.94 | 62.60 | 88.40 | 15 | 23 |
| 1047 | 85 | 2 | 148.59 | 67.13 | 88.40 | 22 | 25 |
| 1057 | 59 | 2 | NA | NA | NA | NA | NA |
| 1097 | 68 | 1 | 168.91 | 78.02 | 123.76 | 16 | 19 |
| 1111 | 81 | 2 | 158.24 | 59.88 | 79.56 | 23 | 28 |
| 1115 | 84 | 1 | 181.61 | 78.93 | 114.92 | 18 | 26 |
ADPPK DataFinally, we combine the covariates with the ADPPK
data.
# Combine covariates with APPPK data
adppk <- adppk_aseq %>%
derive_vars_merged(
dataset_add = covar_vslb,
by_vars = exprs(STUDYID, USUBJID)
) %>%
arrange(STUDYIDN, USUBJIDN, AFRLT, EVID) %>%
mutate(RECSEQ = row_number())| USUBJIDN | AGE | SEXN | CREATBL | EVID | AMT | DV | MDV | RECSEQ |
|---|---|---|---|---|---|---|---|---|
| 1028 | 71 | 1 | 123.76 | 0 | NA | 0.0000000 | 0 | 1 |
| 1028 | 71 | 1 | 123.76 | 1 | 54 | NA | 1 | 2 |
| 1028 | 71 | 1 | 123.76 | 0 | NA | 0.1015662 | 0 | 3 |
| 1028 | 71 | 1 | 123.76 | 0 | NA | 0.5469018 | 0 | 4 |
| 1028 | 71 | 1 | 123.76 | 0 | NA | 0.9254654 | 0 | 5 |
| 1028 | 71 | 1 | 123.76 | 0 | NA | 1.1875059 | 0 | 6 |
| 1028 | 71 | 1 | 123.76 | 0 | NA | 1.3688894 | 0 | 7 |
| 1028 | 71 | 1 | 123.76 | 0 | NA | 1.6831476 | 0 | 8 |
| 1028 | 71 | 1 | 123.76 | 0 | NA | 1.7552923 | 0 | 9 |
| 1028 | 71 | 1 | 123.76 | 0 | NA | 1.7718547 | 0 | 10 |
Adding labels and attributes for SAS transport files is supported by the following packages:
metacore: establish a common foundation for the use of metadata within an R session.
metatools: enable the use of metacore objects. Metatools can be used to build datasets or enhance columns in existing datasets as well as checking datasets against the metadata.
xportr: functionality to associate all metadata information to a local R data frame, perform data set level validation checks and convert into a transport v5 file(xpt).
NOTE: All these packages are in the experimental phase, but the vision is to have them associated with an End to End pipeline under the umbrella of the pharmaverse. An example of applying metadata and perform associated checks can be found at the pharmaverse E2E example.
| ADaM | Sourcing Command |
|---|---|
| ADPC | use_ad_template("ADPC") |
| ADPPK | use_ad_template("ADPPK") |