diff --git a/NEWS.md b/NEWS.md index c13f0a91..b45b0c97 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,20 @@ the dosing including dose amount and route. # PKNCA 0.12.2 +## Features added + +* `add.interval.col()` gains `pptestcd_cdisc` and `pptest_cdisc` arguments for + CDISC standard parameter code and name mappings. Route-dependent parameters + (CL, VZ, MRT, VSS) accept a nested list to distinguish intravascular and + extravascular CDISC codes (#403) +* `as.data.frame.PKNCAresults()` gains `out_format = "cdisc"` to translate + PPTESTCD to CDISC standard codes and add a PPTEST column. Route-dependent + translations are resolved from the dose data (#403) +* When `out_format = "cdisc"` and any parameter has "INT" in its PPTESTCD, + PPSTINT and PPENINT columns are added with ISO 8601 durations relative to + the last dose time. The time unit is taken from `timeu_pref` or `timeu` + (#403) + ## Bug Fixes * `normalize.data.frame()` no longer triggers a dplyr deprecation warning diff --git a/R/001-add.interval.col.R b/R/001-add.interval.col.R index da794d84..47cda7a2 100644 --- a/R/001-add.interval.col.R +++ b/R/001-add.interval.col.R @@ -23,6 +23,13 @@ assign("interval.cols", list(), envir=.PKNCAEnv) #' to NCA parameter names. See the details for information on use of #' `formalsmap`. #' @param datatype The type of data used for the calculation +#' @param pptestcd_cdisc The CDISC PPTESTCD code for this parameter. Can be a +#' character string for simple mappings, or a named list for route-dependent +#' mappings (e.g., `list(route = list(extravascular = "CLF/FO", intravascular +#' = "CLO"))`). Defaults to `name` if not provided. +#' @param pptest_cdisc The CDISC PPTEST name for this parameter. Can be a +#' character string or a named list (same structure as `pptestcd_cdisc`). +#' Defaults to `desc` if not provided. #' @returns NULL (Calling this function has a side effect of changing the #' available intervals for calculations) #' @@ -90,7 +97,9 @@ add.interval.col <- function(name, formalsmap=list(), datatype=c("interval", "individual", - "population")) { + "population"), + pptestcd_cdisc=NULL, + pptest_cdisc=NULL) { # Check inputs if (!is.character(name)) { stop("name must be a character string") @@ -159,6 +168,21 @@ add.interval.col <- function(name, stop("All names for the formalsmap list must be arguments to the function.") } } + # Default CDISC mappings to name/desc when not provided + if (is.null(pptestcd_cdisc)) { + pptestcd_cdisc <- name + } + if (is.null(pptest_cdisc)) { + pptest_cdisc <- desc + } + # Validate CDISC arguments: must be a character string or a named list + # with a "route" element containing named sub-elements + if (!is.character(pptestcd_cdisc) && !is.list(pptestcd_cdisc)) { + stop("pptestcd_cdisc must be a character string or a list") + } + if (!is.character(pptest_cdisc) && !is.list(pptest_cdisc)) { + stop("pptest_cdisc must be a character string or a list") + } current <- get("interval.cols", envir=.PKNCAEnv) current[[name]] <- list( @@ -170,7 +194,9 @@ add.interval.col <- function(name, sparse=sparse, formalsmap=formalsmap, depends=depends, - datatype=datatype + datatype=datatype, + pptestcd_cdisc=pptestcd_cdisc, + pptest_cdisc=pptest_cdisc ) assign("interval.cols", current, envir=.PKNCAEnv) } diff --git a/R/auc.R b/R/auc.R index 19e4f685..f05af15c 100644 --- a/R/auc.R +++ b/R/auc.R @@ -313,7 +313,9 @@ add.interval.col("aucinf.obs", unit_type="auc", pretty_name="AUCinf,obs", desc="The area under the concentration time curve from the beginning of the interval to infinity with extrapolation to infinity from the observed Clast", - depends=c("lambda.z", "clast.obs")) + depends=c("lambda.z", "clast.obs"), + pptestcd_cdisc="AUCIFO", + pptest_cdisc="AUC Infinity Obs") add.interval.col("aucinf.pred", FUN="pk.calc.auc.inf.pred", @@ -321,21 +323,27 @@ add.interval.col("aucinf.pred", unit_type="auc", pretty_name="AUCinf,pred", desc="The area under the concentration time curve from the beginning of the interval to infinity with extrapolation to infinity from the predicted Clast", - depends=c("lambda.z", "clast.pred")) + depends=c("lambda.z", "clast.pred"), + pptestcd_cdisc="AUCIFP", + pptest_cdisc="AUC Infinity Pred") add.interval.col("auclast", FUN="pk.calc.auc.last", values=c(FALSE, TRUE), unit_type="auc", pretty_name="AUClast", - desc="The area under the concentration time curve from the beginning of the interval to the last concentration above the limit of quantification") + desc="The area under the concentration time curve from the beginning of the interval to the last concentration above the limit of quantification", + pptestcd_cdisc="AUCLST", + pptest_cdisc="AUC to Last Nonzero Conc") add.interval.col("aucall", FUN="pk.calc.auc.all", values=c(FALSE, TRUE), unit_type="auc", pretty_name="AUCall", - desc="The area under the concentration time curve from the beginning of the interval to the last concentration above the limit of quantification plus the triangle from that last concentration to 0 at the first concentration below the limit of quantification" + desc="The area under the concentration time curve from the beginning of the interval to the last concentration above the limit of quantification plus the triangle from that last concentration to 0 at the first concentration below the limit of quantification", + pptestcd_cdisc="AUCALL", + pptest_cdisc="AUC All" ) add.interval.col("aumcinf.obs", @@ -344,7 +352,9 @@ add.interval.col("aumcinf.obs", unit_type="aumc", pretty_name="AUMC,inf,obs", desc="The area under the concentration time moment curve from the beginning of the interval to infinity with extrapolation to infinity from the observed Clast", - depends=c("lambda.z", "clast.obs")) + depends=c("lambda.z", "clast.obs"), + pptestcd_cdisc="AUMCIFO", + pptest_cdisc="AUMC Infinity Obs") add.interval.col("aumcinf.pred", FUN="pk.calc.aumc.inf.pred", @@ -352,21 +362,27 @@ add.interval.col("aumcinf.pred", unit_type="aumc", pretty_name="AUMC,inf,pred", desc="The area under the concentration time moment curve from the beginning of the interval to infinity with extrapolation to infinity from the predicted Clast", - depends=c("lambda.z", "clast.pred")) + depends=c("lambda.z", "clast.pred"), + pptestcd_cdisc="AUMCIFP", + pptest_cdisc="AUMC Infinity Pred") add.interval.col("aumclast", FUN="pk.calc.aumc.last", values=c(FALSE, TRUE), unit_type="aumc", pretty_name="AUMC,last", - desc="The area under the concentration time moment curve from the beginning of the interval to the last concentration above the limit of quantification") + desc="The area under the concentration time moment curve from the beginning of the interval to the last concentration above the limit of quantification", + pptestcd_cdisc="AUMCLST", + pptest_cdisc="AUMC to Last Nonzero Conc") add.interval.col("aumcall", FUN="pk.calc.aumc.all", values=c(FALSE, TRUE), unit_type="aumc", pretty_name="AUMC,all", - desc="The area under the concentration time moment curve from the beginning of the interval to the last concentration above the limit of quantification plus the moment of the triangle from that last concentration to 0 at the first concentration below the limit of quantification") + desc="The area under the concentration time moment curve from the beginning of the interval to the last concentration above the limit of quantification plus the moment of the triangle from that last concentration to 0 at the first concentration below the limit of quantification", + pptestcd_cdisc="AUMCALL", + pptest_cdisc="AUMC All") PKNCA.set.summary( name= diff --git a/R/aucint.R b/R/aucint.R index fb550bec..d183bf4a 100644 --- a/R/aucint.R +++ b/R/aucint.R @@ -243,7 +243,9 @@ add.interval.col("aucint.last", unit_type="auc", pretty_name="AUCint (based on AUClast extrapolation)", desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity with zeros (matching AUClast)", - formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL)) + formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL), + pptestcd_cdisc="AUCINT", + pptest_cdisc="AUC from T1 to T2") PKNCA.set.summary( name="aucint.last", description="geometric mean and geometric coefficient of variation", @@ -257,7 +259,9 @@ add.interval.col("aucint.last.dose", unit_type="auc", pretty_name="AUCint (based on AUClast extrapolation, dose-aware)", desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity with zeros (matching AUClast) with dose-aware interpolation/extrapolation of concentrations", - formalsmap=list(conc="conc.group", time="time.group", time.dose="time.dose.group")) + formalsmap=list(conc="conc.group", time="time.group", time.dose="time.dose.group"), + pptestcd_cdisc="AUCINTD", + pptest_cdisc="AUC from T1 to T2 Normalized by Dose") PKNCA.set.summary( name="aucint.last.dose", description="geometric mean and geometric coefficient of variation", @@ -271,7 +275,9 @@ add.interval.col("aucint.all", unit_type="auc", pretty_name="AUCint (based on AUCall extrapolation)", desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity with the triangle from Tlast to the next point and zero thereafter (matching AUCall)", - formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL)) + formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL), + pptestcd_cdisc="AUCINTA", + pptest_cdisc="AUCint (based on AUCall extrapolation)") PKNCA.set.summary( name="aucint.all", description="geometric mean and geometric coefficient of variation", @@ -285,7 +291,9 @@ add.interval.col("aucint.all.dose", unit_type="auc", pretty_name="AUCint (based on AUCall extrapolation, dose-aware)", desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity with the triangle from Tlast to the next point and zero thereafter (matching AUCall) with dose-aware interpolation/extrapolation of concentrations", - formalsmap=list(conc="conc.group", time="time.group", time.dose="time.dose.group")) + formalsmap=list(conc="conc.group", time="time.group", time.dose="time.dose.group"), + pptestcd_cdisc="AUCINTAD", + pptest_cdisc="AUCint (based on AUCall extrapolation, dose-aware)") PKNCA.set.summary( name="aucint.all.dose", description="geometric mean and geometric coefficient of variation", @@ -300,7 +308,9 @@ add.interval.col("aucint.inf.obs", pretty_name="AUCint (based on AUCinf,obs extrapolation)", desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity with zeros (matching AUClast)", formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL), - depends=c("lambda.z", "clast.obs")) + depends=c("lambda.z", "clast.obs"), + pptestcd_cdisc="AUCINTIS", + pptest_cdisc="AUCint (based on AUCinf,obs extrapolation)") PKNCA.set.summary( name="aucint.inf.obs", description="geometric mean and geometric coefficient of variation", @@ -315,7 +325,9 @@ add.interval.col("aucint.inf.obs.dose", pretty_name="AUCint (based on AUCinf,obs extrapolation, dose-aware)", desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity with zeros (matching AUClast) with dose-aware interpolation/extrapolation of concentrations", formalsmap=list(conc="conc.group", time="time.group", time.dose="time.dose.group"), - depends=c("lambda.z", "clast.obs")) + depends=c("lambda.z", "clast.obs"), + pptestcd_cdisc="AUCINTID", + pptest_cdisc="AUCint (based on AUCinf,obs extrapolation, dose-aware)") PKNCA.set.summary( name="aucint.inf.obs.dose", description="geometric mean and geometric coefficient of variation", @@ -330,7 +342,9 @@ add.interval.col("aucint.inf.pred", pretty_name="AUCint (based on AUCinf,pred extrapolation)", desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity with the triangle from Tlast to the next point and zero thereafter (matching AUCall)", formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL), - depends=c("lambda.z", "clast.pred")) + depends=c("lambda.z", "clast.pred"), + pptestcd_cdisc="AUCINTIP", + pptest_cdisc="AUCint (based on AUCinf,pred extrapolation)") PKNCA.set.summary( name="aucint.inf.pred", description="geometric mean and geometric coefficient of variation", @@ -345,7 +359,9 @@ add.interval.col("aucint.inf.pred.dose", pretty_name="AUCint (based on AUCinf,pred extrapolation, dose-aware)", desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity with the triangle from Tlast to the next point and zero thereafter (matching AUCall) with dose-aware interpolation/extrapolation of concentrations", formalsmap=list(conc="conc.group", time="time.group", time.dose="time.dose.group"), - depends=c("lambda.z", "clast.pred")) + depends=c("lambda.z", "clast.pred"), + pptestcd_cdisc="AUCINTPD", + pptest_cdisc="AUCint (based on AUCinf,pred extrapolation, dose-aware)") PKNCA.set.summary( name="aucint.inf.pred.dose", description="geometric mean and geometric coefficient of variation", diff --git a/R/auciv.R b/R/auciv.R index 81ec38c7..45f9707b 100644 --- a/R/auciv.R +++ b/R/auciv.R @@ -52,7 +52,9 @@ add.interval.col( depends = c("auclast", "c0"), desc = "The AUClast calculated with back-extrapolation for intravenous dosing using extrapolated C0", sparse = FALSE, - formalsmap = list(auc="auclast") + formalsmap = list(auc="auclast"), + pptestcd_cdisc="AUCIVLST", + pptest_cdisc="AUClast (IV dosing)" ) add.interval.col( @@ -63,7 +65,9 @@ add.interval.col( depends = c("aucall", "c0"), desc = "The AUCall calculated with back-extrapolation for intravenous dosing using extrapolated C0", sparse = FALSE, - formalsmap = list(auc="aucall") + formalsmap = list(auc="aucall"), + pptestcd_cdisc="AUCIVA", + pptest_cdisc="AUCall (IV dosing)" ) add.interval.col( @@ -74,7 +78,9 @@ add.interval.col( depends = c("aucint.last", "c0"), desc = "The AUCint,last calculated with back-extrapolation for intravenous dosing using extrapolated C0", sparse = FALSE, - formalsmap = list(auc="aucint.last") + formalsmap = list(auc="aucint.last"), + pptestcd_cdisc="AUCIVILT", + pptest_cdisc="AUCint,last (IV dosing)" ) add.interval.col( @@ -85,7 +91,9 @@ add.interval.col( depends = c("aucint.all", "c0"), desc = "The AUCint,all calculated with back-extrapolation for intravenous dosing using extrapolated C0", sparse = FALSE, - formalsmap = list(auc="aucint.all") + formalsmap = list(auc="aucint.all"), + pptestcd_cdisc="AUCIVINA", + pptest_cdisc="AUCint,all (IV dosing)" ) add.interval.col( @@ -96,7 +104,9 @@ add.interval.col( depends = c("aucinf.obs", "c0"), desc = "The AUCinf,obs calculated with back-extrapolation for intravenous dosing using extrapolated C0", sparse = FALSE, - formalsmap = list(auc="aucinf.obs") + formalsmap = list(auc="aucinf.obs"), + pptestcd_cdisc="AUCIVIS", + pptest_cdisc="AUCinf,obs (IV dosing)" ) add.interval.col( @@ -107,7 +117,9 @@ add.interval.col( depends = c("aucinf.pred", "c0"), desc = "The AUCinf,pred calculated with back-extrapolation for intravenous dosing using extrapolated C0", sparse = FALSE, - formalsmap = list(auc="aucinf.pred") + formalsmap = list(auc="aucinf.pred"), + pptestcd_cdisc="AUCIVIP", + pptest_cdisc="AUCinf,pred (IV dosing)" ) PKNCA.set.summary( @@ -135,7 +147,9 @@ add.interval.col( depends = c("auclast", "aucivlast"), desc = "The back-extrapolation percent for intravenous dosing based on AUClast", sparse = FALSE, - formalsmap = list(auc="auclast", auciv="aucivlast") + formalsmap = list(auc="auclast", auciv="aucivlast"), + pptestcd_cdisc="AUCIVPLT", + pptest_cdisc="AUCbext (based on AUClast)" ) add.interval.col( @@ -146,7 +160,9 @@ add.interval.col( depends = c("aucall", "aucivall"), desc = "The back-extrapolation percent for intravenous dosing based on AUCall", sparse = FALSE, - formalsmap = list(auc="aucall", auciv="aucivall") + formalsmap = list(auc="aucall", auciv="aucivall"), + pptestcd_cdisc="AUCIVPEA", + pptest_cdisc="AUCbext (based on AUCall)" ) add.interval.col( @@ -157,7 +173,9 @@ add.interval.col( depends = c("aucint.last", "aucivint.last"), desc = "The back-extrapolation percent for intravenous dosing based on AUCint,last", sparse = FALSE, - formalsmap = list(auc="aucint.last", auciv="aucivint.last") + formalsmap = list(auc="aucint.last", auciv="aucivint.last"), + pptestcd_cdisc="AUCIVPIL", + pptest_cdisc="AUCbext (based on AUCint,last)" ) add.interval.col( @@ -168,7 +186,9 @@ add.interval.col( depends = c("aucint.all", "aucivint.all"), desc = "The back-extrapolation percent for intravenous dosing based on AUCint,all", sparse = FALSE, - formalsmap = list(auc="aucint.all", auciv="aucivint.all") + formalsmap = list(auc="aucint.all", auciv="aucivint.all"), + pptestcd_cdisc="AUCIVPIA", + pptest_cdisc="AUCbext (based on AUCint,all)" ) add.interval.col( @@ -179,7 +199,9 @@ add.interval.col( depends = c("aucinf.obs", "aucivinf.obs"), desc = "The back-extrapolation percent for intravenous dosing based on AUCinf,obs", sparse = FALSE, - formalsmap = list(auc="aucinf.obs", auciv="aucivinf.obs") + formalsmap = list(auc="aucinf.obs", auciv="aucivinf.obs"), + pptestcd_cdisc="AUCIVPEI", + pptest_cdisc="AUCbext (based on AUCinf,obs)" ) add.interval.col( @@ -190,7 +212,9 @@ add.interval.col( depends = c("aucinf.pred", "aucivinf.pred"), desc = "The back-extrapolation percent for intravenous dosing based on AUCinf,pred", sparse = FALSE, - formalsmap = list(auc="aucinf.pred", auciv="aucivinf.pred") + formalsmap = list(auc="aucinf.pred", auciv="aucivinf.pred"), + pptestcd_cdisc="AUCIVPEP", + pptest_cdisc="AUCbext (based on AUCinf,pred)" ) PKNCA.set.summary( diff --git a/R/class-PKNCAresults.R b/R/class-PKNCAresults.R index e83f2dfb..259dae29 100644 --- a/R/class-PKNCAresults.R +++ b/R/class-PKNCAresults.R @@ -34,14 +34,18 @@ PKNCAresults <- function(result, data, exclude = NULL) { #' #' @param x The object to extract results from #' @param ... Ignored (for compatibility with generic [as.data.frame()]) -#' @param out_format Should the output be 'long' (default) or 'wide'? +#' @param out_format Should the output be 'long' (default), 'wide', or 'cdisc'? +#' When 'cdisc', the PPTESTCD column is translated to CDISC standard codes +#' and a PPTEST column with the CDISC test name is added. Route-dependent +#' parameters (e.g. CL, VZ, MRT) are resolved using the route information +#' from the dose data. #' @param filter_requested Only return rows with parameters that were #' specifically requested? #' @param filter_excluded Should excluded values be removed? #' @param out.format Deprecated in favor of `out_format` #' @returns A data.frame (or usually a tibble) of results #' @export -as.data.frame.PKNCAresults <- function(x, ..., out_format = c('long', 'wide'), filter_requested = FALSE, filter_excluded = FALSE, out.format = deprecated()) { +as.data.frame.PKNCAresults <- function(x, ..., out_format = c('long', 'wide', 'cdisc'), filter_requested = FALSE, filter_excluded = FALSE, out.format = deprecated()) { if (!filter_excluded) { ret <- x$result } else { @@ -77,7 +81,9 @@ as.data.frame.PKNCAresults <- function(x, ..., out_format = c('long', 'wide'), f ) } - if (out_format %in% 'wide') { + if (out_format %in% 'cdisc') { + ret <- pknca_cdisc_translate(ret, x) + } else if (out_format %in% 'wide') { if ("PPSTRESU" %in% names(ret)) { # Use standardized results ret$PPTESTCD <- sprintf("%s (%s)", ret$PPTESTCD, ret$PPSTRESU) @@ -95,6 +101,260 @@ as.data.frame.PKNCAresults <- function(x, ..., out_format = c('long', 'wide'), f ret } +# Translate PPTESTCD to CDISC standard codes and add PPTEST column +# +# Also adds PPSTINT and PPENINT columns (ISO 8601 durations relative to the +# last dose time) when any resolved PPTESTCD contains "INT". +# +# @param ret The long-format result data.frame +# @param x The PKNCAresults object (for accessing dose/route data) +# @returns The data.frame with PPTESTCD translated and PPTEST added +# @keywords Internal +# @noRd +pknca_cdisc_translate <- function(ret, x) { + all_intervals <- get.interval.cols() + # Determine route for each result row + route_per_row <- pknca_cdisc_get_route(ret, x) + # Build CDISC PPTESTCD and PPTEST for each row + cdisc_pptestcd <- character(nrow(ret)) + cdisc_pptest <- character(nrow(ret)) + for (i in seq_len(nrow(ret))) { + pknca_name <- ret$PPTESTCD[i] + col_def <- all_intervals[[pknca_name]] + if (is.null(col_def) || is.null(col_def$pptestcd_cdisc)) { + # No mapping available, keep original + cdisc_pptestcd[i] <- pknca_name + cdisc_pptest[i] <- if (!is.null(col_def$desc)) col_def$desc else "" + next + } + route <- route_per_row[i] + cdisc_pptestcd[i] <- resolve_cdisc_value(col_def$pptestcd_cdisc, route) + cdisc_pptest[i] <- resolve_cdisc_value(col_def$pptest_cdisc, route) + } + ret$PPTESTCD <- cdisc_pptestcd + # Insert PPTEST after PPTESTCD + pptestcd_pos <- which(names(ret) == "PPTESTCD") + if (length(pptestcd_pos) == 1) { + before <- ret[, seq_len(pptestcd_pos), drop = FALSE] + after <- ret[, seq(pptestcd_pos + 1, ncol(ret)), drop = FALSE] + ret <- cbind(before, PPTEST = cdisc_pptest, after) + } else { + ret$PPTEST <- cdisc_pptest + } + # Add PPSTINT/PPENINT if any PPTESTCD contains "INT" + has_int <- grepl("INT", cdisc_pptestcd, fixed = TRUE) + if (any(has_int)) { + ret <- pknca_cdisc_add_interval_columns(ret, x, has_int) + } + ret +} + +# Add PPSTINT and PPENINT columns for interval-based parameters +# +# These columns express the interval start and end as ISO 8601 durations +# relative to the last dose time for each subject/group. +# +# @param ret The result data.frame (already has PPTESTCD translated) +# @param x The PKNCAresults object +# @param has_int Logical vector indicating which rows have INT parameters +# @returns The data.frame with PPSTINT and PPENINT columns added +# @keywords Internal +# @noRd +pknca_cdisc_add_interval_columns <- function(ret, x, has_int) { + timeu <- pknca_cdisc_get_timeu(x) + last_dose_times <- pknca_cdisc_get_last_dose_time(ret, x) + ppstint <- rep(NA_character_, nrow(ret)) + ppenint <- rep(NA_character_, nrow(ret)) + for (i in which(has_int)) { + dose_time <- last_dose_times[i] + if (is.na(dose_time)) next + start_rel <- ret$start[i] - dose_time + end_rel <- ret$end[i] - dose_time + ppstint[i] <- format_iso8601_duration(start_rel, timeu) + ppenint[i] <- format_iso8601_duration(end_rel, timeu) + } + ret$PPSTINT <- ppstint + ret$PPENINT <- ppenint + ret +} + +# Get the time unit string for ISO 8601 formatting +# +# Uses timeu_pref if available, otherwise timeu. +# +# @param x The PKNCAresults object +# @returns A character string with the time unit (e.g. "hr", "min", "day"), +# or NA_character_ if not set +# @keywords Internal +# @noRd +pknca_cdisc_get_timeu <- function(x) { + if (is.null(x$data$conc) || !inherits(x$data$conc, "PKNCAconc")) { + return(NA_character_) + } + # Prefer timeu_pref, fall back to timeu + timeu_pref <- x$data$conc$units$timeu_pref + if (!is.null(timeu_pref) && !is.na(timeu_pref)) { + return(timeu_pref) + } + timeu <- x$data$conc$units$timeu + if (!is.null(timeu) && !is.na(timeu)) { + return(timeu) + } + # Check if timeu is stored as a column attribute + timeu_col <- x$data$conc$columns$timeu + if (!is.null(timeu_col) && length(timeu_col) > 0) { + # Column-based: take the first value + vals <- unique(x$data$conc$data[[timeu_col]]) + vals <- vals[!is.na(vals)] + if (length(vals) == 1) return(vals) + } + NA_character_ +} + +# Format a numeric duration as an ISO 8601 duration string +# +# @param value Numeric duration value +# @param timeu The time unit (e.g. "hr", "h", "min", "day", "d", "s", "sec") +# @returns An ISO 8601 duration string (e.g. "PT0H", "PT24H", "P1D") +# @keywords Internal +# @noRd +format_iso8601_duration <- function(value, timeu) { + if (is.na(value) || is.infinite(value)) return(NA_character_) + timeu_lower <- tolower(timeu) + if (is.na(timeu_lower)) { + # No unit info: assume hours + timeu_lower <- "hr" + } + # Map time unit to ISO 8601 designator + if (timeu_lower %in% c("hr", "h", "hour", "hours")) { + paste0("PT", value, "H") + } else if (timeu_lower %in% c("min", "minute", "minutes")) { + paste0("PT", value, "M") + } else if (timeu_lower %in% c("s", "sec", "second", "seconds")) { + paste0("PT", value, "S") + } else if (timeu_lower %in% c("day", "days", "d")) { + paste0("P", value, "D") + } else { + # Unknown unit: default to hours + paste0("PT", value, "H") + } +} + +# Get the last dose time for each row in the results +# +# For each result row, finds the most recent dose time that is <= the interval +# start, matching on group variables. +# +# @param ret The result data.frame +# @param x The PKNCAresults object +# @returns A numeric vector of last dose times, one per row (NA if unavailable) +# @keywords Internal +# @noRd +pknca_cdisc_get_last_dose_time <- function(ret, x) { + result <- rep(NA_real_, nrow(ret)) + if (is.null(x$data$dose) || identical(x$data$dose, NA) || + !inherits(x$data$dose, "PKNCAdose")) { + return(result) + } + dose_df <- x$data$dose$data + time_col <- x$data$dose$columns$time + if (length(time_col) == 0) return(result) + group_cols <- unlist(x$data$dose$columns$groups) + merge_cols <- intersect(group_cols, names(ret)) + for (i in seq_len(nrow(ret))) { + interval_start <- ret$start[i] + # Filter dose data to matching groups + dose_subset <- dose_df + if (length(merge_cols) > 0) { + mask <- rep(TRUE, nrow(dose_subset)) + for (gc in merge_cols) { + mask <- mask & (dose_subset[[gc]] == ret[[gc]][i]) + } + dose_subset <- dose_subset[mask, , drop = FALSE] + } + # Find the last dose time <= interval start + dose_times <- dose_subset[[time_col]] + valid_times <- dose_times[!is.na(dose_times) & dose_times <= interval_start] + if (length(valid_times) > 0) { + result[i] <- max(valid_times) + } + } + result +} + +# Resolve a CDISC value that may be a simple string or a route-dependent list +# +# @param value A character string or a list with a "route" element +# @param route The route for the current row ("extravascular" or "intravascular") +# @returns A character string with the resolved CDISC value +# @keywords Internal +# @noRd +resolve_cdisc_value <- function(value, route) { + if (is.character(value)) { + return(value) + } + if (is.list(value) && !is.null(value$route)) { + route_lower <- tolower(route) + if (route_lower %in% names(value$route)) { + return(value$route[[route_lower]]) + } + # Default to first element (extravascular) if route not found + return(value$route[[1]]) + } + # Fallback + as.character(value) +} + +# Get the route of administration for each row in the results +# +# @param ret The long-format result data.frame +# @param x The PKNCAresults object +# @returns A character vector of routes, one per row +# @keywords Internal +# @noRd +pknca_cdisc_get_route <- function(ret, x) { + default_route <- "extravascular" + # Check if dose data is available + if (is.null(x$data$dose) || identical(x$data$dose, NA) || + !inherits(x$data$dose, "PKNCAdose")) { + return(rep(default_route, nrow(ret))) + } + route_data <- getAttributeColumn( + object = x$data$dose, attr_name = "route", warn_missing = character() + ) + if (is.null(route_data)) { + return(rep(default_route, nrow(ret))) + } + # Get the dose data with route and group columns + dose_df <- x$data$dose$data + route_col <- x$data$dose$columns$route + group_cols <- unlist(x$data$dose$columns$groups) + # If route is a scalar (same for all), return it for all rows + if (length(unique(route_data[[1]])) == 1) { + return(rep(tolower(route_data[[1]][1]), nrow(ret))) + } + # Route varies by group: merge with results on group columns + # Use only group columns that exist in both datasets + merge_cols <- intersect(group_cols, names(ret)) + if (length(merge_cols) == 0) { + return(rep(default_route, nrow(ret))) + } + # Get unique route per group combination + dose_route <- unique(dose_df[, c(merge_cols, route_col), drop = FALSE]) + names(dose_route)[names(dose_route) == route_col] <- ".route_cdisc" + merged <- merge( + data.frame(.row_id = seq_len(nrow(ret)), ret[, merge_cols, drop = FALSE]), + dose_route, + by = merge_cols, + all.x = TRUE, + sort = FALSE + ) + merged <- merged[order(merged$.row_id), ] + routes <- tolower(as.character(merged$.route_cdisc)) + routes[is.na(routes)] <- default_route + routes +} + #' @rdname getDataName #' @export getDataName.PKNCAresults <- function(object) { diff --git a/R/half.life.R b/R/half.life.R index 989775c1..aad63a40 100644 --- a/R/half.life.R +++ b/R/half.life.R @@ -626,7 +626,9 @@ add.interval.col("half.life", unit_type="time", pretty_name="Half-life", desc="The (terminal) half-life", - depends=c("tmax", "tlast")) + depends=c("tmax", "tlast"), + pptestcd_cdisc="LAMZHL", + pptest_cdisc="Half-Life Lambda z") PKNCA.set.summary( name="half.life", description="arithmetic mean and standard deviation", @@ -639,7 +641,9 @@ add.interval.col("r.squared", unit_type="unitless", pretty_name="$r^2$", desc="The r^2 value of the half-life calculation", - depends="half.life") + depends="half.life", + pptestcd_cdisc="R2", + pptest_cdisc="R Squared") PKNCA.set.summary( name="r.squared", description="arithmetic mean and standard deviation", @@ -652,7 +656,9 @@ add.interval.col("adj.r.squared", unit_type="unitless", pretty_name="$r^2_{adj}$", desc="The adjusted r^2 value of the half-life calculation", - depends="half.life") + depends="half.life", + pptestcd_cdisc="R2ADJ", + pptest_cdisc="R Squared Adjusted") PKNCA.set.summary( name="adj.r.squared", description="arithmetic mean and standard deviation", @@ -665,7 +671,9 @@ add.interval.col("lambda.z.corrxy", unit_type="unitless", pretty_name="Correlation (time, log-conc)", desc="Correlation between time and log-concentration for lambda.z points", - depends="half.life") + depends="half.life", + pptestcd_cdisc="CORRXY", + pptest_cdisc="Correlation Between TimeX and Log ConcY") PKNCA.set.summary( name="lambda.z.corrxy", description="arithmetic mean and standard deviation", @@ -678,7 +686,9 @@ add.interval.col("lambda.z", unit_type="inverse_time", pretty_name="$\\lambda_z$", desc="The elimination rate of the terminal half-life", - depends="half.life") + depends="half.life", + pptestcd_cdisc="LAMZ", + pptest_cdisc="Lambda z") PKNCA.set.summary( name="lambda.z", description="geometric mean and geometric coefficient of variation", @@ -691,7 +701,9 @@ add.interval.col("lambda.z.time.first", unit_type="time", pretty_name="First time for $\\lambda_z$", desc="The first time point used for the calculation of half-life", - depends="half.life") + depends="half.life", + pptestcd_cdisc="LAMZLL", + pptest_cdisc="Lambda z Lower Limit") PKNCA.set.summary( name="lambda.z.time.first", description="median and range", @@ -704,7 +716,9 @@ add.interval.col("lambda.z.time.last", unit_type="time", pretty_name="Last time for $\\lambda_z$", desc="The last time point used for the calculation of half-life", - depends="half.life") + depends="half.life", + pptestcd_cdisc="LAMZUL", + pptest_cdisc="Lambda z Upper Limit") PKNCA.set.summary( name="lambda.z.time.last", description="median and range", @@ -717,7 +731,9 @@ add.interval.col("lambda.z.n.points", unit_type="count", pretty_name="Number of points used for lambda_z", desc="The number of points used for the calculation of half-life", - depends="half.life") + depends="half.life", + pptestcd_cdisc="LAMZNPT", + pptest_cdisc="Number of Points for Lambda z") PKNCA.set.summary( name="lambda.z.n.points", description="median and range", @@ -730,7 +746,9 @@ add.interval.col("clast.pred", unit_type="conc", pretty_name="Clast,pred", desc="The concentration at Tlast as predicted by the half-life", - depends="half.life") + depends="half.life", + pptestcd_cdisc="CLSTP", + pptest_cdisc="Clast pred") PKNCA.set.summary( name="clast.pred", description="geometric mean and geometric coefficient of variation", @@ -743,7 +761,9 @@ add.interval.col("span.ratio", unit_type="fraction", pretty_name="Span ratio", desc="The ratio of the half-life to the duration used for half-life calculation", - depends="half.life") + depends="half.life", + pptestcd_cdisc="LAMZSPN", + pptest_cdisc="Lambda z Span") PKNCA.set.summary( name="span.ratio", description="geometric mean and geometric coefficient of variation", diff --git a/R/pk.calc.c0.R b/R/pk.calc.c0.R index fb23b4fc..3fa141b2 100644 --- a/R/pk.calc.c0.R +++ b/R/pk.calc.c0.R @@ -135,7 +135,9 @@ add.interval.col("c0", unit_type="conc", pretty_name="C0", desc="Initial concentration after an IV bolus", - depends=NULL) + depends=NULL, + pptestcd_cdisc="C0", + pptest_cdisc="Initial Conc") PKNCA.set.summary( name="c0", description="geometric mean and geometric coefficient of variation", diff --git a/R/pk.calc.simple.R b/R/pk.calc.simple.R index edc81da6..b69af083 100644 --- a/R/pk.calc.simple.R +++ b/R/pk.calc.simple.R @@ -44,7 +44,9 @@ add.interval.col("cmax", unit_type="conc", pretty_name="Cmax", desc="Maximum observed concentration", - depends=NULL) + depends=NULL, + pptestcd_cdisc="CMAX", + pptest_cdisc="Max Conc") PKNCA.set.summary( name="cmax", description="geometric mean and geometric coefficient of variation", @@ -76,7 +78,9 @@ add.interval.col("cmin", unit_type="conc", pretty_name="Cmin", desc="Minimum observed concentration", - depends=NULL) + depends=NULL, + pptestcd_cdisc="CMIN", + pptest_cdisc="Min Conc") PKNCA.set.summary( name="cmin", description="geometric mean and geometric coefficient of variation", @@ -135,7 +139,9 @@ add.interval.col("tmax", unit_type="time", pretty_name="Tmax", desc="Time of the maximum observed concentration", - depends=NULL) + depends=NULL, + pptestcd_cdisc="TMAX", + pptest_cdisc="Time of CMAX") PKNCA.set.summary( name="tmax", description="median and range", @@ -194,7 +200,9 @@ add.interval.col("tmin", unit_type="time", pretty_name="Tmin", desc="Time of the minimum observed concentration", - depends=NULL) + depends=NULL, + pptestcd_cdisc="TMIN", + pptest_cdisc="Time of CMIN Observation") PKNCA.set.summary( name="tmin", description="median and range", @@ -229,7 +237,9 @@ add.interval.col("tlast", unit_type="time", pretty_name="Tlast", desc="Time of the last concentration observed above the limit of quantification", - depends=NULL) + depends=NULL, + pptestcd_cdisc="TLST", + pptest_cdisc="Time of Last Nonzero Conc") PKNCA.set.summary( name="tlast", description="median and range", @@ -257,7 +267,9 @@ add.interval.col("tfirst", unit_type="time", pretty_name="Tfirst", desc="Time of the first concentration above the limit of quantification", - depends=NULL) + depends=NULL, + pptestcd_cdisc="TFIRST", + pptest_cdisc="Time of First Nonzero Conc") PKNCA.set.summary( name="tfirst", description="median and range", @@ -302,7 +314,9 @@ add.interval.col("clast.obs", unit_type="conc", pretty_name="Clast", desc="The last concentration observed above the limit of quantification", - depends=NULL) + depends=NULL, + pptestcd_cdisc="CLST", + pptest_cdisc="Last Nonzero Conc") PKNCA.set.summary( name="clast.obs", description="geometric mean and geometric coefficient of variation", @@ -329,7 +343,9 @@ add.interval.col("thalf.eff.obs", unit_type="time", pretty_name="Effective half-life (based on MRT,obs)", formalsmap=list(mrt="mrt.obs"), - depends="mrt.obs") + depends="mrt.obs", + pptestcd_cdisc="EFFOHL", + pptest_cdisc="Effective Half-Life Obs") PKNCA.set.summary( name="thalf.eff.obs", description="geometric mean and geometric coefficient of variation", @@ -343,7 +359,9 @@ add.interval.col("thalf.eff.pred", pretty_name="Effective half-life (based on MRT,pred)", desc="The effective half-life (as determined from the MRTpred)", formalsmap=list(mrt="mrt.pred"), - depends="mrt.pred") + depends="mrt.pred", + pptestcd_cdisc="EFFPHL", + pptest_cdisc="Effective Half-Life Pred") PKNCA.set.summary( name="thalf.eff.pred", description="geometric mean and geometric coefficient of variation", @@ -357,7 +375,9 @@ add.interval.col("thalf.eff.last", pretty_name="Effective half-life (based on MRT,last)", desc="The effective half-life (as determined from the MRTlast)", formalsmap=list(mrt="mrt.last"), - depends="mrt.last") + depends="mrt.last", + pptestcd_cdisc="EFFHL", + pptest_cdisc="Effective Half-Life (based on AUClast)") PKNCA.set.summary( name="thalf.eff.last", description="geometric mean and geometric coefficient of variation", @@ -371,7 +391,9 @@ add.interval.col("thalf.eff.iv.obs", pretty_name="Effective half-life (for IV dosing, based on MRT,obs)", desc="The effective half-life (as determined from the intravenous MRTobs)", formalsmap=list(mrt="mrt.iv.obs"), - depends="mrt.iv.obs") + depends="mrt.iv.obs", + pptestcd_cdisc="EFFIVOHL", + pptest_cdisc="Effective Half-Life (for IV dosing, based on MRT Obs)") PKNCA.set.summary( name="thalf.eff.iv.obs", description="geometric mean and geometric coefficient of variation", @@ -385,7 +407,9 @@ add.interval.col("thalf.eff.iv.pred", pretty_name="Effective half-life (for IV dosing, based on MRT,pred)", desc="The effective half-life (as determined from the intravenous MRTpred)", formalsmap=list(mrt="mrt.iv.pred"), - depends="mrt.iv.pred") + depends="mrt.iv.pred", + pptestcd_cdisc="EFFIVPHL", + pptest_cdisc="Effective Half-Life (for IV dosing, based on MRT Pred)") PKNCA.set.summary( name="thalf.eff.iv.pred", description="geometric mean and geometric coefficient of variation", @@ -399,7 +423,9 @@ add.interval.col("thalf.eff.iv.last", pretty_name="Effective half-life (for IV dosing, based on MRTlast)", desc="The effective half-life (as determined from the intravenous MRTlast)", formalsmap=list(mrt="mrt.iv.last"), - depends="mrt.iv.last") + depends="mrt.iv.last", + pptestcd_cdisc="EFFIVLHL", + pptest_cdisc="Effective Half-Life (for IV dosing, based on AUClast)") PKNCA.set.summary( name="thalf.eff.iv.last", description="geometric mean and geometric coefficient of variation", @@ -463,7 +489,9 @@ add.interval.col("aucpext.obs", pretty_name="AUCpext (based on AUCinf,obs)", desc="Percent of the AUCinf that is extrapolated after Tlast calculated from the observed Clast", formalsmap=list(aucinf="aucinf.obs"), - depends=c("auclast", "aucinf.obs")) + depends=c("auclast", "aucinf.obs"), + pptestcd_cdisc="AUCPEO", + pptest_cdisc="AUC %Extrapolation Obs") PKNCA.set.summary( name="aucpext.obs", description="arithmetic mean and standard deviation", @@ -477,7 +505,9 @@ add.interval.col("aucpext.pred", pretty_name="AUCpext (based on AUCinf,pred)", desc="Percent of the AUCinf that is extrapolated after Tlast calculated from the predicted Clast", formalsmap=list(aucinf="aucinf.pred"), - depends=c("auclast", "aucinf.pred")) + depends=c("auclast", "aucinf.pred"), + pptestcd_cdisc="AUCPEP", + pptest_cdisc="AUC %Extrapolation Pred") PKNCA.set.summary( name="aucpext.pred", description="arithmetic mean and standard deviation", @@ -504,7 +534,9 @@ add.interval.col("kel.obs", pretty_name="Kel (based on AUCinf,obs)", desc="Elimination rate (as calculated from the MRT with observed Clast)", formalsmap=list(mrt="mrt.obs"), - depends="mrt.obs") + depends="mrt.obs", + pptestcd_cdisc="KELOS", + pptest_cdisc="Kel (based on AUCinf,obs)") PKNCA.set.summary( name="kel.obs", description="geometric mean and geometric coefficient of variation", @@ -518,7 +550,9 @@ add.interval.col("kel.pred", pretty_name="Kel (based on AUCinf,pred)", desc="Elimination rate (as calculated from the MRT with predicted Clast)", formalsmap=list(mrt="mrt.pred"), - depends="mrt.pred") + depends="mrt.pred", + pptestcd_cdisc="KELP", + pptest_cdisc="Kel (based on AUCinf,pred)") PKNCA.set.summary( name="kel.pred", description="geometric mean and geometric coefficient of variation", @@ -532,7 +566,9 @@ add.interval.col("kel.last", pretty_name="Kel (based on AUClast)", desc="Elimination rate (as calculated from the MRT using AUClast)", formalsmap=list(mrt="mrt.last"), - depends="mrt.last") + depends="mrt.last", + pptestcd_cdisc="KELLST", + pptest_cdisc="Kel (based on AUClast)") PKNCA.set.summary( name="kel.last", description="geometric mean and geometric coefficient of variation", @@ -546,7 +582,9 @@ add.interval.col("kel.iv.obs", pretty_name="Kel (for IV dosing, based on AUCinf,obs)", desc="Elimination rate (as calculated from the intravenous MRTobs)", formalsmap=list(mrt="mrt.iv.obs"), - depends="mrt.iv.obs") + depends="mrt.iv.obs", + pptestcd_cdisc="KELIVOS", + pptest_cdisc="Kel (for IV dosing, based on AUCinf,obs)") PKNCA.set.summary( name="kel.iv.obs", description="geometric mean and geometric coefficient of variation", @@ -560,7 +598,9 @@ add.interval.col("kel.iv.pred", pretty_name="Kel (for IV dosing, based on AUCinf,pred)", desc="Elimination rate (as calculated from the intravenous MRTpred)", formalsmap=list(mrt="mrt.iv.pred"), - depends="mrt.iv.pred") + depends="mrt.iv.pred", + pptestcd_cdisc="KELIVP", + pptest_cdisc="Kel (for IV dosing, based on AUCinf,pred)") PKNCA.set.summary( name="kel.iv.pred", description="geometric mean and geometric coefficient of variation", @@ -574,7 +614,9 @@ add.interval.col("kel.iv.last", pretty_name="Kel (for IV dosing, based on AUClast)", desc="Elimination rate (as calculated from the intravenous MRTlast)", formalsmap=list(mrt="mrt.iv.last"), - depends="mrt.iv.last") + depends="mrt.iv.last", + pptestcd_cdisc="KELIVLT", + pptest_cdisc="Kel (for IV dosing, based on AUClast)") PKNCA.set.summary( name="kel.iv.last", description="geometric mean and geometric coefficient of variation", @@ -621,7 +663,9 @@ add.interval.col("cl.last", pretty_name="CL (based on AUClast)", desc="Clearance or observed oral clearance calculated to Clast", formalsmap=list(auc="auclast"), - depends="auclast") + depends="auclast", + pptestcd_cdisc=list(route=list(extravascular="CLF/FLST", intravascular="CLLST")), + pptest_cdisc=list(route=list(extravascular="CL by F (based on AUClast)", intravascular="CL (based on AUClast)"))) PKNCA.set.summary( name="cl.last", description="geometric mean and geometric coefficient of variation", @@ -635,7 +679,9 @@ add.interval.col("cl.all", pretty_name="CL (based on AUCall)", desc="Clearance or observed oral clearance calculated with AUCall", formalsmap=list(auc="aucall"), - depends="aucall") + depends="aucall", + pptestcd_cdisc=list(route=list(extravascular="CLF/FALL", intravascular="CLALL")), + pptest_cdisc=list(route=list(extravascular="CL by F (based on AUCall)", intravascular="CL (based on AUCall)"))) PKNCA.set.summary( name="cl.all", description="geometric mean and geometric coefficient of variation", @@ -649,7 +695,9 @@ add.interval.col("cl.obs", pretty_name="CL (based on AUCinf,obs)", desc="Clearance or observed oral clearance calculated with observed Clast", formalsmap=list(auc="aucinf.obs"), - depends="aucinf.obs") + depends="aucinf.obs", + pptestcd_cdisc=list(route=list(extravascular="CLF/FO", intravascular="CLO")), + pptest_cdisc=list(route=list(extravascular="Total CL Obs by F", intravascular="Total CL Obs"))) PKNCA.set.summary( name="cl.obs", description="geometric mean and geometric coefficient of variation", @@ -663,7 +711,9 @@ add.interval.col("cl.pred", pretty_name="CL (based on AUCinf,pred)", desc="Clearance or observed oral clearance calculated with predicted Clast", formalsmap=list(auc="aucinf.pred"), - depends="aucinf.pred") + depends="aucinf.pred", + pptestcd_cdisc=list(route=list(extravascular="CLF/FP", intravascular="CLP")), + pptest_cdisc=list(route=list(extravascular="Total CL Pred by F", intravascular="Total CL Pred"))) PKNCA.set.summary( name="cl.pred", description="geometric mean and geometric coefficient of variation", @@ -699,7 +749,9 @@ add.interval.col("f", unit_type="fraction", pretty_name="Bioavailability", desc="Bioavailability or relative bioavailability", - depends=NULL) + depends=NULL, + pptestcd_cdisc="FAB", + pptest_cdisc="Absolute Bioavailability") PKNCA.set.summary( name="f", description="geometric mean and geometric coefficient of variation", @@ -731,7 +783,9 @@ add.interval.col("mrt.obs", pretty_name="MRT (based on AUCinf,obs)", desc="The mean residence time to infinity using observed Clast", formalsmap=list(auc="aucinf.obs", aumc="aumcinf.obs"), - depends=c("aucinf.obs", "aumcinf.obs")) + depends=c("aucinf.obs", "aumcinf.obs"), + pptestcd_cdisc=list(route=list(extravascular="MRTEVFO", intravascular="MRTICFO")), + pptest_cdisc=list(route=list(extravascular="MRT Extravasc Infinity Obs", intravascular="MRT IV Cont Inf Infinity Obs"))) PKNCA.set.summary( name="mrt.obs", description="geometric mean and geometric coefficient of variation", @@ -745,7 +799,9 @@ add.interval.col("mrt.pred", pretty_name="MRT (based on AUCinf,pred)", desc="The mean residence time to infinity using predicted Clast", formalsmap=list(auc="aucinf.pred", aumc="aumcinf.pred"), - depends=c("aucinf.pred", "aumcinf.pred")) + depends=c("aucinf.pred", "aumcinf.pred"), + pptestcd_cdisc=list(route=list(extravascular="MRTEVFP", intravascular="MRTICFP")), + pptest_cdisc=list(route=list(extravascular="MRT Extravasc Infinity Pred", intravascular="MRT IV Cont Inf Infinity Pred"))) PKNCA.set.summary( name="mrt.pred", description="geometric mean and geometric coefficient of variation", @@ -759,7 +815,9 @@ add.interval.col("mrt.last", pretty_name="MRT (based on AUClast)", desc="The mean residence time to the last observed concentration above the LOQ", formalsmap=list(auc="auclast", aumc="aumclast"), - depends=c("auclast", "aumclast")) + depends=c("auclast", "aumclast"), + pptestcd_cdisc=list(route=list(extravascular="MRTEVLST", intravascular="MRTICLST")), + pptest_cdisc=list(route=list(extravascular="MRT Extravasc to Last Nonzero Conc", intravascular="MRT IV Cont Inf to Last Nonzero Conc"))) PKNCA.set.summary( name="mrt.last", description="geometric mean and geometric coefficient of variation", @@ -785,7 +843,9 @@ add.interval.col("mrt.iv.obs", pretty_name="MRT (for IV dosing, based on AUCinf,obs)", desc="The mean residence time to infinity using observed Clast correcting for dosing duration", formalsmap=list(auc="aucinf.obs", aumc="aumcinf.obs"), - depends=c("aucinf.obs", "aumcinf.obs")) + depends=c("aucinf.obs", "aumcinf.obs"), + pptestcd_cdisc="MRTIBIFO", + pptest_cdisc="MRT Intravasc Infinity Obs") PKNCA.set.summary( name="mrt.iv.obs", description="geometric mean and geometric coefficient of variation", @@ -799,7 +859,9 @@ add.interval.col("mrt.iv.pred", pretty_name="MRT (for IV dosing, based on AUCinf,pred)", desc="The mean residence time to infinity using predicted Clast correcting for dosing duration", formalsmap=list(auc="aucinf.pred", aumc="aumcinf.pred"), - depends=c("aucinf.pred", "aumcinf.pred")) + depends=c("aucinf.pred", "aumcinf.pred"), + pptestcd_cdisc="MRTIBIFP", + pptest_cdisc="MRT Intravasc Infinity Pred") PKNCA.set.summary( name="mrt.iv.pred", description="geometric mean and geometric coefficient of variation", @@ -813,7 +875,9 @@ add.interval.col("mrt.iv.last", pretty_name="MRT (for IV dosing, based on AUClast)", desc="The mean residence time to the last observed concentration above the LOQ correcting for dosing duration", formalsmap=list(auc="auclast", aumc="aumclast"), - depends=c("auclast", "aumclast")) + depends=c("auclast", "aumclast"), + pptestcd_cdisc="MRTIBLST", + pptest_cdisc="MRT Intravasc to Last Nonzero Conc") PKNCA.set.summary( name="mrt.iv.last", description="geometric mean and geometric coefficient of variation", @@ -849,7 +913,9 @@ add.interval.col("mrt.md.obs", pretty_name="MRT (for multiple dosing, based on AUCinf,obs)", desc="The mean residence time with multiple dosing and nonlinear kinetics using observed Clast", formalsmap=list(auctau="auclast", aumctau="aumclast", aucinf="aucinf.obs"), - depends=c("auclast", "aumclast", "aucinf.obs")) + depends=c("auclast", "aumclast", "aucinf.obs"), + pptestcd_cdisc="MRTMDO", + pptest_cdisc="MRT (for multiple dosing, based on AUCinf,obs)") PKNCA.set.summary( name="mrt.md.obs", description="geometric mean and geometric coefficient of variation", @@ -863,7 +929,9 @@ add.interval.col("mrt.md.pred", pretty_name="MRT (for multiple dosing, based on AUCinf,pred)", desc="The mean residence time with multiple dosing and nonlinear kinetics using predicted Clast", formalsmap=list(auctau="auclast", aumctau="aumclast", aucinf="aucinf.pred"), - depends=c("auclast", "aumclast", "aucinf.pred")) + depends=c("auclast", "aumclast", "aucinf.pred"), + pptestcd_cdisc="MRTMDP", + pptest_cdisc="MRT (for multiple dosing, based on AUCinf,pred)") PKNCA.set.summary( name="mrt.md.pred", description="geometric mean and geometric coefficient of variation", @@ -897,7 +965,9 @@ add.interval.col("vz.obs", pretty_name="Vz (based on AUCinf,obs)", desc="The terminal volume of distribution using observed Clast", formalsmap=list(cl="cl.obs"), - depends=c("cl.obs", "lambda.z")) + depends=c("cl.obs", "lambda.z"), + pptestcd_cdisc=list(route=list(extravascular="VZF/FO", intravascular="VZO")), + pptest_cdisc=list(route=list(extravascular="Vz by F Obs", intravascular="Vz Obs"))) PKNCA.set.summary( name="vz.obs", description="geometric mean and geometric coefficient of variation", @@ -911,7 +981,9 @@ add.interval.col("vz.pred", pretty_name="Vz (based on AUCinf,pred)", desc="The terminal volume of distribution using predicted Clast", formalsmap=list(cl="cl.pred"), - depends=c("cl.pred", "lambda.z")) + depends=c("cl.pred", "lambda.z"), + pptestcd_cdisc=list(route=list(extravascular="VZF/FP", intravascular="VZP")), + pptest_cdisc=list(route=list(extravascular="Vz by F Pred", intravascular="Vz Pred"))) PKNCA.set.summary( name="vz.pred", description="geometric mean and geometric coefficient of variation", @@ -936,7 +1008,9 @@ add.interval.col("vss.obs", pretty_name="Vss (based on AUCinf,obs)", desc="The steady-state volume of distribution using observed Clast", formalsmap=list(cl="cl.obs", mrt="mrt.obs"), - depends=c("cl.obs", "mrt.obs")) + depends=c("cl.obs", "mrt.obs"), + pptestcd_cdisc=list(route=list(extravascular="VSSF/FO", intravascular="VSSO")), + pptest_cdisc=list(route=list(extravascular="Vss by F Obs", intravascular="Vol Dist Steady State Obs"))) PKNCA.set.summary( name="vss.obs", description="geometric mean and geometric coefficient of variation", @@ -951,7 +1025,9 @@ add.interval.col("vss.pred", pretty_name="Vss (based on AUCinf,pred)", desc="The steady-state volume of distribution using predicted Clast", formalsmap=list(cl="cl.pred", mrt="mrt.pred"), - depends=c("cl.pred", "mrt.pred")) + depends=c("cl.pred", "mrt.pred"), + pptestcd_cdisc=list(route=list(extravascular="VSSF/FP", intravascular="VSSP")), + pptest_cdisc=list(route=list(extravascular="Vss by F Pred", intravascular="Vol Dist Steady State Pred"))) PKNCA.set.summary( name="vss.pred", description="geometric mean and geometric coefficient of variation", @@ -965,7 +1041,9 @@ add.interval.col("vss.last", pretty_name="Vss (based on AUClast)", desc="The steady-state volume of distribution calculating through Tlast", formalsmap=list(cl="cl.last", mrt="mrt.last"), - depends=c("cl.last", "mrt.last")) + depends=c("cl.last", "mrt.last"), + pptestcd_cdisc=list(route=list(extravascular="VSSF/FLST", intravascular="VSSLST")), + pptest_cdisc=list(route=list(extravascular="Vss by F (based on AUClast)", intravascular="Vss (based on AUClast)"))) PKNCA.set.summary( name="vss.last", description="geometric mean and geometric coefficient of variation", @@ -979,7 +1057,9 @@ add.interval.col("vss.iv.obs", pretty_name="Vss (for IV dosing, based on AUCinf,obs)", desc="The steady-state volume of distribution with intravenous infusion using observed Clast", formalsmap=list(cl="cl.obs", mrt="mrt.iv.obs"), - depends=c("cl.obs", "mrt.iv.obs")) + depends=c("cl.obs", "mrt.iv.obs"), + pptestcd_cdisc="VSSIVO", + pptest_cdisc="Vss (for IV dosing, based on AUCinf,obs)") PKNCA.set.summary( name="vss.iv.obs", description="geometric mean and geometric coefficient of variation", @@ -993,7 +1073,9 @@ add.interval.col("vss.iv.pred", pretty_name="Vss (for IV dosing, based on AUCinf,pred)", desc="The steady-state volume of distribution with intravenous infusion using predicted Clast", formalsmap=list(cl="cl.pred", mrt="mrt.iv.pred"), - depends=c("cl.pred", "mrt.iv.pred")) + depends=c("cl.pred", "mrt.iv.pred"), + pptestcd_cdisc="VSSIVP", + pptest_cdisc="Vss (for IV dosing, based on AUCinf,pred)") PKNCA.set.summary( name="vss.iv.pred", description="geometric mean and geometric coefficient of variation", @@ -1007,7 +1089,9 @@ add.interval.col("vss.iv.last", pretty_name="Vss (for IV dosing, based on AUClast)", desc="The steady-state volume of distribution with intravenous infusion calculating through Tlast", formalsmap=list(cl="cl.last", mrt="mrt.iv.last"), - depends=c("cl.last", "mrt.iv.last")) + depends=c("cl.last", "mrt.iv.last"), + pptestcd_cdisc="VSSIVLST", + pptest_cdisc="Vss (for IV dosing, based on AUClast)") PKNCA.set.summary( name="vss.iv.last", description="geometric mean and geometric coefficient of variation", @@ -1021,7 +1105,9 @@ add.interval.col("vss.md.obs", pretty_name="Vss (for multiple-dose, based on Clast,obs)", desc="The steady-state volume of distribution for nonlinear multiple-dose data using observed Clast", formalsmap=list(cl="cl.last", mrt="mrt.md.obs"), - depends=c("cl.last", "mrt.md.obs")) + depends=c("cl.last", "mrt.md.obs"), + pptestcd_cdisc="VSSMDO", + pptest_cdisc="Vss (for multiple-dose, based on AUCinf,obs)") PKNCA.set.summary( name="vss.md.obs", description="geometric mean and geometric coefficient of variation", @@ -1035,7 +1121,9 @@ add.interval.col("vss.md.pred", pretty_name="Vss (for multiple-dose, based on Clast,pred)", desc="The steady-state volume of distribution for nonlinear multiple-dose data using predicted Clast", formalsmap=list(cl="cl.last", mrt="mrt.md.pred"), - depends=c("cl.last", "mrt.md.pred")) + depends=c("cl.last", "mrt.md.pred"), + pptestcd_cdisc="VSSMDP", + pptest_cdisc="Vss (for multiple-dose, based on AUCinf,pred)") PKNCA.set.summary( name="vss.md.pred", description="geometric mean and geometric coefficient of variation", @@ -1068,7 +1156,9 @@ add.interval.col( pretty_name = "Cav", desc = "The average concentration during an interval (calculated with AUClast)", depends = "auclast", - formalsmap = list(auc = "auclast") + formalsmap = list(auc = "auclast"), + pptestcd_cdisc="CAVG", + pptest_cdisc="Average Conc" ) add.interval.col( "cav.int.last", @@ -1079,6 +1169,8 @@ add.interval.col( desc = "The average concentration during an interval (calculated with AUCint.last)", depends = "aucint.last", formalsmap = list(auc = "aucint.last"), + pptestcd_cdisc="CAVGINT", + pptest_cdisc="Average Conc from T1 to T2" ) add.interval.col( "cav.int.all", @@ -1089,6 +1181,8 @@ add.interval.col( desc = "The average concentration during an interval (calculated with AUCint.all)", depends = "aucint.all", formalsmap = list(auc = "aucint.all"), + pptestcd_cdisc="CAVGINA", + pptest_cdisc="Cavg All" ) add.interval.col( "cav.int.inf.obs", @@ -1099,6 +1193,8 @@ add.interval.col( desc = "The average concentration during an interval (calculated with AUCint.inf.obs)", depends = "aucint.inf.obs", formalsmap = list(auc = "aucint.inf.obs"), + pptestcd_cdisc="CAVGINO", + pptest_cdisc="Cavg Infinity Obs" ) add.interval.col( "cav.int.inf.pred", @@ -1109,6 +1205,8 @@ add.interval.col( desc = "The average concentration during an interval (calculated with AUCint.inf.pred)", depends = "aucint.inf.pred", formalsmap = list(auc = "aucint.inf.pred"), + pptestcd_cdisc="CAVGINP", + pptest_cdisc="Cavg Infinity Pred" ) PKNCA.set.summary( @@ -1144,7 +1242,9 @@ add.interval.col("ctrough", unit_type="conc", pretty_name="Ctrough", desc="The trough (end of interval) concentration", - depends=NULL) + depends=NULL, + pptestcd_cdisc="CTROUGH", + pptest_cdisc="Conc Trough") PKNCA.set.summary( name="ctrough", description="geometric mean and geometric coefficient of variation", @@ -1175,7 +1275,9 @@ add.interval.col("cstart", unit_type="conc", pretty_name="Cstart", desc="The predose concentration", - depends=NULL) + depends=NULL, + pptestcd_cdisc="CSTART", + pptest_cdisc="Cstart") PKNCA.set.summary( name="cstart", description="geometric mean and geometric coefficient of variation", @@ -1203,7 +1305,9 @@ add.interval.col("ptr", unit_type="fraction", pretty_name="Peak-to-trough ratio", desc="Peak-to-Trough ratio (fraction)", - depends=c("cmax", "ctrough")) + depends=c("cmax", "ctrough"), + pptestcd_cdisc="PTROUGHR", + pptest_cdisc="Peak Trough Ratio") PKNCA.set.summary( name="ptr", description="geometric mean and geometric coefficient of variation", @@ -1234,7 +1338,9 @@ add.interval.col("tlag", unit_type="time", pretty_name="Tlag", desc="Lag time", - depends=NULL) + depends=NULL, + pptestcd_cdisc="TLAG", + pptest_cdisc="Time to First Nonzero Conc") PKNCA.set.summary( name="tlag", description="median and range", @@ -1265,7 +1371,9 @@ add.interval.col("deg.fluc", unit_type="%", pretty_name="Degree of fluctuation", desc="Degree of fluctuation", - depends=c("cmax", "cmin", "cav")) + depends=c("cmax", "cmin", "cav"), + pptestcd_cdisc="DEGFLUC", + pptest_cdisc="Degree of fluctuation") PKNCA.set.summary( name="deg.fluc", description="arithmetic mean and standard deviation", @@ -1292,7 +1400,9 @@ add.interval.col("swing", unit_type="%", pretty_name="Swing", desc="Swing relative to Cmin", - depends=c("cmax", "cmin")) + depends=c("cmax", "cmin"), + pptestcd_cdisc="SWING", + pptest_cdisc="Swing") PKNCA.set.summary( name="swing", description="arithmetic mean and standard deviation", @@ -1327,7 +1437,9 @@ add.interval.col("ceoi", unit_type="conc", pretty_name="Ceoi", desc="Concentration at the end of infusion", - depends=NULL) + depends=NULL, + pptestcd_cdisc="CEOI", + pptest_cdisc="Ceoi") PKNCA.set.summary( name="ceoi", description="geometric mean and geometric coefficient of variation", @@ -1364,7 +1476,9 @@ add.interval.col( pretty_name="AUC,above", desc="The area under the concentration time the beginning of the interval to the last concentration above the limit of quantification plus the triangle from that last concentration to 0 at the first concentration below the limit of quantification, with a concentration subtracted from all concentrations and values below zero after subtraction set to zero", depends="cstart", - formalsmap = list(conc_above = "cstart") + formalsmap = list(conc_above = "cstart"), + pptestcd_cdisc="AUCABVPA", + pptest_cdisc="AUC above predose" ) PKNCA.set.summary( name="aucabove.predose.all", @@ -1379,7 +1493,9 @@ add.interval.col( pretty_name="AUC,above", desc="The area under the concentration time the beginning of the interval to the last concentration above the limit of quantification plus the triangle from that last concentration to 0 at the first concentration below the limit of quantification, with a concentration subtracted from all concentrations and values below zero after subtraction set to zero", depends="ctrough", - formalsmap = list(conc_above = "ctrough") + formalsmap = list(conc_above = "ctrough"), + pptestcd_cdisc="AUCABVTA", + pptest_cdisc="AUC above trough" ) PKNCA.set.summary( name="aucabove.trough.all", @@ -1414,7 +1530,9 @@ add.interval.col( unit_type = "count", pretty_name = "Concentration count", desc = "Number of non-missing concentrations for an interval", - depends = NULL + depends = NULL, + pptestcd_cdisc="CNTCONC", + pptest_cdisc="Concentration count" ) #' @describeIn pk.calc.count_conc Count the number of concentration measurements @@ -1461,7 +1579,9 @@ add.interval.col( values=c(FALSE, TRUE), unit_type="dose", pretty_name="Total dose", - desc="Total dose administered during an interval" + desc="Total dose administered during an interval", + pptestcd_cdisc="TDOSE", + pptest_cdisc="Total dose administered" ) PKNCA.set.summary( name="totdose", diff --git a/R/pk.calc.urine.R b/R/pk.calc.urine.R index 165b6135..1ee6479c 100644 --- a/R/pk.calc.urine.R +++ b/R/pk.calc.urine.R @@ -13,7 +13,9 @@ add.interval.col("volpk", values=c(FALSE, TRUE), unit_type="volume", pretty_name="Total Urine Volume", - desc="The sum of urine volumes for the interval") + desc="The sum of urine volumes for the interval", + pptestcd_cdisc="VOLPK", + pptest_cdisc="Volume of PK sample") PKNCA.set.summary( name="volpk", description="geometric mean and geometric coefficient of variation", @@ -52,7 +54,9 @@ add.interval.col("ae", values=c(FALSE, TRUE), unit_type="amount", pretty_name="Amount excreted", - desc="The amount excreted (typically into urine or feces)") + desc="The amount excreted (typically into urine or feces)", + pptestcd_cdisc="RCAMINT", + pptest_cdisc="Amt Rec from T1 to T2") PKNCA.set.summary( name="ae", description="geometric mean and geometric coefficient of variation", @@ -82,7 +86,9 @@ add.interval.col("clr.last", pretty_name="Renal clearance (from AUClast)", formalsmap=list(auc="auclast"), depends="ae", - desc="The renal clearance calculated using AUClast") + desc="The renal clearance calculated using AUClast", + pptestcd_cdisc="RENALCL", + pptest_cdisc="Renal CL") PKNCA.set.summary( name="clr.last", description="geometric mean and geometric coefficient of variation", @@ -96,7 +102,9 @@ add.interval.col("clr.obs", pretty_name="Renal clearance (from AUCinf,obs)", formalsmap=list(auc="aucinf.obs"), depends="ae", - desc="The renal clearance calculated using AUCinf,obs") + desc="The renal clearance calculated using AUCinf,obs", + pptestcd_cdisc="RENALCL", + pptest_cdisc="Renal CL") PKNCA.set.summary( name="clr.obs", description="geometric mean and geometric coefficient of variation", @@ -110,7 +118,9 @@ add.interval.col("clr.pred", pretty_name="Renal clearance (from AUCinf,pred)", formalsmap=list(auc="aucinf.pred"), depends="ae", - desc="The renal clearance calculated using AUCinf,pred") + desc="The renal clearance calculated using AUCinf,pred", + pptestcd_cdisc="RENALCL", + pptest_cdisc="Renal CL") PKNCA.set.summary( name="clr.pred", description="geometric mean and geometric coefficient of variation", @@ -139,7 +149,9 @@ add.interval.col("fe", pretty_name="Fraction excreted", values=c(FALSE, TRUE), depends="ae", - desc="The fraction of the dose excreted") + desc="The fraction of the dose excreted", + pptestcd_cdisc="FREXINT", + pptest_cdisc="Fract Excr from T1 to T2") PKNCA.set.summary( name="fe", description="geometric mean and geometric coefficient of variation", @@ -187,7 +199,9 @@ add.interval.col("ertlst", FUN="pk.calc.ertlst", unit_type="time", pretty_name="Tlast excretion rate", - desc="The midpoint collection time of the last measurable excretion rate (typically in urine or feces)") + desc="The midpoint collection time of the last measurable excretion rate (typically in urine or feces)", + pptestcd_cdisc="ERTLST", + pptest_cdisc="Time of Last Excretion Rate") PKNCA.set.summary( name="ertlst", @@ -235,7 +249,9 @@ add.interval.col("ermax", FUN="pk.calc.ermax", unit_type="amount_time", pretty_name="Maximum excretion rate", - desc="The maximum excretion rate (typically in urine or feces)") + desc="The maximum excretion rate (typically in urine or feces)", + pptestcd_cdisc="ERMAX", + pptest_cdisc="Max Excretion Rate") PKNCA.set.summary( name="ermax", @@ -290,7 +306,9 @@ add.interval.col("ertmax", FUN="pk.calc.ertmax", unit_type="time", pretty_name="Tmax excretion rate", - desc="The midpoint collection time of the maximum excretion rate (typically in urine or feces)") + desc="The midpoint collection time of the maximum excretion rate (typically in urine or feces)", + pptestcd_cdisc="ERTMAX", + pptest_cdisc="Midpoint of Interval of Maximum ER") PKNCA.set.summary( name="ertmax", diff --git a/R/sparse.R b/R/sparse.R index 7a59f2cc..25b790ab 100644 --- a/R/sparse.R +++ b/R/sparse.R @@ -352,7 +352,9 @@ add.interval.col( values=c(FALSE, TRUE), unit_type="auc", pretty_name="Sparse AUClast", - desc="For sparse PK sampling, the area under the concentration time curve from the beginning of the interval to the last concentration above the limit of quantification" + desc="For sparse PK sampling, the area under the concentration time curve from the beginning of the interval to the last concentration above the limit of quantification", + pptestcd_cdisc="SPARSEAL", + pptest_cdisc="Sparse AUClast" ) PKNCA.set.summary( name="sparse_auclast", @@ -368,7 +370,9 @@ add.interval.col( unit_type="auc", pretty_name="Sparse AUClast standard error", desc="For sparse PK sampling, the standard error of the area under the concentration time curve from the beginning of the interval to the last concentration above the limit of quantification", - depends="sparse_auclast" + depends="sparse_auclast", + pptestcd_cdisc="SPARSEAS", + pptest_cdisc="Sparse AUClast standard error" ) PKNCA.set.summary( name="sparse_auc_se", @@ -384,7 +388,9 @@ add.interval.col( unit_type="count", pretty_name="Sparse AUClast degrees of freedom", desc="For sparse PK sampling, the standard error degrees of freedom of the area under the concentration time curve from the beginning of the interval to the last concentration above the limit of quantification", - depends="sparse_auclast" + depends="sparse_auclast", + pptestcd_cdisc="SPARSEAD", + pptest_cdisc="Sparse AUClast degrees of freedom" ) PKNCA.set.summary( name="sparse_auc_df", diff --git a/R/time.above.R b/R/time.above.R index aaad375b..a3857d36 100644 --- a/R/time.above.R +++ b/R/time.above.R @@ -89,7 +89,9 @@ add.interval.col("time_above", values=c(FALSE, TRUE), unit_type="time", pretty_name="Time above Concentration", - desc="Time above a given concentration") + desc="Time above a given concentration", + pptestcd_cdisc="TAT", + pptest_cdisc="Time Above Threshold") PKNCA.set.summary( name="time_above", description="arithmetic mean and standard deviation", diff --git a/R/zzz-pk.calc.dn.R b/R/zzz-pk.calc.dn.R index 661d16d0..eac6c64f 100644 --- a/R/zzz-pk.calc.dn.R +++ b/R/zzz-pk.calc.dn.R @@ -17,6 +17,19 @@ local({ "clr.last", "clr.obs", "clr.pred")) { current_unit_type <- get.interval.cols()[[n]]$unit_type current_pretty_name <- get.interval.cols()[[n]]$pretty_name + current_pptestcd_cdisc <- get.interval.cols()[[n]]$pptestcd_cdisc + current_pptest_cdisc <- get.interval.cols()[[n]]$pptest_cdisc + # Derive dose-normalized CDISC codes from the base parameter + dn_pptestcd <- if (is.character(current_pptestcd_cdisc)) { + paste0(current_pptestcd_cdisc, "D") + } else { + current_pptestcd_cdisc + } + dn_pptest <- if (is.character(current_pptest_cdisc)) { + paste(current_pptest_cdisc, "by Dose") + } else { + current_pptest_cdisc + } # Add the column to the interval specification add.interval.col( name=paste(n, "dn", sep="."), @@ -26,7 +39,9 @@ local({ pretty_name=paste(current_pretty_name, "(dose-normalized)"), desc=paste("Dose normalized", n), formalsmap=list(parameter=n), - depends=c(n) + depends=c(n), + pptestcd_cdisc=dn_pptestcd, + pptest_cdisc=dn_pptest ) PKNCA.set.summary( name=paste(n, "dn", sep="."), diff --git a/man/add.interval.col.Rd b/man/add.interval.col.Rd index 5f6762c8..93eb7e85 100644 --- a/man/add.interval.col.Rd +++ b/man/add.interval.col.Rd @@ -14,7 +14,9 @@ add.interval.col( desc = "", sparse = FALSE, formalsmap = list(), - datatype = c("interval", "individual", "population") + datatype = c("interval", "individual", "population"), + pptestcd_cdisc = NULL, + pptest_cdisc = NULL ) } \arguments{ @@ -44,6 +46,14 @@ to NCA parameter names. See the details for information on use of \code{formalsmap}.} \item{datatype}{The type of data used for the calculation} + +\item{pptestcd_cdisc}{The CDISC PPTESTCD code for this parameter. Can be a +character string for simple mappings, or a named list for route-dependent +mappings (e.g., \code{list(route = list(extravascular = "CLF/FO", intravascular = "CLO"))}). Defaults to \code{name} if not provided.} + +\item{pptest_cdisc}{The CDISC PPTEST name for this parameter. Can be a +character string or a named list (same structure as \code{pptestcd_cdisc}). +Defaults to \code{desc} if not provided.} } \value{ NULL (Calling this function has a side effect of changing the diff --git a/man/as.data.frame.PKNCAresults.Rd b/man/as.data.frame.PKNCAresults.Rd index c5fea030..7679972d 100644 --- a/man/as.data.frame.PKNCAresults.Rd +++ b/man/as.data.frame.PKNCAresults.Rd @@ -8,7 +8,7 @@ data.frame.} \method{as.data.frame}{PKNCAresults}( x, ..., - out_format = c("long", "wide"), + out_format = c("long", "wide", "cdisc"), filter_requested = FALSE, filter_excluded = FALSE, out.format = deprecated() @@ -19,7 +19,11 @@ data.frame.} \item{...}{Ignored (for compatibility with generic \code{\link[=as.data.frame]{as.data.frame()}})} -\item{out_format}{Should the output be 'long' (default) or 'wide'?} +\item{out_format}{Should the output be 'long' (default), 'wide', or 'cdisc'? +When 'cdisc', the PPTESTCD column is translated to CDISC standard codes +and a PPTEST column with the CDISC test name is added. Route-dependent +parameters (e.g. CL, VZ, MRT) are resolved using the route information +from the dose data.} \item{filter_requested}{Only return rows with parameters that were specifically requested?} diff --git a/man/filter.PKNCAresults.Rd b/man/filter.PKNCAresults.Rd index b5e04945..b374e84d 100644 --- a/man/filter.PKNCAresults.Rd +++ b/man/filter.PKNCAresults.Rd @@ -18,14 +18,14 @@ lazy data frame (e.g. from dbplyr or dtplyr). See \emph{Methods}, below, for more details.} \item{...}{<\code{\link[rlang:args_data_masking]{data-masking}}> Expressions that -return a logical vector, defined in terms of the variables in \code{.data}. If -multiple expressions are included, they are combined with the \code{&} operator. -To combine expressions using \code{|} instead, wrap them in \code{\link[dplyr:when_any]{when_any()}}. Only -rows for which all expressions evaluate to \code{TRUE} are kept (for \code{filter()}) -or dropped (for \code{filter_out()}).} +return a logical value, and are defined in terms of the variables in +\code{.data}. If multiple expressions are included, they are combined with the +\code{&} operator. Only rows for which all conditions evaluate to \code{TRUE} are +kept.} -\item{.preserve}{Relevant when the \code{.data} input is grouped. If \code{.preserve = FALSE} (the default), the grouping structure is recalculated based on the -resulting data, otherwise the grouping is kept as is.} +\item{.preserve}{Relevant when the \code{.data} input is grouped. +If \code{.preserve = FALSE} (the default), the grouping structure +is recalculated based on the resulting data, otherwise the grouping is kept as is.} } \description{ dplyr filtering for PKNCA diff --git a/man/group_by.PKNCAresults.Rd b/man/group_by.PKNCAresults.Rd index 9be8a460..8b6b6d2b 100644 --- a/man/group_by.PKNCAresults.Rd +++ b/man/group_by.PKNCAresults.Rd @@ -26,16 +26,20 @@ lazy data frame (e.g. from dbplyr or dtplyr). See \emph{Methods}, below, for more details.} -\item{...}{<\code{\link[rlang:args_data_masking]{data-masking}}> In \code{group_by()}, -variables or computations to group by. Computations are always done on the -ungrouped data frame. To perform computations on the grouped data, you need -to use a separate \code{mutate()} step before the \code{group_by()}. +\item{...}{In \code{group_by()}, variables or computations to group by. +Computations are always done on the ungrouped data frame. +To perform computations on the grouped data, you need to use +a separate \code{mutate()} step before the \code{group_by()}. Computations are not allowed in \code{nest_by()}. In \code{ungroup()}, variables to remove from the grouping.} \item{.add}{When \code{FALSE}, the default, \code{group_by()} will override existing groups. To add to the existing groups, use -\code{.add = TRUE}.} +\code{.add = TRUE}. + +This argument was previously called \code{add}, but that prevented +creating a new grouping variable called \code{add}, and conflicts with +our naming conventions.} \item{.drop}{Drop groups formed by factor levels that don't appear in the data? The default is \code{TRUE} except when \code{.data} has been previously diff --git a/tests/testthat/test-001-add.interval.col.R b/tests/testthat/test-001-add.interval.col.R index 7a7b72af..765b26be 100644 --- a/tests/testthat/test-001-add.interval.col.R +++ b/tests/testthat/test-001-add.interval.col.R @@ -114,7 +114,9 @@ test_that("add.interval.col", { sparse=FALSE, formalsmap=list(), depends=NULL, - datatype="interval" + datatype="interval", + pptestcd_cdisc="a", + pptest_cdisc="test addition" ), info="interval column assignment works with FUN=NA" ) @@ -132,7 +134,9 @@ test_that("add.interval.col", { sparse=FALSE, formalsmap=list(), depends=NULL, - datatype="interval" + datatype="interval", + pptestcd_cdisc="a", + pptest_cdisc="test addition" ), info="interval column assignment works with FUN=a character string" ) @@ -150,7 +154,9 @@ test_that("add.interval.col", { sparse=FALSE, formalsmap=list(x="values"), depends=NULL, - datatype="interval" + datatype="interval", + pptestcd_cdisc="a", + pptest_cdisc="test addition" ), info="interval column assignment works with FUN=NA" ) @@ -176,5 +182,32 @@ test_that("fake parameters", { ) }) +test_that("add.interval.col rejects invalid pptestcd_cdisc types", { + expect_error( + add.interval.col(name="a", FUN="mean", unit_type="conc", pretty_name="a", + desc="test", pptestcd_cdisc=123), + regexp="pptestcd_cdisc must be a character string or a list" + ) +}) + +test_that("add.interval.col rejects invalid pptest_cdisc types", { + expect_error( + add.interval.col(name="a", FUN="mean", unit_type="conc", pretty_name="a", + desc="test", pptest_cdisc=123), + regexp="pptest_cdisc must be a character string or a list" + ) +}) + +test_that("add.interval.col accepts list for pptestcd_cdisc", { + add.interval.col(name="a", FUN="mean", unit_type="conc", pretty_name="a", + desc="test", + pptestcd_cdisc=list(route=list(extravascular="EV", intravascular="IV")), + pptest_cdisc="test desc") + result <- get("interval.cols", envir=PKNCA:::.PKNCAEnv)[["a"]] + expect_true(is.list(result$pptestcd_cdisc)) + expect_equal(result$pptestcd_cdisc$route$extravascular, "EV") + expect_equal(result$pptestcd_cdisc$route$intravascular, "IV") +}) + # Reset the original state assign("interval.cols", original_state, envir=PKNCA:::.PKNCAEnv) diff --git a/tests/testthat/test-class-PKNCAresults.R b/tests/testthat/test-class-PKNCAresults.R index 09faa6b1..3f59fff4 100644 --- a/tests/testthat/test-class-PKNCAresults.R +++ b/tests/testthat/test-class-PKNCAresults.R @@ -444,3 +444,278 @@ test_that("as.data.frame.PKNCAresults can filter to remove excluded parameters", expect_equal(nrow(as.data.frame(o_result)), 24) expect_equal(nrow(as.data.frame(o_result, filter_excluded = TRUE)), 14) }) + +# CDISC output format tests ---- + +test_that("as.data.frame.PKNCAresults with out_format='cdisc' adds PPTESTCD and PPTEST", { + d_conc <- data.frame( + subject = rep(1, 4), + time = 0:3, + conc = c(0, 1, 0.5, 0.25) + ) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject) + d_dose <- data.frame(subject = 1, time = 0, dose = 10) + o_dose <- PKNCAdose(d_dose, dose ~ time | subject) + o_data <- PKNCAdata(o_conc, o_dose, intervals = data.frame( + start = 0, end = 3, cmax = TRUE, auclast = TRUE, tmax = TRUE + )) + suppressMessages(o_nca <- pk.nca(o_data)) + + result_cdisc <- as.data.frame(o_nca, out_format = "cdisc") + + expect_true("PPTESTCD" %in% names(result_cdisc)) + expect_true("PPTEST" %in% names(result_cdisc)) + expect_true("CMAX" %in% result_cdisc$PPTESTCD) + expect_true("AUCLST" %in% result_cdisc$PPTESTCD) + expect_true("TMAX" %in% result_cdisc$PPTESTCD) + # PPTEST should be placed right after PPTESTCD + pptestcd_pos <- which(names(result_cdisc) == "PPTESTCD") + pptest_pos <- which(names(result_cdisc) == "PPTEST") + expect_equal(pptest_pos, pptestcd_pos + 1) + # Check PPTEST values + cmax_row <- result_cdisc[result_cdisc$PPTESTCD == "CMAX", ] + expect_equal(cmax_row$PPTEST, "Max Conc") +}) + +test_that("as.data.frame.PKNCAresults with out_format='cdisc' resolves route-dependent params", { + d_conc <- data.frame( + subject = rep(1, 5), + time = 0:4, + conc = c(0, 2, 1, 0.5, 0.25) + ) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject) + d_dose <- data.frame(subject = 1, time = 0, dose = 10) + + # Extravascular + o_dose_ev <- PKNCAdose(d_dose, dose ~ time | subject, route = "extravascular") + o_data_ev <- PKNCAdata(o_conc, o_dose_ev, intervals = data.frame( + start = 0, end = Inf, cl.obs = TRUE, aucinf.obs = TRUE, half.life = TRUE + )) + suppressMessages(suppressWarnings(o_nca_ev <- pk.nca(o_data_ev))) + result_ev <- as.data.frame(o_nca_ev, out_format = "cdisc") + expect_true("CLF/FO" %in% result_ev$PPTESTCD) + expect_false("CLO" %in% result_ev$PPTESTCD) + + # Intravascular + o_dose_iv <- PKNCAdose(d_dose, dose ~ time | subject, route = "intravascular") + o_data_iv <- PKNCAdata(o_conc, o_dose_iv, intervals = data.frame( + start = 0, end = Inf, cl.obs = TRUE, aucinf.obs = TRUE, half.life = TRUE + )) + suppressMessages(suppressWarnings(o_nca_iv <- pk.nca(o_data_iv))) + result_iv <- as.data.frame(o_nca_iv, out_format = "cdisc") + expect_true("CLO" %in% result_iv$PPTESTCD) + expect_false("CLF/FO" %in% result_iv$PPTESTCD) +}) + +test_that("as.data.frame.PKNCAresults with out_format='cdisc' does not add PPSTINT/PPENINT without INT params", { + d_conc <- data.frame( + subject = rep(1, 4), + time = 0:3, + conc = c(0, 1, 0.5, 0.25) + ) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject) + d_dose <- data.frame(subject = 1, time = 0, dose = 10) + o_dose <- PKNCAdose(d_dose, dose ~ time | subject) + o_data <- PKNCAdata(o_conc, o_dose, intervals = data.frame( + start = 0, end = 3, cmax = TRUE + )) + suppressMessages(o_nca <- pk.nca(o_data)) + + result_cdisc <- as.data.frame(o_nca, out_format = "cdisc") + expect_false("PPSTINT" %in% names(result_cdisc)) + expect_false("PPENINT" %in% names(result_cdisc)) +}) + +test_that("as.data.frame.PKNCAresults with out_format='cdisc' adds PPSTINT/PPENINT for INT params", { + d_conc <- data.frame( + subject = rep(1, 5), + time = 0:4, + conc = c(0, 2, 1, 0.5, 0.25) + ) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject, timeu = "hr") + d_dose <- data.frame(subject = 1, time = 0, dose = 10) + o_dose <- PKNCAdose(d_dose, dose ~ time | subject) + o_data <- PKNCAdata(o_conc, o_dose, intervals = data.frame( + start = 0, end = 4, cmax = TRUE, aucint.last = TRUE + ), options = list(allow_partial_missing_units = TRUE)) + expect_warning( + suppressMessages(o_nca <- pk.nca(o_data)), + regexp = "Units are provided for some" + ) + + result_cdisc <- as.data.frame(o_nca, out_format = "cdisc") + + # PPSTINT and PPENINT columns should exist + expect_true("PPSTINT" %in% names(result_cdisc)) + expect_true("PPENINT" %in% names(result_cdisc)) + + # INT rows should have values, non-INT rows should be NA + int_rows <- grepl("INT", result_cdisc$PPTESTCD, fixed = TRUE) + expect_true(any(int_rows), info = "At least one INT parameter should be present") + expect_true(all(!is.na(result_cdisc$PPSTINT[int_rows]))) + expect_true(all(!is.na(result_cdisc$PPENINT[int_rows]))) + expect_true(all(is.na(result_cdisc$PPSTINT[!int_rows]))) + expect_true(all(is.na(result_cdisc$PPENINT[!int_rows]))) + + # Values should be ISO 8601 durations relative to dose time (0) + # start=0, dose_time=0 -> PT0H; end=4, dose_time=0 -> PT4H + int_result <- result_cdisc[int_rows, ] + expect_equal(int_result$PPSTINT[1], "PT0H") + expect_equal(int_result$PPENINT[1], "PT4H") +}) + +test_that("PPSTINT/PPENINT uses timeu_pref when available", { + d_conc <- data.frame( + subject = rep(1, 5), + time = 0:4, + conc = c(0, 2, 1, 0.5, 0.25) + ) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject, timeu = "hr", timeu_pref = "min") + d_dose <- data.frame(subject = 1, time = 0, dose = 10) + o_dose <- PKNCAdose(d_dose, dose ~ time | subject) + o_data <- PKNCAdata(o_conc, o_dose, intervals = data.frame( + start = 0, end = 4, aucint.last = TRUE + ), options = list(allow_partial_missing_units = TRUE)) + expect_warning( + suppressMessages(o_nca <- pk.nca(o_data)), + regexp = "Units are provided for some" + ) + + result_cdisc <- as.data.frame(o_nca, out_format = "cdisc") + int_rows <- grepl("INT", result_cdisc$PPTESTCD, fixed = TRUE) + # timeu_pref is "min", so durations should use M designator + expect_equal(result_cdisc$PPSTINT[int_rows][1], "PT0M") + expect_equal(result_cdisc$PPENINT[int_rows][1], "PT4M") +}) + +test_that("PPSTINT/PPENINT computes relative to last dose time", { + d_conc <- data.frame( + subject = rep(1, 10), + time = 0:9, + conc = c(0, 2, 1, 0.5, 0.25, 0, 3, 1.5, 0.75, 0.3) + ) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject, timeu = "hr") + # Two doses: at time 0 and time 5 + d_dose <- data.frame(subject = c(1, 1), time = c(0, 5), dose = c(10, 10)) + o_dose <- PKNCAdose(d_dose, dose ~ time | subject) + o_data <- PKNCAdata(o_conc, o_dose, intervals = data.frame( + start = c(0, 5), end = c(5, 9), aucint.last = TRUE + ), options = list(allow_partial_missing_units = TRUE)) + expect_warning( + suppressMessages(o_nca <- pk.nca(o_data)), + regexp = "Units are provided for some" + ) + + result_cdisc <- as.data.frame(o_nca, out_format = "cdisc") + int_rows <- grepl("INT", result_cdisc$PPTESTCD, fixed = TRUE) + int_result <- result_cdisc[int_rows, ] + + # First interval: start=0, end=5, last_dose=0 -> PT0H, PT5H + row1 <- int_result[int_result$start == 0, ] + expect_equal(row1$PPSTINT[1], "PT0H") + expect_equal(row1$PPENINT[1], "PT5H") + + # Second interval: start=5, end=9, last_dose=5 -> PT0H, PT4H + row2 <- int_result[int_result$start == 5, ] + expect_equal(row2$PPSTINT[1], "PT0H") + expect_equal(row2$PPENINT[1], "PT4H") +}) + +test_that("PPSTINT/PPENINT uses day designator for day units", { + d_conc <- data.frame( + subject = rep(1, 4), + time = c(0, 1, 2, 3), + conc = c(0, 2, 1, 0.5) + ) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject, timeu = "day") + d_dose <- data.frame(subject = 1, time = 0, dose = 10) + o_dose <- PKNCAdose(d_dose, dose ~ time | subject) + o_data <- PKNCAdata(o_conc, o_dose, intervals = data.frame( + start = 0, end = 3, aucint.last = TRUE + ), options = list(allow_partial_missing_units = TRUE)) + expect_warning( + suppressMessages(o_nca <- pk.nca(o_data)), + regexp = "Units are provided for some" + ) + + result_cdisc <- as.data.frame(o_nca, out_format = "cdisc") + int_rows <- grepl("INT", result_cdisc$PPTESTCD, fixed = TRUE) + expect_equal(result_cdisc$PPSTINT[int_rows][1], "P0D") + expect_equal(result_cdisc$PPENINT[int_rows][1], "P3D") +}) + +test_that("format_iso8601_duration handles edge cases", { + expect_equal(PKNCA:::format_iso8601_duration(0, "hr"), "PT0H") + expect_equal(PKNCA:::format_iso8601_duration(24, "hr"), "PT24H") + expect_equal(PKNCA:::format_iso8601_duration(1.5, "hr"), "PT1.5H") + expect_equal(PKNCA:::format_iso8601_duration(30, "min"), "PT30M") + expect_equal(PKNCA:::format_iso8601_duration(3600, "s"), "PT3600S") + expect_equal(PKNCA:::format_iso8601_duration(7, "day"), "P7D") + expect_true(is.na(PKNCA:::format_iso8601_duration(NA, "hr"))) + expect_true(is.na(PKNCA:::format_iso8601_duration(Inf, "hr"))) +}) + +test_that("as.data.frame.PKNCAresults default format does not include PPSTINT/PPENINT", { + d_conc <- data.frame( + subject = rep(1, 5), + time = 0:4, + conc = c(0, 2, 1, 0.5, 0.25) + ) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject, timeu = "hr") + d_dose <- data.frame(subject = 1, time = 0, dose = 10) + o_dose <- PKNCAdose(d_dose, dose ~ time | subject) + o_data <- PKNCAdata(o_conc, o_dose, intervals = data.frame( + start = 0, end = 4, aucint.last = TRUE + ), options = list(allow_partial_missing_units = TRUE)) + expect_warning( + suppressMessages(o_nca <- pk.nca(o_data)), + regexp = "Units are provided for some" + ) + + # Default (long) format should not have PPSTINT/PPENINT + result_long <- as.data.frame(o_nca) + expect_false("PPSTINT" %in% names(result_long)) + expect_false("PPENINT" %in% names(result_long)) +}) + +test_that("pknca_cdisc_get_route falls back to extravascular when no dose data", { + d_conc <- data.frame(subject = rep(1, 4), time = 0:3, conc = c(0, 1, 0.5, 0.25)) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject) + o_data <- PKNCAdata(o_conc, intervals = data.frame(start = 0, end = 3, cmax = TRUE)) + suppressMessages(o_nca <- pk.nca(o_data)) + + result_cdisc <- as.data.frame(o_nca, out_format = "cdisc") + expect_true("PPTESTCD" %in% names(result_cdisc)) + expect_true("CMAX" %in% result_cdisc$PPTESTCD) +}) + +test_that("resolve_cdisc_value falls back to first route when route not matched", { + # Route-dependent list with unknown route should fall back to first element + val <- list(route = list(extravascular = "EV_CODE", intravascular = "IV_CODE")) + expect_equal(PKNCA:::resolve_cdisc_value(val, "unknown_route"), "EV_CODE") + # Non-list, non-character fallback + expect_equal(PKNCA:::resolve_cdisc_value(42, "extravascular"), "42") +}) + +test_that("format_iso8601_duration falls back to hours for unknown unit", { + expect_equal(PKNCA:::format_iso8601_duration(5, "fortnights"), "PT5H") + expect_equal(PKNCA:::format_iso8601_duration(10, NA), "PT10H") +}) + +test_that("pknca_cdisc_get_timeu returns NA when no conc data", { + # Minimal PKNCAresults with no conc object + minimal <- PKNCAresults(data.frame(a = 1), data = list()) + expect_true(is.na(PKNCA:::pknca_cdisc_get_timeu(minimal))) +}) + +test_that("pknca_cdisc_get_last_dose_time returns NA when no dose data", { + d_conc <- data.frame(subject = rep(1, 4), time = 0:3, conc = c(0, 1, 0.5, 0.25)) + o_conc <- PKNCAconc(d_conc, conc ~ time | subject) + o_data <- PKNCAdata(o_conc, intervals = data.frame(start = 0, end = 3, cmax = TRUE)) + suppressMessages(o_nca <- pk.nca(o_data)) + + ret <- as.data.frame(o_nca) + result <- PKNCA:::pknca_cdisc_get_last_dose_time(ret, o_nca) + expect_true(all(is.na(result))) +}) +