From 0d01bd1eb90ff87e6937e7e620f8ae7ecd7beb79 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Sun, 9 Aug 2026 11:43:31 -0400 Subject: [PATCH 1/3] docs: correct adj.r.squared.factor, span.ratio, and aucint.inf description strings Three runtime description strings contradicted the implementation (issue 582): - The adj.r.squared.factor option description claimed an additive 'factor times the number of data points' criterion; the code keeps candidate lambda.z regressions with an adjusted r-squared within adj.r.squared.factor of the best and then selects the one with the most data points. - The span.ratio parameter desc stated the inverse ratio; the code computes the regression time span divided by the half-life. - The aucint.inf.obs, aucint.inf.pred, and .dose variant descs were copy-pasted from aucint.last/aucint.all; these parameters extrapolate beyond Tlast using the half-life and the observed or predicted Clast. Add regression tests pinning the corrected wording and a NEWS.md entry. Co-Authored-By: Claude Fable 5 --- NEWS.md | 9 +++++++++ R/PKNCA.options.R | 9 ++++++--- R/aucint.R | 8 ++++---- R/half.life.R | 2 +- tests/testthat/test-PKNCA.options.R | 6 ++++++ tests/testthat/test-aucint.R | 24 ++++++++++++++++++++++++ tests/testthat/test-half.life.R | 8 ++++++++ 7 files changed, 58 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 563fa773..e7dcafc7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,15 @@ the dosing including dose amount and route. # Development version +* Documentation fix: three runtime description strings that contradicted the + implementation were corrected: the `adj.r.squared.factor` option description + (it is a tolerance below the best adjusted r-squared, not an additive + per-point bonus), the `span.ratio` parameter description (the regression time + span divided by the half-life, not the inverse), and the descriptions of + `aucint.inf.obs`, `aucint.inf.pred`, and their `.dose` variants (they + extrapolate using the half-life and the observed/predicted Clast rather than + the AUClast/AUCall rules) (#582). + * Bug fix: `pk.nca()` no longer errors on unsorted concentration-time data. Group-level concentration data are now sorted by time before calculation, so parameters that use the full group (e.g. `aucint.all` and the other `aucint*` diff --git a/R/PKNCA.options.R b/R/PKNCA.options.R index 24bb3f02..6d2122bf 100644 --- a/R/PKNCA.options.R +++ b/R/PKNCA.options.R @@ -4,9 +4,12 @@ adj.r.squared.factor=function(x, default=FALSE, description=FALSE) { if (description) return(paste( - "The adjusted r^2 for the calculation of lambda.z has this factor", - "times the number of data points added to it. It allows for more", - "data points to be preferred in the calculation of half-life.")) + "During the calculation of lambda.z, all candidate regressions", + "with an adjusted r^2 within adj.r.squared.factor of the best", + "adjusted r^2 are considered acceptable, and the acceptable", + "regression using the most data points is selected. It allows", + "for more data points to be preferred in the calculation of", + "half-life.")) if (default) return(0.0001) if (length(x) != 1) diff --git a/R/aucint.R b/R/aucint.R index 5c343c68..6f3d0c7c 100644 --- a/R/aucint.R +++ b/R/aucint.R @@ -292,7 +292,7 @@ add.interval.col("aucint.inf.obs", values=c(FALSE, TRUE), unit_type="auc", 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)", + desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity using the half-life and the observed Clast (matching AUCinf,obs)", formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL), depends=c("lambda.z", "clast.obs"), pptestcd_cdisc="AUCINTIS", @@ -303,7 +303,7 @@ add.interval.col("aucint.inf.obs.dose", values=c(FALSE, TRUE), unit_type="auc", 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", + desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity using the half-life and the observed Clast (matching AUCinf,obs) 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"), pptestcd_cdisc="AUCINTID", @@ -314,7 +314,7 @@ add.interval.col("aucint.inf.pred", values=c(FALSE, TRUE), unit_type="auc", 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)", + desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity using the half-life and the predicted Clast (matching AUCinf,pred)", formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL), depends=c("lambda.z", "clast.pred"), pptestcd_cdisc="AUCINTIP", @@ -325,7 +325,7 @@ add.interval.col("aucint.inf.pred.dose", values=c(FALSE, TRUE), unit_type="auc", 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", + desc="The area under the concentration time curve in the interval extrapolating from Tlast to infinity using the half-life and the predicted Clast (matching AUCinf,pred) 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"), pptestcd_cdisc="AUCINTPD", diff --git a/R/half.life.R b/R/half.life.R index b84c77a5..53f0af80 100644 --- a/R/half.life.R +++ b/R/half.life.R @@ -762,7 +762,7 @@ add.interval.col("span.ratio", values=c(FALSE, TRUE), unit_type="fraction", pretty_name="Span ratio", - desc="The ratio of the half-life to the duration used for half-life calculation", + desc="The ratio of the lambda z regression time span to the half-life", depends="half.life", pptestcd_cdisc="LAMZSPN", pptest_cdisc="Lambda z Span") diff --git a/tests/testthat/test-PKNCA.options.R b/tests/testthat/test-PKNCA.options.R index f805ccd8..b838a5b1 100644 --- a/tests/testthat/test-PKNCA.options.R +++ b/tests/testthat/test-PKNCA.options.R @@ -467,6 +467,12 @@ test_that("PKNCA.options.describe", { info="Option descriptions are provided accurately.") }) +test_that("adj.r.squared.factor description matches the within-tolerance selection in the code (#582)", { + desc <- PKNCA:::PKNCA.options.describe("adj.r.squared.factor") + expect_match(desc, "within adj.r.squared.factor of the best adjusted r^2", fixed=TRUE) + expect_match(desc, "regression using the most data points is selected", fixed=TRUE) +}) + test_that("PKNCA.options fails when setting defaults and another option simultaneously", { expect_error( PKNCA.options(default=TRUE, tau.choices=24), diff --git a/tests/testthat/test-aucint.R b/tests/testthat/test-aucint.R index ed4d36b3..2ee264c1 100644 --- a/tests/testthat/test-aucint.R +++ b/tests/testthat/test-aucint.R @@ -738,3 +738,27 @@ test_that("Integration functions are passed correctly through wrapper", { expect_true(is.numeric(auc_result) && auc_result > 0) expect_true(is.numeric(aumc_result) && aumc_result > 0) }) + +test_that("aucint.inf descriptions state AUCinf extrapolation, not the AUClast/AUCall rules (#582)", { + cols <- get.interval.cols() + expect_match( + cols[["aucint.inf.obs"]]$desc, + "using the half-life and the observed Clast (matching AUCinf,obs)", + fixed=TRUE + ) + expect_match( + cols[["aucint.inf.obs.dose"]]$desc, + "using the half-life and the observed Clast (matching AUCinf,obs) with dose-aware interpolation/extrapolation of concentrations", + fixed=TRUE + ) + expect_match( + cols[["aucint.inf.pred"]]$desc, + "using the half-life and the predicted Clast (matching AUCinf,pred)", + fixed=TRUE + ) + expect_match( + cols[["aucint.inf.pred.dose"]]$desc, + "using the half-life and the predicted Clast (matching AUCinf,pred) with dose-aware interpolation/extrapolation of concentrations", + fixed=TRUE + ) +}) diff --git a/tests/testthat/test-half.life.R b/tests/testthat/test-half.life.R index 1e932b99..8d2ef39d 100644 --- a/tests/testthat/test-half.life.R +++ b/tests/testthat/test-half.life.R @@ -996,3 +996,11 @@ test_that("pk.calc.half.life tobit manually.selected.points sets exclude for neg "Negative half-life estimated with manually-selected points" ) }) + +test_that("span.ratio description is the time span divided by the half-life, not the inverse (#582)", { + expect_match( + get.interval.cols()[["span.ratio"]]$desc, + "lambda z regression time span to the half-life", + fixed=TRUE + ) +}) From b830183f2b3dc604df6e5a125bafb22a91c6f580 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Sun, 9 Aug 2026 13:24:31 -0400 Subject: [PATCH 2/3] docs: correct aumcint.inf description strings as well The aumcint.inf.obs, aumcint.inf.pred, and .dose variant descs carried the same copy-paste defect as their aucint.inf counterparts (issue 582): they described the AUMClast/AUMCall extrapolation rules, but these parameters extrapolate beyond Tlast using the half-life and the observed or predicted Clast. Extend the wording-pinning tests and the NEWS.md bullet to cover them. Co-Authored-By: Claude Fable 5 --- NEWS.md | 9 +++++---- R/aucint.R | 8 ++++---- tests/testthat/test-aucint.R | 24 ++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index e7dcafc7..a965688d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,14 +6,15 @@ the dosing including dose amount and route. # Development version -* Documentation fix: three runtime description strings that contradicted the +* Documentation fix: runtime description strings that contradicted the implementation were corrected: the `adj.r.squared.factor` option description (it is a tolerance below the best adjusted r-squared, not an additive per-point bonus), the `span.ratio` parameter description (the regression time span divided by the half-life, not the inverse), and the descriptions of - `aucint.inf.obs`, `aucint.inf.pred`, and their `.dose` variants (they - extrapolate using the half-life and the observed/predicted Clast rather than - the AUClast/AUCall rules) (#582). + `aucint.inf.obs`, `aucint.inf.pred`, `aumcint.inf.obs`, `aumcint.inf.pred`, + and their `.dose` variants (they extrapolate using the half-life and the + observed/predicted Clast rather than the AUClast/AUCall and + AUMClast/AUMCall rules) (#582). * Bug fix: `pk.nca()` no longer errors on unsorted concentration-time data. Group-level concentration data are now sorted by time before calculation, so diff --git a/R/aucint.R b/R/aucint.R index 6f3d0c7c..74eafb2b 100644 --- a/R/aucint.R +++ b/R/aucint.R @@ -443,7 +443,7 @@ add.interval.col("aumcint.inf.obs", values=c(FALSE, TRUE), unit_type="aumc", pretty_name="AUMCint (based on AUMCinf,obs extrapolation)", - desc="The area under the moment curve in the interval extrapolating from Tlast to infinity with zeros (matching AUMClast)", + desc="The area under the moment curve in the interval extrapolating from Tlast to infinity using the half-life and the observed Clast (matching AUMCinf,obs)", formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL), depends=c("lambda.z", "clast.obs")) @@ -453,7 +453,7 @@ add.interval.col("aumcint.inf.obs.dose", values=c(FALSE, TRUE), unit_type="aumc", pretty_name="AUMCint (based on AUMCinf,obs extrapolation, dose-aware)", - desc="The area under the moment curve in the interval extrapolating from Tlast to infinity with zeros (matching AUMClast) with dose-aware interpolation/extrapolation of concentrations", + desc="The area under the moment curve in the interval extrapolating from Tlast to infinity using the half-life and the observed Clast (matching AUMCinf,obs) 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")) @@ -463,7 +463,7 @@ add.interval.col("aumcint.inf.pred", values=c(FALSE, TRUE), unit_type="aumc", pretty_name="AUMCint (based on AUMCinf,pred extrapolation)", - desc="The area under the moment curve in the interval extrapolating from Tlast to infinity with the triangle from Tlast to the next point and zero thereafter (matching AUMCall)", + desc="The area under the moment curve in the interval extrapolating from Tlast to infinity using the half-life and the predicted Clast (matching AUMCinf,pred)", formalsmap=list(conc="conc.group", time="time.group", time.dose=NULL), depends=c("lambda.z", "clast.pred")) @@ -473,7 +473,7 @@ add.interval.col("aumcint.inf.pred.dose", values=c(FALSE, TRUE), unit_type="aumc", pretty_name="AUMCint (based on AUMCinf,pred extrapolation, dose-aware)", - desc="The area under the moment curve in the interval extrapolating from Tlast to infinity with the triangle from Tlast to the next point and zero thereafter (matching AUMCall) with dose-aware interpolation/extrapolation of concentrations", + desc="The area under the moment curve in the interval extrapolating from Tlast to infinity using the half-life and the predicted Clast (matching AUMCinf,pred) 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")) diff --git a/tests/testthat/test-aucint.R b/tests/testthat/test-aucint.R index 2ee264c1..5d78a7b1 100644 --- a/tests/testthat/test-aucint.R +++ b/tests/testthat/test-aucint.R @@ -762,3 +762,27 @@ test_that("aucint.inf descriptions state AUCinf extrapolation, not the AUClast/A fixed=TRUE ) }) + +test_that("aumcint.inf descriptions state AUMCinf extrapolation, not the AUMClast/AUMCall rules (#582)", { + cols <- get.interval.cols() + expect_match( + cols[["aumcint.inf.obs"]]$desc, + "using the half-life and the observed Clast (matching AUMCinf,obs)", + fixed=TRUE + ) + expect_match( + cols[["aumcint.inf.obs.dose"]]$desc, + "using the half-life and the observed Clast (matching AUMCinf,obs) with dose-aware interpolation/extrapolation of concentrations", + fixed=TRUE + ) + expect_match( + cols[["aumcint.inf.pred"]]$desc, + "using the half-life and the predicted Clast (matching AUMCinf,pred)", + fixed=TRUE + ) + expect_match( + cols[["aumcint.inf.pred.dose"]]$desc, + "using the half-life and the predicted Clast (matching AUMCinf,pred) with dose-aware interpolation/extrapolation of concentrations", + fixed=TRUE + ) +}) From d7d1d378809061c834713e4e8ed384eac856ff32 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Mon, 10 Aug 2026 04:23:27 -0400 Subject: [PATCH 3/3] docs: drop NEWS entry for docs-only change Per maintainer guidance, documentation-only changes (including parameter description strings and dead-code removal with no behavior change) do not need NEWS items. Co-Authored-By: Claude Fable 5 --- NEWS.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index a965688d..563fa773 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,16 +6,6 @@ the dosing including dose amount and route. # Development version -* Documentation fix: runtime description strings that contradicted the - implementation were corrected: the `adj.r.squared.factor` option description - (it is a tolerance below the best adjusted r-squared, not an additive - per-point bonus), the `span.ratio` parameter description (the regression time - span divided by the half-life, not the inverse), and the descriptions of - `aucint.inf.obs`, `aucint.inf.pred`, `aumcint.inf.obs`, `aumcint.inf.pred`, - and their `.dose` variants (they extrapolate using the half-life and the - observed/predicted Clast rather than the AUClast/AUCall and - AUMClast/AUMCall rules) (#582). - * Bug fix: `pk.nca()` no longer errors on unsorted concentration-time data. Group-level concentration data are now sorted by time before calculation, so parameters that use the full group (e.g. `aucint.all` and the other `aucint*`