From 1dba232ce5125342751092ccceda052a687bab1e Mon Sep 17 00:00:00 2001 From: Jeffrey Dickinson Date: Wed, 25 Oct 2023 21:46:11 +0000 Subject: [PATCH 01/10] #12 Add PK ADaM templates --- adam/adpc.qmd | 706 ++++++++++++++++++++++++++++++++++++++++++++++ adam/adppk.qmd | 591 ++++++++++++++++++++++++++++++++++++++ adam/pk_spec.xlsx | Bin 0 -> 40884 bytes 3 files changed, 1297 insertions(+) create mode 100644 adam/adpc.qmd create mode 100644 adam/adppk.qmd create mode 100644 adam/pk_spec.xlsx diff --git a/adam/adpc.qmd b/adam/adpc.qmd new file mode 100644 index 0000000..0c44f90 --- /dev/null +++ b/adam/adpc.qmd @@ -0,0 +1,706 @@ +--- +title: "ADPC Template Walkthrough" +--- + +The Non-compartmental analysis (NCA) ADaM uses the CDISC Implementation Guide (). 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 | + +## First Load Packages + +First we will load the packages required for our project. We will use `{admiral}` for the creation of analysis data. `{admiral}` requires `{dplyr}`, `{lubridate}` and `{stringr}`. We will use `{metacore}` and `{metatools}` to store and manipulate metadata from our specifications. We will use `{xportr}` to perform checks on the final data and export to a transport file. + +The source SDTM data will come from the CDISC pilot study data stored in `{pharmaversesdtm}`. + +```{r echo=TRUE, message=FALSE} +#| label: Load Packages +# Load Packages +library(admiral) +library(dplyr) +library(lubridate) +library(stringr) +library(metacore) +library(metatools) +library(xportr) +library(pharmaversesdtm) +``` + +## Next Load Specifications for Metacore + +We have saved our specifications in an Excel file and will load them into `{metacore}` with the `spec_to_metacore()` function. The spec file can be found [here](https://github.com/pharmaverse/e2e_pk/blob/main/pk_spec.xlsx){target="_blank"}. + +```{r echo=TRUE} +#| label: Load Specs +#| warning: false +# ---- Load Specs for Metacore ---- + +metacore <- spec_to_metacore("pk_spec.xlsx") %>% + select_dataset("ADPC") +``` + +## Load Source Datasets + +We will load are SDTM data from `{pharmaversesdtm}`. The main components of this will be exposure data from `EX` and pharmacokinetic concentration data from `PC`. We will use `ADSL` for baseline characteristics and we will derive additional baselines from vital signs `VS`. + +```{r} +#| label: Load Source +# ---- Load source datasets ---- +# Load PC, EX, VS, LB and ADSL +data("pc") +data("ex") +data("vs") + +data("admiral_adsl") + +adsl <- admiral_adsl +ex <- convert_blanks_to_na(ex) +pc <- convert_blanks_to_na(pc) +vs <- convert_blanks_to_na(vs) +``` + +## Derivations + +### Derive PC Dates + +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. + +```{r} +#| label: PC Dates + +# 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 + +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`. + +```{r} +#| label: Dosing + +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)) +``` + +### Expand Dosing Records + +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. + +```{r} +#| label: Expand +# ---- 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 + +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. + +```{r} +#| label: First Dose + +# ---- 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 + +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`. + +```{r} +#| label: Previous Dose +# ---- 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), + filter_add = NULL, + filter_join = ADTM > ADTM.join, + mode = "last", + check_type = "none" + ) +``` + +### Find Next Dose + +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`. + +```{r} +#| label: Next Dose +# ---- 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), + filter_add = NULL, + filter_join = ADTM <= ADTM.join, + mode = "first", + check_type = "none" + ) +``` + +### Find Previous Nominal Dose + +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`. + +```{r} +#| label: Previous Nominal Dose +# ---- Find previous nominal dose ---- + +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), + filter_add = NULL, + filter_join = NFRLT > NFRLT.join, + mode = "last", + check_type = "none" + ) +``` + +### Find Next Nominal Time + +```{r} +#| label: Next Nominal Dose +# ---- 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), + filter_add = NULL, + filter_join = NFRLT <= NFRLT.join, + mode = "first", + check_type = "none" + ) +``` + +### Combine PC and EX Data + +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. + +```{r} +#| label: Combine + +# ---- 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 Reference + +For nominal relative times we calculate `NRRLT` generally as `NFRLT - NFRLT_prev` and `NXRLT` as `NFRLT - NFRLT_next`. + +```{r} +#| label: Nominal Reference + +# 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 + +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. + +```{r} +#| label: Analysis 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 == " 0 ~ 0.5 * ALLOQ, + TRUE ~ PCSTRESN + ), + AVALU = case_when( + EVID == 1 ~ EXDOSU, + TRUE ~ PCSTRESU + ), + AVALCAT1 = if_else(PCSTRESC == "% + # Add SRCSEQ + mutate( + SRCDOM = DOMAIN, + SRCVAR = "SEQ", + SRCSEQ = coalesce(PCSEQ, EXSEQ) + ) +``` + +### Derive DTYPE Copy Records + +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`. + +```{r} +#| label: DTYPE + +# ---- 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)) +``` + +### Combine Original and DTYPE Copy + +Now 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. + +```{r} +#| label: Combine DTYPE +# ---- 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_), + ) +``` + +### Derive BASE and CHG + +The `{admiral}` function `derive_var_base()` is used to derive `BASE` and the function `derive_var_chg()` is used to derive change from baseline `CHG`. + +```{r} +#| label: BASE + +# ---- Derive BASE and Calculate Change from Baseline ---- + +adpc_base <- adpc_dtype %>% + 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 + +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` using `create_var_from_codelist()` from `{metatools}`. + +```{r} +#| label: ASEQ + +# ---- 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" + ) %>% + # Derive PARAM and PARAMN using metatools + create_var_from_codelist(metacore, input_var = PARAMCD, out_var = PARAM) %>% + create_var_from_codelist(metacore, input_var = PARAMCD, out_var = PARAMN) +``` + +### Derive Additional Baselines + +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. + +```{r} +#| label: Baselines +#---- 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" + ) +``` + +### Combine with ADSL + +If needed, the other `ADSL` variables can now be added: + +```{r} +#| label: Combine with ADSL +# ---- Add all ADSL variables ---- + +# Add all ADSL variables +adpc_prefinal <- adpc_baselines %>% + derive_vars_merged( + dataset_add = select(adsl, !!!negate_vars(adsl_vars)), + by_vars = exprs(STUDYID, USUBJID) + ) +``` + +## Check Data With Metacore + +We use `{metacore}` to perform a number of checks on the data. We will drop variables not in the specs and make sure all the variables from the specs are included. + +```{r} +#| label: Metacore +#| warning: false +# Final Steps, Select final variables and Add labels + +dir <- "." + +# Apply metadata and perform associated checks ---- +# uses {metatools} +adpc <- adpc_prefinal %>% + drop_unspec_vars(metacore) %>% # Drop unspecified variables from specs + check_variables(metacore) %>% # Check all variables specified are present and no more + check_ct_data(metacore) %>% # Checks all variables with CT only contain values within the CT + order_cols(metacore) %>% # Orders the columns according to the spec + sort_by_key(metacore) # Sorts the rows by the sort keys +``` + +## Apply Labels and Formats with xportr + +Using `{xportr}` we check variable type, assign variable lenght, add variable labels, add variable formats, and save a transport file. + +```{r} +#| label: xportr +#| warning: false +adpc_xpt <- adpc %>% + xportr_type(metacore) %>% # Coerce variable type to match spec + xportr_length(metacore) %>% # Assigns SAS length from a variable level metadata + xportr_label(metacore) %>% # Assigns variable label from metacore specifications + xportr_format(metacore) %>% # Assigns variable format from metacore specifications + xportr_df_label(metacore) %>% # Assigns dataset label from metacore specifications + xportr_write(file.path(dir, "adpc.xpt")) # Write xpt v5 transport file +``` + +## Save Final Output + +Finally we save the final output. + +```{r} +#| label: Save +# ---- Save output ---- + +saveRDS(adpc, file = file.path(dir, "adpc.rds"), compress = "bzip2") +``` + +# Example Scripts {#example} + +| ADaM | Sample Code | +|--------------------|----------------------------------------------------| +| ADPC | [ad_adpc_spec.R](https://github.com/pharmaverse/e2e_pk/blob/main/ad_adpc_spec.R){target="_blank"} | + +# Spec File + +[pk_spec.xlsx](https://github.com/pharmaverse/e2e_pk/blob/main/pk_spec.xlsx){target="_blank"} diff --git a/adam/adppk.qmd b/adam/adppk.qmd new file mode 100644 index 0000000..0b10b8a --- /dev/null +++ b/adam/adppk.qmd @@ -0,0 +1,591 @@ +--- +title: "ADPPK Template Walkthrough" +--- + +The Population PK Analysis Data (ADPPK) follows the CDISC Implementation Guide (). 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. + +## First Load Packages + +First we will load the packages required for our project. We will use `{admiral}` for the creation of analysis data. `{admiral}` requires `{dplyr}`, `{lubridate}` and `{stringr}`. We will use `{metacore}` and `{metatools}` to store and manipulate metadata from our specifications. We will use `{xportr}` to perform checks on the final data and export to a transport file. + +The source SDTM data will come from the CDISC pilot study data stored in `{pharmaversesdtm}`. + +```{r echo=TRUE, message=FALSE} +#| label: Load Packages +# Load Packages +library(admiral) +library(dplyr) +library(lubridate) +library(stringr) +library(metacore) +library(metatools) +library(xportr) +library(readr) +library(pharmaversesdtm) +``` + +## Next Load Specifications for Metacore + +We have saved our specifications in an Excel file and will load them into `{metacore}` with the `spec_to_metacore()` function. The spec file can be found [here](https://github.com/pharmaverse/e2e_pk/blob/main/pk_spec.xlsx){target="_blank"} + +```{r echo=TRUE, message=FALSE} +#| label: Load Specs +#| warning: false +# ---- Load Specs for Metacore ---- +metacore <- spec_to_metacore("pk_spec.xlsx") %>% + select_dataset("ADPPK") +``` + +## Load Source Datasets + +We will load are SDTM data from `{pharmaversesdtm}`. The main components of this will be exposure data from `EX` and pharmacokinetic concentration data from `PC`. We will use `ADSL` for baseline characteristics and we will derive additional baselines from vital signs `VS` and laboratory data `LB`. + +```{r} +#| label: Load Source +# ---- Load source datasets ---- +# Load PC, EX, VS, LB and ADSL +data("pc") +data("ex") +data("vs") +data("lb") + +data("admiral_adsl") +adsl <- admiral_adsl + +ex <- convert_blanks_to_na(ex) +pc <- convert_blanks_to_na(pc) +vs <- convert_blanks_to_na(vs) +lb <- convert_blanks_to_na(lb) +``` + +## Derivations + +### Derive PC Dates + +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. + +```{r} +#| label: PC Dates +# ---- 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 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 + +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`. + +```{r} +#| label: Dosing +# ---- 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)) +``` + +### Expand Dosing Records + +The `{admiral}` function `create_single_dose_dataset()` will 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. + +```{r} +#| label: Expand +# ---- 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)) +``` + +### Find First Dose + +We find the first dose for the concentration records using the `{admiral}` function `derive_vars_merged()` + +```{r} +#| label: First Dose +# ---- Find first dose per treatment per subject ---- +# ---- Join with ADPPK 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), + ) +``` + +### Find Previous Dose + +For `ADPPK` we will find the previous dose with respect to actual time and nominal time. We will use \`derive_vars_joined(). + +```{r} +#| label: Previous Dose +# ---- 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), + filter_add = NULL, + filter_join = ADTM > ADTM.join, + mode = "last", + check_type = "none" + ) +``` + +### Find Previous Nominal Dose + +```{r} +#| label: Previous Nominal Dose +# ---- 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), + filter_add = NULL, + filter_join = NFRLT > NFRLT.join, + mode = "last", + check_type = "none" + ) +``` + +### Combine PC and EX Data + +Here we 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. + +```{r} +#| label: Combine +# ---- 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 + ) + ) +``` + +### Derive Analysis Variables + +The 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. + +```{r} +#| label: Analysis Variables +# ---- 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 == "% + # Calculate ASEQ + derive_var_obs_number( + new_var = ASEQ, + by_vars = exprs(STUDYID, USUBJID), + order = exprs(AFRLT, EVID), + check_type = "error" + ) %>% + mutate( + PROJID = DRUG, + PROJIDN = 1, + PART = 1, + ) +``` + +## Derive Covariates Using Metacore + +In this step we will create our numeric covariates using the `create_var_from_codelist()` function from `{metatools}`. + +```{r} +#| label: Covariates +#---- Derive Covariates ---- +# Include numeric values for STUDYIDN, USUBJIDN, SEXN, RACEN etc. + +covar <- adsl %>% + create_var_from_codelist(metacore, input_var = STUDYID, out_var = STUDYIDN) %>% + create_var_from_codelist(metacore, input_var = SEX, out_var = SEXN) %>% + create_var_from_codelist(metacore, input_var = RACE, out_var = RACEN) %>% + create_var_from_codelist(metacore, input_var = ETHNIC, out_var = AETHNIC) %>% + create_var_from_codelist(metacore, input_var = AETHNIC, out_var = AETHNICN) %>% + create_var_from_codelist(metacore, input_var = ARMCD, out_var = COHORT) %>% + create_var_from_codelist(metacore, input_var = ARMCD, out_var = COHORTC) %>% + create_var_from_codelist(metacore, input_var = COUNTRY, out_var = COUNTRYN) %>% + create_var_from_codelist(metacore, input_var = COUNTRY, out_var = COUNTRYL) %>% + 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), + ROUTE = unique(ex$EXROUTE), + FORM = unique(ex$EXDOSFRM), + REGION1 = COUNTRY, + REGION1N = COUNTRYN, + SUBJTYPC = "Volunteer", + ) %>% + create_var_from_codelist(metacore, input_var = FORM, out_var = FORMN) %>% + create_var_from_codelist(metacore, input_var = ROUTE, out_var = ROUTEN) %>% + create_var_from_codelist(metacore, input_var = SUBJTYPC, out_var = SUBJTYP) +``` + +### Derive Additional Baselines + +Next we add additional baselines from vital signs and laboratory data. We will use the `{admiral}` functions `derive_vars_merged()` and `derive_vars_transposed()` to add these. + +```{r} +#| label: Baselines +#---- 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" + ), + 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) +``` + +### Combine with Covariates + +We combine our covariates with the rest of the data + +```{r} +#| label: Combine with Covariates +# Combine covariates with APPPK data + +adppk_prefinal <- adppk_aseq %>% + derive_vars_merged( + dataset_add = select(covar_vslb, !!!negate_vars(adsl_vars)), + by_vars = exprs(STUDYID, USUBJID) + ) %>% + arrange(STUDYIDN, USUBJIDN, AFRLT, EVID) %>% + # Add RECSEQ + # Exclude records if needed + mutate( + RECSEQ = row_number(), + EXCLFCOM = "None" + ) %>% + create_var_from_codelist(metacore, input_var = DVID, out_var = DVIDN) %>% + create_var_from_codelist(metacore, input_var = EXCLFCOM, out_var = EXCLF) +``` + +## Check Data With Metacore + +We use `{metacore}` to perform a number of checks on the data. We will drop variables not in the specs and make sure all the variables from the specs are included. + +```{r} +#| label: Metacore +#| warning: false +# Final Steps, Select final variables and Add labels +# This process will be based on your metadata, no example given for this reason +# ... +dir <- "." + +# Apply metadata and perform associated checks ---- +# uses {metatools} + +adppk <- adppk_prefinal %>% + drop_unspec_vars(metacore) %>% # Drop unspecified variables from specs + check_variables(metacore) %>% # Check all variables specified are present and no more + check_ct_data(metacore) %>% # Checks all variables with CT only contain values within the CT + order_cols(metacore) %>% # Orders the columns according to the spec + sort_by_key(metacore) # Sorts the rows by the sort keys +``` + +## Apply Labels and Formats with xportr + +Using {xportr} we check variable type, assign variable lenght, add variable labels, add variable formats, and save a transport file. + +```{r} +#| label: xportr + +adppk_xpt <- adppk %>% + xportr_type(metacore) %>% # Coerce variable type to match spec + xportr_length(metacore) %>% # Assigns SAS length from a variable level metadata + xportr_label(metacore) %>% # Assigns variable label from metacore specifications + xportr_format(metacore) %>% # Assigns variable format from metacore specifications + xportr_df_label(metacore) %>% # Assigns dataset label from metacore specifications + xportr_write(file.path(dir, "adppk.xpt")) # Write xpt v5 transport file +``` + +## Save Final Output + +Finally we save the final output. We will also create a `CSV` file for the modeler. + +```{r} +#| label: Save +# ---- Save output ---- +saveRDS(adppk, file = file.path(dir, "adppk.rds"), compress = "bzip2") + +# Write CSV +write_csv(adppk_xpt, "adppk.csv") +``` + +# Example Scripts {#example} + +| ADaM | Sample Code | +|----------------|--------------------------------------------------------| +| ADPPK | [ad_adppk_spec.R](https://github.com/pharmaverse/e2e_pk/blob/main/ad_adppk_spec.R){target="_blank"} | + +# Spec File + +[pk_spec.xlsx](https://github.com/pharmaverse/e2e_pk/blob/main/pk_spec.xlsx){target="_blank"} diff --git a/adam/pk_spec.xlsx b/adam/pk_spec.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d96dd466de68aa14a4330f9bb88d632b3bb93ff4 GIT binary patch literal 40884 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 Bc;Em4 literal 0 HcmV?d00001 From 0ec588d89caffa4a1c994f13f5e7b803b071fca2 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Thu, 26 Oct 2023 13:43:32 +0000 Subject: [PATCH 02/10] docs: changing title, ignoring csv, xpt and rds files --- .gitignore | 11 ++++++++++- adam/adpc.qmd | 2 +- adam/adppk.qmd | 2 +- index.qmd | 4 ++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 33e03be..69c4ef7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,13 @@ cache _freeze _site site_libs -.DS_Store \ No newline at end of file +.DS_Store +.Rproj.user +*.csv +*.xpt +*.rds +project.Rproj +.Rhistory +pharmaverse.examples.Rcheck/ +pharmaverse.examples*.tar.gz +pharmaverse.examples*.tgz diff --git a/adam/adpc.qmd b/adam/adpc.qmd index 0c44f90..8884a2f 100644 --- a/adam/adpc.qmd +++ b/adam/adpc.qmd @@ -1,5 +1,5 @@ --- -title: "ADPC Template Walkthrough" +title: "ADPC" --- The Non-compartmental analysis (NCA) ADaM uses the CDISC Implementation Guide (). 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. diff --git a/adam/adppk.qmd b/adam/adppk.qmd index 0b10b8a..3de8940 100644 --- a/adam/adppk.qmd +++ b/adam/adppk.qmd @@ -1,5 +1,5 @@ --- -title: "ADPPK Template Walkthrough" +title: "ADPPK" --- The Population PK Analysis Data (ADPPK) follows the CDISC Implementation Guide (). 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. diff --git a/index.qmd b/index.qmd index 80045ad..9991ea8 100644 --- a/index.qmd +++ b/index.qmd @@ -4,11 +4,11 @@ title: "`pharmaverse` examples" This book contains end-to-end examples of using pharmaverse packages together to achieve common clinical reporting analyses. -The examples use consistent source SDTMs to create ADaMs (such as `ADSL` and `ADAE`) +The examples use consistent source SDTMs to create ADaMs (such as `ADSL`, `ADPC`, `ADPPK`) and using these as input to produce some familiar Tables/Listings/Graphs and associated interactive displays (via Shiny). -Other examples may be included here, e.g. for PK/PD or Therapeutic Area +Other examples may be included here, e.g. Therapeutic Area specifics (such as Oncology or Vaccines). Note that this examples book should only be used to show how collections of From 19196341a8303e6d5ff97f6f4a06b1fb5615de2a Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Thu, 26 Oct 2023 13:52:27 +0000 Subject: [PATCH 03/10] chore: #12 turn off code folding --- _quarto.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_quarto.yml b/_quarto.yml index de1b1fb..026f2b3 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -64,7 +64,7 @@ format: anchor-sections: true smooth-scroll: true code-link: true - code-fold: true + code-fold: false code-overflow: scroll code-line-numbers: true code-copy: true From e79f572f039e3ab2e79f131470564c89f8e8afb1 Mon Sep 17 00:00:00 2001 From: Jeffrey Dickinson Date: Fri, 3 Nov 2023 22:19:19 +0000 Subject: [PATCH 04/10] Chore #12 Update PK templates, add order: to put ADSL first. --- adam/ADSL.qmd | 74 ++++++------------------ adam/adpc.qmd | 149 +++++++------------------------------------------ adam/adppk.qmd | 72 ++++-------------------- 3 files changed, 50 insertions(+), 245 deletions(-) diff --git a/adam/ADSL.qmd b/adam/ADSL.qmd index 3fe2b81..da725b2 100644 --- a/adam/ADSL.qmd +++ b/adam/ADSL.qmd @@ -1,33 +1,24 @@ --- title: "ADSL" +order: 1 --- ## Introduction -This guide will show you how four pharmaverse packages, along with some from -tidyverse, can be used to create an ADaM such as `ADSL` end-to-end, using -`{pharmaversesdtm}` SDTM data as input. +This guide will show you how four pharmaverse packages, along with some from tidyverse, can be used to create an ADaM such as `ADSL` end-to-end, using `{pharmaversesdtm}` SDTM data as input. The four packages used with a brief description of their purpose are as follows: -* [`{metacore}`](https://atorus-research.github.io/metacore/): provides harmonized -metadata/specifications object. -* [`{metatools}`](https://pharmaverse.github.io/metatools/): uses the provided -metadata to build/enhance and check the dataset. -* [`{admiral}`](https://pharmaverse.github.io/admiral/index.html): provides the -ADaM derivations. -* [`{xportr}`](https://atorus-research.github.io/xportr/): delivers the SAS -transport file (XPT) and eSub checks. +- [`{metacore}`](https://atorus-research.github.io/metacore/): provides harmonized metadata/specifications object. +- [`{metatools}`](https://pharmaverse.github.io/metatools/): uses the provided metadata to build/enhance and check the dataset. +- [`{admiral}`](https://pharmaverse.github.io/admiral/index.html): provides the ADaM derivations. +- [`{xportr}`](https://atorus-research.github.io/xportr/): delivers the SAS transport file (XPT) and eSub checks. -It is important to understand `{metacore}` objects by reading through the above -linked package site, as these are fundamental to being able to use `{metatools}` -and `{xportr}`. Each company may need to build a specification reader to create -these objects from their source standard specification templates. +It is important to understand `{metacore}` objects by reading through the above linked package site, as these are fundamental to being able to use `{metatools}` and `{xportr}`. Each company may need to build a specification reader to create these objects from their source standard specification templates. ## Load Data and Required pharmaverse Packages -The first step is to load our pharmaverse packages and input data. -Below shows the versions of each of these package used. +The first step is to load our pharmaverse packages and input data. Below shows the versions of each of these package used. ```{r setup, message=FALSE, warning=FALSE, results='hold'} library(metacore) @@ -61,29 +52,21 @@ metacore <- metacore %>% select_dataset("ADSL") ``` -Here is an example of how a `{metacore}` object looks showing variable level -metadata: +Here is an example of how a `{metacore}` object looks showing variable level metadata: ```{r} metacore$ds_vars ``` - + ## Start Building Derivations -The first derivation step we are going to do is to pull through all the columns -that come directly from the SDTM datasets. You might know which datasets you are -going to pull from directly already, but if you don't you can call -`metatools::build_from_derived()` with just an empty list and the error will tell -you which datasets you need to supply. +The first derivation step we are going to do is to pull through all the columns that come directly from the SDTM datasets. You might know which datasets you are going to pull from directly already, but if you don't you can call `metatools::build_from_derived()` with just an empty list and the error will tell you which datasets you need to supply. ```{r, error=TRUE} build_from_derived(metacore, list(), predecessor_only = FALSE) ``` -In this case all the columns come from `DM` so that is the only dataset we will -pass into `metatools::build_from_derived()`. The resulting dataset has all the -columns combined and any columns that needed renaming between SDTM and ADaM are -renamed. +In this case all the columns come from `DM` so that is the only dataset we will pass into `metatools::build_from_derived()`. The resulting dataset has all the columns combined and any columns that needed renaming between SDTM and ADaM are renamed. ```{r demographcis} adsl_preds <- build_from_derived(metacore, @@ -92,27 +75,15 @@ adsl_preds <- build_from_derived(metacore, head(adsl_preds, n=10) ``` -Now we have the base dataset, we can start to create some variables. We can start -with creating the subgroups using the controlled terminology, in this case `AGEGR1`. -The metacore object holds all the metadata needed to make `ADSL`. Part of that -metadata is the controlled terminology, which can help automate the creation of -subgroups. We can look into the `{metacore}` object and see the controlled -terminology for `AGEGR1`. +Now we have the base dataset, we can start to create some variables. We can start with creating the subgroups using the controlled terminology, in this case `AGEGR1`. The metacore object holds all the metadata needed to make `ADSL`. Part of that metadata is the controlled terminology, which can help automate the creation of subgroups. We can look into the `{metacore}` object and see the controlled terminology for `AGEGR1`. ```{r} get_control_term(metacore, variable = AGEGR1) ``` -Because this controlled terminology is written in a fairly standard format we -can automate the creation of `AGEGR1`. The function `metatools::create_cat_var()` -takes in a `{metacore}` object, a reference variable - in this case `AGE` because -that is the continuous variable `AGEGR1` is created from, and the name of the -sub-grouped variable. It will take the controlled terminology from the sub-grouped -variable and group the reference variables accordingly. +Because this controlled terminology is written in a fairly standard format we can automate the creation of `AGEGR1`. The function `metatools::create_cat_var()` takes in a `{metacore}` object, a reference variable - in this case `AGE` because that is the continuous variable `AGEGR1` is created from, and the name of the sub-grouped variable. It will take the controlled terminology from the sub-grouped variable and group the reference variables accordingly. -Using a similar philosophy we can create the numeric version of `RACE` using the -controlled terminology stored in the `{metacore}` object with the -`metatools::create_var_from_codelist()` function. +Using a similar philosophy we can create the numeric version of `RACE` using the controlled terminology stored in the `{metacore}` object with the `metatools::create_var_from_codelist()` function. ```{r ct} adsl_ct <- adsl_preds %>% @@ -129,13 +100,7 @@ adsl_ct <- adsl_preds %>% head(adsl_ct, n=10) ``` -Now we have sorted out what we can easily do with controlled terminology it is -time to start deriving some variables. -Here you could refer directly to using the `{admiral}` template and [vignette](https://pharmaverse.github.io/admiral/cran-release/articles/adsl.html) -in practice, but for the purpose of this end-to-end ADaM vignette we will share -a few exposure derivations from there. -We derive the start and end of treatment (which requires dates to first be -converted from DTC to DTM), the treatment duration, and the safety population flag. +Now we have sorted out what we can easily do with controlled terminology it is time to start deriving some variables. Here you could refer directly to using the `{admiral}` template and [vignette](https://pharmaverse.github.io/admiral/cran-release/articles/adsl.html) in practice, but for the purpose of this end-to-end ADaM vignette we will share a few exposure derivations from there. We derive the start and end of treatment (which requires dates to first be converted from DTC to DTM), the treatment duration, and the safety population flag. ```{r exposure} ex_ext <- ex %>% @@ -221,12 +186,7 @@ adsl_raw <- adsl_raw %>% ## Apply Metadata to Create an eSub XPT and Perform Associated Checks -Now we have all the variables defined we can run some checks before applying the -necessary formatting. -The top four functions performing checks and sorting/ordering come from -`{metatools}`, whereas the others focused around applying attributes to prepare -for XPT come from `{xportr}`. At the end you could add a call to -`xportr::xportr_write()` to produce the XPT file. +Now we have all the variables defined we can run some checks before applying the necessary formatting. The top four functions performing checks and sorting/ordering come from `{metatools}`, whereas the others focused around applying attributes to prepare for XPT come from `{xportr}`. At the end you could add a call to `xportr::xportr_write()` to produce the XPT file. ```{r checks, warning=FALSE, message=FALSE} diff --git a/adam/adpc.qmd b/adam/adpc.qmd index 8884a2f..4a96a00 100644 --- a/adam/adpc.qmd +++ b/adam/adpc.qmd @@ -1,20 +1,9 @@ --- title: "ADPC" +order: 2 --- -The Non-compartmental analysis (NCA) ADaM uses the CDISC Implementation Guide (). 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 | +The Non-compartmental analysis (NCA) ADaM uses the CDISC Implementation Guide (). 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. For more details see the `{admiral}` [vignette](https://pharmaverse.github.io/admiral/articles/pk_adnca.html){target="_blank"}. ## First Load Packages @@ -37,7 +26,7 @@ library(pharmaversesdtm) ## Next Load Specifications for Metacore -We have saved our specifications in an Excel file and will load them into `{metacore}` with the `spec_to_metacore()` function. The spec file can be found [here](https://github.com/pharmaverse/e2e_pk/blob/main/pk_spec.xlsx){target="_blank"}. +We have saved our specifications in an Excel file and will load them into `{metacore}` with the `spec_to_metacore()` function. ```{r echo=TRUE} #| label: Load Specs @@ -72,18 +61,7 @@ vs <- convert_blanks_to_na(vs) ### Derive PC Dates -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. +Here we use `{admiral}` functions for working with dates and we will also create a nominal time from first dose `NFRLT` for `PC` data based on `PCTPTNUM`. ```{r} #| label: PC Dates @@ -119,7 +97,7 @@ pc_dates <- pc %>% ### Get Dosing Information -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`. +Here we also create nomimal time from first dose `NFRLT` for `EX` data based on `VISITDY`. ```{r} #| label: Dosing @@ -163,16 +141,10 @@ ex_dates <- ex %>% ### Expand Dosing Records -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. +Since there is a start date and end date for dosing records we need to expand the dosing records between the start date and end date using the `{admiral}` function `create_single_dose_dataset()`. ```{r} #| label: Expand -# ---- Expand dosing records between start and end dates ---- -# Updated function includes nominal_time parameter ex_exp <- ex_dates %>% create_single_dose_dataset( @@ -210,14 +182,11 @@ ex_exp <- ex_dates %>% ### Find First Dose -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. +In this section we will find the first dose for each subject and drug. ```{r} #| label: First Dose -# ---- 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, @@ -237,13 +206,12 @@ adpc_first_dose <- pc_dates %>% ) ``` -### Find Previous Dose +### Find Previous Dose and Next 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`. +Use `derive_vars_joined()` to find the previous dose and the next dose. ```{r} -#| label: Previous Dose -# ---- Find previous dose ---- +#| label: Previous Dose and Next Dose adpc_prev <- adpc_first_dose %>% derive_vars_joined( @@ -260,15 +228,6 @@ adpc_prev <- adpc_first_dose %>% mode = "last", check_type = "none" ) -``` - -### Find Next Dose - -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`. - -```{r} -#| label: Next Dose -# ---- Find next dose ---- adpc_next <- adpc_prev %>% derive_vars_joined( @@ -287,13 +246,12 @@ adpc_next <- adpc_prev %>% ) ``` -### Find Previous Nominal Dose +### Find Previous and Next Nominal Dose -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`. +Use the same method to find the previous and next nominal times. ```{r} #| label: Previous Nominal Dose -# ---- Find previous nominal dose ---- adpc_nom_prev <- adpc_next %>% derive_vars_joined( @@ -307,13 +265,6 @@ adpc_nom_prev <- adpc_next %>% mode = "last", check_type = "none" ) -``` - -### Find Next Nominal Time - -```{r} -#| label: Next Nominal Dose -# ---- Find next nominal time ---- adpc_nom_next <- adpc_nom_prev %>% derive_vars_joined( @@ -331,18 +282,11 @@ adpc_nom_next <- adpc_nom_prev %>% ### Combine PC and EX Data -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 `PC` and `EX` records and derive the additional relative time variables. ```{r} #| label: Combine -# ---- Combine ADPC and EX data ---- -# Derive Relative Time Variables - adpc_arrlt <- bind_rows(adpc_nom_next, ex_exp) %>% group_by(USUBJID, DRUG) %>% mutate( @@ -402,7 +346,7 @@ adpc_arrlt <- bind_rows(adpc_nom_next, ex_exp) %>% ### Derive Nominal Reference -For nominal relative times we calculate `NRRLT` generally as `NFRLT - NFRLT_prev` and `NXRLT` as `NFRLT - NFRLT_next`. +For nominal relative times we calculate the nominal relative time to reference dose `NRRLT`. ```{r} #| label: Nominal Reference @@ -425,23 +369,11 @@ adpc_nrrlt <- adpc_arrlt %>% ### Derive Analysis Variables -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. +Here we derive the analysis variables such as `AVAL` and `ATPTREF`. ```{r} #| label: Analysis 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( @@ -508,17 +440,11 @@ adpc_aval <- adpc_nrrlt %>% ### Derive DTYPE Copy Records -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`. +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. ```{r} #| label: DTYPE -# ---- Create DTYPE copy records ---- - dtype <- adpc_aval %>% filter(NFRLT > 0 & NXRLT == 0 & EVID == 0 & !is.na(AVISIT_next)) %>% select(-PCRFTDT, -PCRFTTM) %>% @@ -542,13 +468,10 @@ dtype <- adpc_aval %>% ### Combine Original and DTYPE Copy -Now 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. +Now the duplicated records are combined with the original records. ```{r} #| label: Combine DTYPE -# ---- Combine original records and DTYPE copy records ---- adpc_dtype <- bind_rows(adpc_aval, dtype) %>% arrange(STUDYID, USUBJID, BASETYPE, ADTM, NFRLT) %>% @@ -562,8 +485,6 @@ adpc_dtype <- bind_rows(adpc_aval, dtype) %>% ### Derive BASE and CHG -The `{admiral}` function `derive_var_base()` is used to derive `BASE` and the function `derive_var_chg()` is used to derive change from baseline `CHG`. - ```{r} #| label: BASE @@ -580,11 +501,9 @@ adpc_base <- adpc_dtype %>% adpc_chg <- derive_var_chg(adpc_base) ``` -### Add ASEQ +### Derive `PARAM` with `{metatools}` -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` using `create_var_from_codelist()` from `{metatools}`. +Here we derive `PARAM` and `PARAMN` using `create_var_from_codelist()` from `{metatools}`. ```{r} #| label: ASEQ @@ -606,7 +525,7 @@ adpc_aseq <- adpc_chg %>% ### Derive Additional Baselines -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. +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()`. ```{r} #| label: Baselines @@ -654,12 +573,7 @@ We use `{metacore}` to perform a number of checks on the data. We will drop vari ```{r} #| label: Metacore #| warning: false -# Final Steps, Select final variables and Add labels - -dir <- "." - # Apply metadata and perform associated checks ---- -# uses {metatools} adpc <- adpc_prefinal %>% drop_unspec_vars(metacore) %>% # Drop unspecified variables from specs check_variables(metacore) %>% # Check all variables specified are present and no more @@ -681,26 +595,5 @@ adpc_xpt <- adpc %>% xportr_label(metacore) %>% # Assigns variable label from metacore specifications xportr_format(metacore) %>% # Assigns variable format from metacore specifications xportr_df_label(metacore) %>% # Assigns dataset label from metacore specifications - xportr_write(file.path(dir, "adpc.xpt")) # Write xpt v5 transport file + xportr_write("adpc.xpt") # Write xpt v5 transport file ``` - -## Save Final Output - -Finally we save the final output. - -```{r} -#| label: Save -# ---- Save output ---- - -saveRDS(adpc, file = file.path(dir, "adpc.rds"), compress = "bzip2") -``` - -# Example Scripts {#example} - -| ADaM | Sample Code | -|--------------------|----------------------------------------------------| -| ADPC | [ad_adpc_spec.R](https://github.com/pharmaverse/e2e_pk/blob/main/ad_adpc_spec.R){target="_blank"} | - -# Spec File - -[pk_spec.xlsx](https://github.com/pharmaverse/e2e_pk/blob/main/pk_spec.xlsx){target="_blank"} diff --git a/adam/adppk.qmd b/adam/adppk.qmd index 3de8940..a84c2a0 100644 --- a/adam/adppk.qmd +++ b/adam/adppk.qmd @@ -1,8 +1,9 @@ --- title: "ADPPK" +order: 3 --- -The Population PK Analysis Data (ADPPK) follows the CDISC Implementation Guide (). 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 Population PK Analysis Data (ADPPK) follows the CDISC Implementation Guide (). 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. For more details see the `{admiral}` [vignette](https://pharmaverse.github.io/admiral/articles/pk_adnca.html){target="_blank"}. ## First Load Packages @@ -26,7 +27,7 @@ library(pharmaversesdtm) ## Next Load Specifications for Metacore -We have saved our specifications in an Excel file and will load them into `{metacore}` with the `spec_to_metacore()` function. The spec file can be found [here](https://github.com/pharmaverse/e2e_pk/blob/main/pk_spec.xlsx){target="_blank"} +We have saved our specifications in an Excel file and will load them into `{metacore}` with the `spec_to_metacore()` function. ```{r echo=TRUE, message=FALSE} #| label: Load Specs @@ -62,18 +63,7 @@ lb <- convert_blanks_to_na(lb) ### Derive PC Dates -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. +Here we use `{admiral}` functions for working with dates and we will also create a nominal time from first dose `NFRLT` for `PC` data based on `PCTPTNUM`. ```{r} #| label: PC Dates @@ -109,7 +99,7 @@ pc_dates <- pc %>% ### Get Dosing Information -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`. +Here we also create nominal time from first dose `NFRLT` for `EX` data based on `VISITDY`. ```{r} #| label: Dosing @@ -154,12 +144,10 @@ ex_dates <- ex %>% ### Expand Dosing Records -The `{admiral}` function `create_single_dose_dataset()` will 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. +Since there is a start date and end date for dosing records we need to expand the dosing records between the start date and end date using the `{admiral}` function `create_single_dose_dataset()`. ```{r} #| label: Expand -# ---- Expand dosing records between start and end dates ---- -# Updated function includes nominal_time parameter ex_exp <- ex_dates %>% create_single_dose_dataset( @@ -196,7 +184,7 @@ ex_exp <- ex_dates %>% ### Find First Dose -We find the first dose for the concentration records using the `{admiral}` function `derive_vars_merged()` +In this section we will find the first dose for each subject and drug. ```{r} #| label: First Dose @@ -224,7 +212,7 @@ adppk_first_dose <- pc_dates %>% ### Find Previous Dose -For `ADPPK` we will find the previous dose with respect to actual time and nominal time. We will use \`derive_vars_joined(). +For `ADPPK` we will find the previous dose with respect to actual time and nominal time. ```{r} #| label: Previous Dose @@ -251,7 +239,6 @@ adppk_prev <- adppk_first_dose %>% ```{r} #| label: Previous Nominal Dose -# ---- Find previous nominal dose ---- adppk_nom_prev <- adppk_prev %>% derive_vars_joined( @@ -269,12 +256,10 @@ adppk_nom_prev <- adppk_prev %>% ### Combine PC and EX Data -Here we 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. +Here we 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). ```{r} #| label: Combine -# ---- Combine ADPPK and EX data ---- -# Derive Relative Time Variables adppk_aprlt <- bind_rows(adppk_nom_prev, ex_exp) %>% group_by(USUBJID, DRUG) %>% @@ -397,8 +382,6 @@ adppk_aval <- adppk_aprlt %>% ### Add ASEQ -We add a sequence variable using the `{admiral}` function `derive_var_obs_number()`. - ```{r} #| label: ASEQ # ---- Add ASEQ ---- @@ -418,7 +401,7 @@ adppk_aseq <- adppk_aval %>% ) ``` -## Derive Covariates Using Metacore +## Derive Covariates Using `{metatools}` In this step we will create our numeric covariates using the `create_var_from_codelist()` function from `{metatools}`. @@ -455,11 +438,10 @@ covar <- adsl %>% ### Derive Additional Baselines -Next we add additional baselines from vital signs and laboratory data. We will use the `{admiral}` functions `derive_vars_merged()` and `derive_vars_transposed()` to add these. +Next we add additional baselines from vital signs and laboratory data. ```{r} #| label: Baselines -#---- Derive additional baselines from VS and LB ---- labsbl <- lb %>% filter(LBBLFL == "Y" & LBTESTCD %in% c("CREAT", "ALT", "AST", "BILI")) %>% @@ -535,13 +517,6 @@ We use `{metacore}` to perform a number of checks on the data. We will drop vari ```{r} #| label: Metacore #| warning: false -# Final Steps, Select final variables and Add labels -# This process will be based on your metadata, no example given for this reason -# ... -dir <- "." - -# Apply metadata and perform associated checks ---- -# uses {metatools} adppk <- adppk_prefinal %>% drop_unspec_vars(metacore) %>% # Drop unspecified variables from specs @@ -564,28 +539,5 @@ adppk_xpt <- adppk %>% xportr_label(metacore) %>% # Assigns variable label from metacore specifications xportr_format(metacore) %>% # Assigns variable format from metacore specifications xportr_df_label(metacore) %>% # Assigns dataset label from metacore specifications - xportr_write(file.path(dir, "adppk.xpt")) # Write xpt v5 transport file -``` - -## Save Final Output - -Finally we save the final output. We will also create a `CSV` file for the modeler. - -```{r} -#| label: Save -# ---- Save output ---- -saveRDS(adppk, file = file.path(dir, "adppk.rds"), compress = "bzip2") - -# Write CSV -write_csv(adppk_xpt, "adppk.csv") + xportr_write("adppk.xpt") # Write xpt v5 transport file ``` - -# Example Scripts {#example} - -| ADaM | Sample Code | -|----------------|--------------------------------------------------------| -| ADPPK | [ad_adppk_spec.R](https://github.com/pharmaverse/e2e_pk/blob/main/ad_adppk_spec.R){target="_blank"} | - -# Spec File - -[pk_spec.xlsx](https://github.com/pharmaverse/e2e_pk/blob/main/pk_spec.xlsx){target="_blank"} From 2380843808d688e2dba1dbd5961d6293a6a94b7b Mon Sep 17 00:00:00 2001 From: Jeffrey Dickinson Date: Fri, 3 Nov 2023 22:42:41 +0000 Subject: [PATCH 05/10] #12 add package::function format --- adam/adpc.qmd | 4 ++-- adam/adppk.qmd | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/adam/adpc.qmd b/adam/adpc.qmd index 4a96a00..3a1ff8c 100644 --- a/adam/adpc.qmd +++ b/adam/adpc.qmd @@ -26,7 +26,7 @@ library(pharmaversesdtm) ## Next Load Specifications for Metacore -We have saved our specifications in an Excel file and will load them into `{metacore}` with the `spec_to_metacore()` function. +We have saved our specifications in an Excel file and will load them into `{metacore}` with the `metacore::spec_to_metacore()` function. ```{r echo=TRUE} #| label: Load Specs @@ -141,7 +141,7 @@ ex_dates <- ex %>% ### Expand Dosing Records -Since there is a start date and end date for dosing records we need to expand the dosing records between the start date and end date using the `{admiral}` function `create_single_dose_dataset()`. +Since there is a start date and end date for dosing records we need to expand the dosing records between the start date and end date using the function `admiral::create_single_dose_dataset()`. ```{r} #| label: Expand diff --git a/adam/adppk.qmd b/adam/adppk.qmd index a84c2a0..e34b18c 100644 --- a/adam/adppk.qmd +++ b/adam/adppk.qmd @@ -27,7 +27,7 @@ library(pharmaversesdtm) ## Next Load Specifications for Metacore -We have saved our specifications in an Excel file and will load them into `{metacore}` with the `spec_to_metacore()` function. +We have saved our specifications in an Excel file and will load them into `{metacore}` with the `metacore::spec_to_metacore()` function. ```{r echo=TRUE, message=FALSE} #| label: Load Specs @@ -144,7 +144,7 @@ ex_dates <- ex %>% ### Expand Dosing Records -Since there is a start date and end date for dosing records we need to expand the dosing records between the start date and end date using the `{admiral}` function `create_single_dose_dataset()`. +Since there is a start date and end date for dosing records we need to expand the dosing records between the start date and end date using the function `admiral::create_single_dose_dataset()`. ```{r} #| label: Expand @@ -403,7 +403,7 @@ adppk_aseq <- adppk_aval %>% ## Derive Covariates Using `{metatools}` -In this step we will create our numeric covariates using the `create_var_from_codelist()` function from `{metatools}`. +In this step we will create our numeric covariates using the `metatools::create_var_from_codelist()` function. ```{r} #| label: Covariates From 707b4660bbfe407902753317089c5e2a44bc77ae Mon Sep 17 00:00:00 2001 From: Jeffrey Dickinson Date: Sat, 4 Nov 2023 20:03:34 +0000 Subject: [PATCH 06/10] #12 Comment out call to xportr_write(). --- adam/adpc.qmd | 7 ++++--- adam/adppk.qmd | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/adam/adpc.qmd b/adam/adpc.qmd index 3a1ff8c..f752b7e 100644 --- a/adam/adpc.qmd +++ b/adam/adpc.qmd @@ -584,7 +584,7 @@ adpc <- adpc_prefinal %>% ## Apply Labels and Formats with xportr -Using `{xportr}` we check variable type, assign variable lenght, add variable labels, add variable formats, and save a transport file. +Using `{xportr}` we check variable type, assign variable lenght, add variable labels, add variable formats, and save a transport file. At the end you could add a call to `xportr::xportr_write()` to produce the XPT file. ```{r} #| label: xportr @@ -594,6 +594,7 @@ adpc_xpt <- adpc %>% xportr_length(metacore) %>% # Assigns SAS length from a variable level metadata xportr_label(metacore) %>% # Assigns variable label from metacore specifications xportr_format(metacore) %>% # Assigns variable format from metacore specifications - xportr_df_label(metacore) %>% # Assigns dataset label from metacore specifications - xportr_write("adpc.xpt") # Write xpt v5 transport file + xportr_df_label(metacore) # Assigns dataset label from metacore specifications + # xportr_write("adpc.xpt") # Write xpt v5 transport file + ``` diff --git a/adam/adppk.qmd b/adam/adppk.qmd index e34b18c..1008525 100644 --- a/adam/adppk.qmd +++ b/adam/adppk.qmd @@ -528,7 +528,7 @@ adppk <- adppk_prefinal %>% ## Apply Labels and Formats with xportr -Using {xportr} we check variable type, assign variable lenght, add variable labels, add variable formats, and save a transport file. +Using {xportr} we check variable type, assign variable lenght, add variable labels, add variable formats, and save a transport file with `xportr::xportr_write()`. ```{r} #| label: xportr @@ -538,6 +538,7 @@ adppk_xpt <- adppk %>% xportr_length(metacore) %>% # Assigns SAS length from a variable level metadata xportr_label(metacore) %>% # Assigns variable label from metacore specifications xportr_format(metacore) %>% # Assigns variable format from metacore specifications - xportr_df_label(metacore) %>% # Assigns dataset label from metacore specifications - xportr_write("adppk.xpt") # Write xpt v5 transport file + xportr_df_label(metacore) # Assigns dataset label from metacore specifications + # xportr_write("adppk.xpt") # Write xpt v5 transport file + ``` From 9db29ebaf27f985da405719562061f3b7d8ba4b8 Mon Sep 17 00:00:00 2001 From: Jeffrey Dickinson Date: Mon, 6 Nov 2023 18:34:37 +0000 Subject: [PATCH 07/10] #12 add tempdir --- adam/adpc.qmd | 6 ++++-- adam/adppk.qmd | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/adam/adpc.qmd b/adam/adpc.qmd index f752b7e..6c6a043 100644 --- a/adam/adpc.qmd +++ b/adam/adpc.qmd @@ -589,12 +589,14 @@ Using `{xportr}` we check variable type, assign variable lenght, add variable la ```{r} #| label: xportr #| warning: false +dir <- tempdir() # Change to whichever directory you want to save the dataset in + adpc_xpt <- adpc %>% xportr_type(metacore) %>% # Coerce variable type to match spec xportr_length(metacore) %>% # Assigns SAS length from a variable level metadata xportr_label(metacore) %>% # Assigns variable label from metacore specifications xportr_format(metacore) %>% # Assigns variable format from metacore specifications - xportr_df_label(metacore) # Assigns dataset label from metacore specifications - # xportr_write("adpc.xpt") # Write xpt v5 transport file + xportr_df_label(metacore) %>% # Assigns dataset label from metacore specifications + xportr_write(file.path(dir, "adpc.xpt")) # Write xpt v5 transport file ``` diff --git a/adam/adppk.qmd b/adam/adppk.qmd index 1008525..202cbb5 100644 --- a/adam/adppk.qmd +++ b/adam/adppk.qmd @@ -532,13 +532,14 @@ Using {xportr} we check variable type, assign variable lenght, add variable labe ```{r} #| label: xportr +dir <- tempdir() # Change to whichever directory you want to save the dataset in adppk_xpt <- adppk %>% xportr_type(metacore) %>% # Coerce variable type to match spec xportr_length(metacore) %>% # Assigns SAS length from a variable level metadata xportr_label(metacore) %>% # Assigns variable label from metacore specifications xportr_format(metacore) %>% # Assigns variable format from metacore specifications - xportr_df_label(metacore) # Assigns dataset label from metacore specifications - # xportr_write("adppk.xpt") # Write xpt v5 transport file + xportr_df_label(metacore) %>% # Assigns dataset label from metacore specifications + xportr_write(file.path(dir, "adppk.xpt")) # Write xpt v5 transport file ``` From 61269d37cc4f25a1a6d3a5aed14345029d50e8ea Mon Sep 17 00:00:00 2001 From: Ross Farrugia <82581364+rossfarrugia@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:00:16 +0000 Subject: [PATCH 08/10] Rename ADSL.qmd to adsl.qmd --- adam/{ADSL.qmd => adsl.qmd} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename adam/{ADSL.qmd => adsl.qmd} (100%) diff --git a/adam/ADSL.qmd b/adam/adsl.qmd similarity index 100% rename from adam/ADSL.qmd rename to adam/adsl.qmd From f399508426e6c772e9792b3d526b3d9d5615b957 Mon Sep 17 00:00:00 2001 From: Ross Farrugia <82581364+rossfarrugia@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:01:53 +0000 Subject: [PATCH 09/10] explain section uses metacore and metatools --- adam/adpc.qmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adam/adpc.qmd b/adam/adpc.qmd index 6c6a043..90cb93c 100644 --- a/adam/adpc.qmd +++ b/adam/adpc.qmd @@ -566,9 +566,9 @@ adpc_prefinal <- adpc_baselines %>% ) ``` -## Check Data With Metacore +## Check Data With metacore and metatools -We use `{metacore}` to perform a number of checks on the data. We will drop variables not in the specs and make sure all the variables from the specs are included. +We use `{metacore}` objects with `{metatools}` functions to perform a number of checks on the data. We will drop variables not in the specs and make sure all the variables from the specs are included. ```{r} #| label: Metacore From 44bec2a2fd4d05b0f01375dafe96a27e2e54bf76 Mon Sep 17 00:00:00 2001 From: Ross Farrugia <82581364+rossfarrugia@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:02:40 +0000 Subject: [PATCH 10/10] explain metacore and metatools used --- adam/adppk.qmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adam/adppk.qmd b/adam/adppk.qmd index 202cbb5..f7baa4b 100644 --- a/adam/adppk.qmd +++ b/adam/adppk.qmd @@ -510,9 +510,9 @@ adppk_prefinal <- adppk_aseq %>% create_var_from_codelist(metacore, input_var = EXCLFCOM, out_var = EXCLF) ``` -## Check Data With Metacore +## Check Data With metacore and metatools -We use `{metacore}` to perform a number of checks on the data. We will drop variables not in the specs and make sure all the variables from the specs are included. +We use `{metacore}` objects with `{metatools}` functions to perform a number of checks on the data. We will drop variables not in the specs and make sure all the variables from the specs are included. ```{r} #| label: Metacore