Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ Version: 0.11.0.9000
Authors@R: c(
person("Bill", "Denney", email="wdenney@humanpredictions.com", role=c("aut", "cre"), comment=c(ORCID="0000-0002-5759-428X")),
person("Clare", "Buckeridge", email="clare.buckeridge@pfizer.com", role="aut"),
person("Sridhar", "Duvvuri", role="ctb"))
person("Sridhar", "Duvvuri", role="ctb"),
person("Gerardo Jose", "Rodriguez", , "gerardo.jrac@gmail.com", role = "aut", comment = c(ORCID = "0000-0003-1413-0060"))
)
Imports:
checkmate,
dplyr (>= 0.5.0),
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ export(PKNCA.choose.option)
export(PKNCA.options)
export(PKNCA.options.describe)
export(PKNCA.set.summary)
export(PKNCA_impute_method_start_c1)
export(PKNCA_impute_method_start_cmin)
export(PKNCA_impute_method_start_conc0)
export(PKNCA_impute_method_start_logslope)
export(PKNCA_impute_method_start_predose)
export(PKNCAconc)
export(PKNCAdata)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ the dosing including dose amount and route.

* `PKNCAconc()` and `PKNCAdose()` can now accept unit specifications as either
column names or units to use (#336).
* New default PKNCA impute start methods for C1 and IV bolus logslope to add for
intervals without a start concentration
* PKNCA options can now use `tmax` as a reference for BLQ handling by using new
names in the `conc.blq` argument (`before.tmax`,`after.tmax`)

Expand Down
47 changes: 47 additions & 0 deletions R/impute.R
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,53 @@ PKNCA_impute_method_start_predose <- function(conc, time, start, end, conc.group
ret
}

#' @describeIn PKNCA_impute_method Imputes based on a logarithmic slope on the
#' first points at time zero concentration (usually in IV bolus at dose time).
#' If this imputation is used for NCA calculations, back-extrapolation percent
#' will not be calculated or will be calculated as zero.
#' @export
PKNCA_impute_method_start_logslope <- function(conc, time, start, end, ..., options = list()) { # nolint

ret <- data.frame(conc = conc, time = time)
mask_start <- time %in% start
if (!any(mask_start)) {
all_concs <- conc[time >= start & time <= end]
all_times <- time[time >= start & time <= end]
if (!all(is.na(all_concs))) {
c0 <- pk.calc.c0.method.logslope(conc = all_concs,
time = all_times,
time.dose = start)
if (!is.na(c0)) {
ret <- rbind(ret, data.frame(time = start, conc = c0))
ret <- ret[order(ret$time), ]
}
}
}
ret
}

#' @describeIn PKNCA_impute_method Shift the following concentration to a start
#' to become the time zero concentration (rarely used; non-monodecay IV bolus).
#' If this imputation is used for NCA calculations, back-extrapolation percent
#' will not be calculated or will be calculated as zero.
#' @return A data frame with imputed start concentration.
#' @export
PKNCA_impute_method_start_c1 <- function(conc, time, start, end, ..., options = list()) { # nolint
ret <- data.frame(conc = conc, time = time)
mask_start <- time %in% start
if (!any(mask_start)) {
assert_conc_time(conc, time)
all_concs <- conc[time >= start & time <= end]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use pk.calc.c0.method.c1. That way, you don't need to filter all_concs, all_times, and your is.na() test below can be on c1. That will also ensure that c1 is consistently calculated anywhere it is used within PKNCA.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an assert before all_concs <- because if any time=NA all_concs will contain NA, let me know what you think of it!

all_times <- time[time >= start & time <= end]
c1 <- pk.calc.c0.method.c1(conc = all_concs,
time = all_times,
time.dose = start)
ret <- rbind(ret, data.frame(time = start, conc = c1))
ret <- ret[order(ret$time), ]
}
ret
}

#' Separate out a vector of PKNCA imputation methods into a list of functions
#'
#' An error will be raised if the functions are not found.
Expand Down
21 changes: 21 additions & 0 deletions man/PKNCA_impute_method.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

84 changes: 84 additions & 0 deletions tests/testthat/test-impute.R
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,90 @@ test_that("PKNCA_impute_method_start_cmin", {

})

test_that("PKNCA_impute_method_start_logslope", {
# No imputation when start is in the data
expect_equal(
PKNCA_impute_method_start_logslope(conc = 3:1, time = 0:2, start = 0, end = 2),
data.frame(conc = 3:1, time = 0:2)
)
# Impute when start is not in the data
expect_equal(
PKNCA_impute_method_start_logslope(conc = 3:1, time = 1:3, start = 0, end = 3),
data.frame(conc = c(4.5, 3:1), time = 0:3),
ignore_attr = TRUE
)
# Data outside the interval are ignored (before interval)
expect_equal(
PKNCA_impute_method_start_logslope(conc = c(0, 2:1), time = c(-1, 1:2), start = 0, end = 2),
data.frame(conc = c(0, 4, 2:1), time = c(-1, 0, 1:2)),
ignore_attr = TRUE
)
# No modification if no C1 -> C2 decline in samples
expect_equal(
PKNCA_impute_method_start_logslope(conc = c(1, 1, 1), time = 1:3, start = 0, end = 3),
data.frame(conc = c(1, 1, 1), time = 1:3),
ignore_attr = TRUE
)
# No modification if C1 = C2 in samples
expect_equal(
PKNCA_impute_method_start_logslope(conc = c(3, 3, 1), time = 1:3, start = 0, end = 3),
data.frame(conc = c(3, 3, 1), time = 1:3),
ignore_attr = TRUE
)
# All concentrations are NA -> does not change
expect_equal(
PKNCA_impute_method_start_logslope(conc = c(NA, NA, NA), time = 1:3, start = 0, end = 3),
data.frame(conc = c(NA, NA, NA), time = 1:3)
)
# All times are NA -> does not change
expect_equal(
PKNCA_impute_method_start_logslope(conc = 1:3, time = c(NA, NA, NA), start = 0, end = 3),
data.frame(conc = 1:3, time = c(NA, NA, NA))
)
# All concentrations are 0 -> does not change
expect_equal(
PKNCA_impute_method_start_logslope(conc = c(0, 0, 0), time = 1:3, start = 0, end = 3),
data.frame(conc = c(0, 0, 0), time = 1:3)
)
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add tests for:

  1. All concentrations are NA
  2. All times are NA
  3. All concentrations are 0

(I see that list of tests is needed for the other imputation methods, too. I just added an issue, #361, to add those after this PR is merged.)

Copy link
Contributor Author

@Gero1999 Gero1999 Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey sorry for the delay on this @billdenney! Some questions regarding the cases and their expected outputs:

  1. All concentrations are NA

Initially I developed the functions using the same strucuture as PKNCA_impute_method_start_cmin:

PKNCA_impute_method_start_cmin <- function(conc, time, start, end, ..., options = list()) {
  ret <- data.frame(conc = conc, time = time)
  mask_start <- time %in% start
  if (!any(mask_start)) {
    all_concs <- conc[start <= time & time <= end]
    if (!all(is.na(all_concs))) {
      cmin <- min(all_concs, na.rm = TRUE)
      ret <- rbind(ret, data.frame(time = start, conc = cmin))
      ret <- ret[order(ret$time), ]
    }
  }
  ret
}

In this case, all conc = NA will return the same dataset for PKNCA_impute_method_start_cmin

But if for PKNCA_impute_method_start_c1 we want directly the original pk.calc.c0.method.c1 handling the NA case like in the original function, when all conc = NA then start = NA with a warning message

Is this the desired behavior we would like to have for PKNCA_impute_method_start_c1 even if it is different to PKNCA_impute_method_start_cmin? The same with PKNCA_impute_method_start_logslope?

  1. All times are NA

Returns same dataset for PKNCA_impute_method_start_cmin
Returns error for PKNCA_impute_method_start_c1 with default pk.calc.c0.method.c1 behaviour

  1. All concs are 0

Should PKNCA_impute_method_start_logslope return same dataset or NA?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept their original behaviours of these testing cases as on my initial pr. Let me know if you think I need to standardize them. This is probably something we need to consider on the other PKNCA_impute_method_start when doing #361

PKNCA_impute_method_start_logslope

  1. All concentrations are NA:

    • The function does not change the input data. The output will have the same NA concentrations as the input.
  2. All times are NA:

    • The function does not change the input data. The output will have the same NA times as the input.
  3. All concentrations are 0:

    • The function does not change the input data. The output will have the same 0 concentrations as the input.

PKNCA_impute_method_start_c1

  1. All concentrations are NA:

    • The function adds the start time with an NA concentration to the data. The output will have NA concentrations, including the imputed start time.
  2. All times are NA:

    • The function raises an error indicating that the time vector contains missing values.
  3. All concentrations are 0:

    • The function adds the start time with a 0 concentration to the data. The output will have 0 concentrations, including the imputed start time.


test_that("PKNCA_impute_method_start_c1", {
# No imputation when start is in the data
expect_equal(
PKNCA_impute_method_start_c1(conc = 1:3, time = 0:2, start = 0, end = 2),
data.frame(conc = 1:3, time = 0:2)
)
# Impute when start is not in the data
expect_equal(
PKNCA_impute_method_start_c1(conc = 1:3, time = 1:3, start = 0, end = 3),
data.frame(conc = c(1, 1:3), time = 0:3),
ignore_attr = TRUE
)
# Data outside the interval are ignored (before interval)
expect_equal(
PKNCA_impute_method_start_c1(conc = 1:3, time = c(-1, 1:2), start = 0, end = 2),
data.frame(conc = c(1, 2, 2:3), time = c(-1, 0, 1:2)),
ignore_attr = TRUE
)
# All concentrations are NA
expect_equal(
PKNCA_impute_method_start_c1(conc = c(NA, NA, NA), time = 1:3, start = 0, end = 3),
data.frame(conc = c(NA, NA, NA, NA), time = 0:3),
ignore_attr = TRUE
)
# All times are NA
expect_error(
PKNCA_impute_method_start_c1(conc = 1:3, time = c(NA, NA, NA), start = 0, end = 3),
"Assertion on 'time' failed: Contains missing values"
)
# All concentrations are 0
expect_equal(
PKNCA_impute_method_start_c1(conc = c(0, 0, 0), time = 1:3, start = 0, end = 3),
data.frame(conc = c(0, 0, 0, 0), time = 0:3),
ignore_attr = TRUE
)
})

test_that("PKNCA_impute_fun_list", {
expect_equal(
PKNCA_impute_fun_list(NA_character_),
Expand Down
Loading