Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
556e9a7
draft: blq imputation options in NCA setup
Gero1999 Jun 23, 2025
15b357b
fix: make work 'Set value for all BLQ'
Gero1999 Jun 25, 2025
9884b46
fix: improve invalid input text handling
Gero1999 Jun 26, 2025
8dab1e1
docs: roxygenise
Gero1999 Jun 26, 2025
2a7f54a
refactor: create blq_selectize helper fun to elude repetition
Gero1999 Jun 26, 2025
5b33ac1
refactor: lintr
Gero1999 Jun 26, 2025
61b87d0
docs: roxygenise
Gero1999 Jun 26, 2025
a643f94
fix: default before_tmax should be 0
Gero1999 Jun 27, 2025
39356c1
fix: problem in blq_selectize definition for BLQ value
Gero1999 Jun 27, 2025
f6cf248
Merge remote-tracking branch 'origin/main' into 139-feat/blq-options
Gero1999 Jun 27, 2025
f6b2c8a
data: AVAL < 0.05 was changed to 0 (BLQ)
Gero1999 Jun 27, 2025
5e3eee3
spelling: update wordlist to include BLQ
Gero1999 Jun 27, 2025
b745304
feat: add option for "No BLQ handling"
Gero1999 Jul 1, 2025
9c898ec
data: reset dataset back ADNCA
Gero1999 Jul 1, 2025
88ccadb
merge: solve conflicts in settings / tab_nca
Gero1999 Oct 19, 2025
668b377
try change strategy BLQ
Gero1999 Oct 20, 2025
b5816f3
Merge remote-tracking branch 'origin/main' into 139-feat/blq-options
Gero1999 Oct 22, 2025
6fdd4bc
stage/stash
Gero1999 Oct 22, 2025
430265c
merge main: solving easy conflicts in WORDLIST & PKNCA.R
Gero1999 Dec 4, 2025
ab3ccdc
try: failing to include BLQ in impute
Gero1999 Dec 4, 2025
7f72c3c
stage
Gero1999 Dec 4, 2025
18b8f33
fix: test with expected "" instead of NA
Gero1999 Dec 4, 2025
897a84e
try: simplify call of imputable params
Gero1999 Dec 4, 2025
a99999e
Merge remote-tracking branch 'origin/main' into 139-feat/blq-options
Gero1999 Dec 5, 2025
a90bc68
merge: solve conflict in create_start_impute.R ("" > NA_character_)
Gero1999 Dec 16, 2025
9ae60bc
fix: NA prevents issues ("") & use conc.group, time.group
Gero1999 Dec 16, 2025
05641d6
refactor: lintr
Gero1999 Dec 16, 2025
3fc8acd
rm PR changes that are not needed
Gero1999 Dec 16, 2025
f8e97de
test: fix expected NA (instead of "")
Gero1999 Dec 16, 2025
d626406
add missing global bindings
Gero1999 Dec 16, 2025
7a17abd
Apply style suggestions from code review
Gero1999 Dec 31, 2025
197db3b
suggestion: don't asign reactive should_impute_c0
Gero1999 Dec 31, 2025
1c20017
style: make equal name with return (blq_imputation_rule)
Gero1999 Dec 31, 2025
cfcfea1
merge: solved main conflict by mv blq code in update_PKNCA_object to …
Gero1999 Dec 31, 2025
1c5d37d
change default BLQ imputation to no imputation
Gero1999 Dec 31, 2025
fbc290a
refactor: define a fun to remove imputation methods
Gero1999 Dec 31, 2025
0e75210
refactor: lintr
Gero1999 Dec 31, 2025
22b4425
man: roxygenise
Gero1999 Dec 31, 2025
040f106
default blq_imputation_rule = NULL so tests do not have to be changed
Gero1999 Jan 2, 2026
6c1b9f0
news: inform of BLQ imputation
Gero1999 Jan 2, 2026
07f1a79
man: roxygenise
Gero1999 Jan 2, 2026
ddd9f60
docs: change docs in rm_impute_obs_params()
Gero1999 Jan 2, 2026
049ac6c
notification: refactor msg
Gero1999 Jan 2, 2026
5f2dbf0
refactor: lintr, roxygenise
Gero1999 Jan 2, 2026
1e08be0
change docs & rm arg blq_imputation_rule from PKNCA.R
Gero1999 Jan 2, 2026
2e46694
refactor: lintr
Gero1999 Jan 2, 2026
9940083
don't require in slope_plots blq_imputation_rule
Gero1999 Jan 2, 2026
5c49d11
english: correct comment grammar
Gero1999 Jan 2, 2026
b3ba671
early NULL return use length()==0
Gero1999 Jan 2, 2026
7e6ec73
docs: just docs change order arg blq_imputation_rule
Gero1999 Jan 2, 2026
8716335
Update NEWS.md
Gero1999 Jan 2, 2026
7cab948
start drafting tests for blq_imputation_rule arg
Gero1999 Jan 5, 2026
14c392a
finish tests for blq_imputation_rule
Gero1999 Jan 5, 2026
b3edf4f
refactor: lintr
Gero1999 Jan 5, 2026
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
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* New Parameter Selection section in NCA tab allowing to select parameters by study type (#795)
* The App optionally maps end of sample collection (AEFRLT) for excretion rate parameter calculations: ERTLST, ERTMAX. (#745)
* Option to upload multiple input files, which will be bound together to form a single ADNCA data set (#821)
* BLQ imputation rules can be applied to the NCA via `NCA Setup > Data Imputation` (#139)


## Bugs fixed
Expand Down
1 change: 1 addition & 0 deletions R/PKNCA.R
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ PKNCA_create_data_object <- function(adnca_data, nca_exclude_reason_columns = NU
intervals = intervals, #TODO: should be default
units = PKNCA_build_units_table(pknca_conc, pknca_dose)
)

pknca_data_object
}

Expand Down
106 changes: 76 additions & 30 deletions R/intervals.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ format_pkncadata_intervals <- function(pknca_conc,
dose_groups <- unname(unlist(pknca_dose$columns$groups))
time_column <- pknca_dose$columns$time
# Obtain all possible pknca parameters
params <- setdiff(names(PKNCA::get.interval.cols()),
c("start", "end"))
params <- setdiff(
names(PKNCA::get.interval.cols()),
c("start", "end")
)

# Select conc data and for time column give priority to non-predose samples
sub_pknca_conc <- pknca_conc$data %>%
Expand All @@ -67,29 +69,32 @@ format_pkncadata_intervals <- function(pknca_conc,
group_by(!!!syms(dose_groups)) %>%
mutate(is_one_dose = length(unique(DOSNOA)) == 1) %>%
ungroup() %>%
select(any_of(c(dose_groups,
time_column, "DOSNOA", "is_one_dose")))
select(any_of(c(
dose_groups,
time_column, "DOSNOA", "is_one_dose"
)))

# Based on dose times create a data frame with start and end times
dose_intervals <- left_join(sub_pknca_dose,
sub_pknca_conc,
by = intersect(names(sub_pknca_dose), c(conc_groups, "DOSNOA")),
relationship = "many-to-many") %>%

sub_pknca_conc,
by = intersect(names(sub_pknca_dose), c(conc_groups, "DOSNOA")),
relationship = "many-to-many"
) %>%
# Pick 1 per concentration group and dose number
group_by(!!!syms(dose_groups), DOSNOA) %>%
mutate(max_end = max(ARRLT, na.rm = TRUE)) %>% # calculate max end time for Dose group
filter(ARRLT >= 0) %>% # filter out negative ARRLT values
group_by(!!!syms(c(conc_groups, "DOSNOA"))) %>%
slice(1) %>% # slice one row per conc group
ungroup() %>%

# Make start from last dose (pknca_dose) or first concentration (pknca_conc)
mutate(start = if (start_from_last_dose) !!sym(time_column)
else !!sym(time_column) + !!sym("ARRLT")) %>%
mutate(start = if (start_from_last_dose) {
!!sym(time_column)
} else {
!!sym(time_column) + !!sym("ARRLT")
}) %>%
group_by(!!!syms(conc_groups)) %>%
arrange(start) %>%

# Make end based on next dose time (if no more, TRTRINT or last NFRLT)
mutate(end = if (has_trtrint) {
case_when(
Expand All @@ -104,17 +109,16 @@ format_pkncadata_intervals <- function(pknca_conc,
is_one_dose ~ Inf,
TRUE ~ start + max_end
)
}
) %>%
}) %>%
ungroup() %>%
select(any_of(c("start", "end", conc_groups,
"ATPTREF", "DOSNOA", "VOLUME"))) %>%

select(any_of(c(
"start", "end", conc_groups,
"ATPTREF", "DOSNOA", "VOLUME"
))) %>%
# Create logical columns with only TRUE for the NCA parameters requested by the user
mutate(!!!setNames(rep(FALSE, length(params)), params)) %>%
# Identify the intervals as the base ones for the NCA analysis
mutate(type_interval = "main")

}

#' Update an intervals data frame with user-selected parameters by study type
Expand All @@ -124,12 +128,22 @@ format_pkncadata_intervals <- function(pknca_conc,
#' @param study_types_df A data frame mapping analysis profiles to their study type.
#' @param auc_data A data frame containing partial AUC ranges.
#' @param impute Logical indicating whether to impute start values for parameters.
#' @param blq_imputation_rule A list defining the Below Limit of Quantification (BLQ)
#' imputation rule using PKNCA format. The list should either contain three elements named:
#' `first`, `middle`, and `last` or two elements named `before.tmax` and `after.tmax`.
#' Each element can be a numeric value (substituting the BLQ value), or a string such as
#' `"drop"` (ignores the value) or `"keep"` (keeps the value as 0). Default is NULL,
#' which does not specify any BLQ imputation in any interval.
#'
#' @returns An updated PKNCAdata object with parameter intervals based on user selections.
#'
update_main_intervals <- function(data, parameter_selections,
study_types_df, auc_data, impute = TRUE) {

update_main_intervals <- function(
data,
parameter_selections,
study_types_df, auc_data,
impute = TRUE,
blq_imputation_rule = NULL
) {
all_pknca_params <- setdiff(names(PKNCA::get.interval.cols()), c("start", "end"))

# Determine the grouping columns from the study_types_df
Expand Down Expand Up @@ -189,9 +203,32 @@ update_main_intervals <- function(data, parameter_selections,

# Impute start values if requested
if (impute) {
data <- apply_imputation(data)
data <- create_start_impute(data)
}

############################################
# Define a BLQ imputation method for PKNCA
# and apply it only for non-observational parameters

if (!is.null(blq_imputation_rule)) {
PKNCA_impute_method_blq <<- function(conc.group, time.group, ...) { # nolint
PKNCA::clean.conc.blq(conc = conc.group, time = time.group, conc.blq = blq_imputation_rule)
}
Comment on lines +214 to +216
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

Using the global assignment operator <<- creates a function in the global environment, which can lead to namespace pollution and testing difficulties. Consider passing this function as a parameter or using a more controlled scoping mechanism.

Suggested change
PKNCA_impute_method_blq <<- function(conc.group, time.group, ...) { # nolint
PKNCA::clean.conc.blq(conc = conc.group, time = time.group, conc.blq = blq_imputation_rule)
}
assign(
"PKNCA_impute_method_blq",
function(conc.group, time.group, ...) { # nolint
PKNCA::clean.conc.blq(
conc = conc.group,
time = time.group,
conc.blq = blq_imputation_rule
)
},
envir = .GlobalEnv
)

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@m-kolomanski will this be good enough?


data$intervals <- data$intervals %>%
mutate(
impute = ifelse(
is.na(impute) | impute == "",
"blq",
paste0("blq, ", impute)
)
)
}

############################################
# Remove any imputation from the observational parameters
data <- rm_impute_obs_params(data, metadata_nca_parameters)

data
}

Expand All @@ -201,25 +238,34 @@ update_main_intervals <- function(data, parameter_selections,
#' selectively removing imputation for parameters that are not dependent on AUC.
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

The description states this function "imputing start values and then selectively removing imputation" but this is no longer accurate. After the refactoring, this function only removes imputation from observational parameters (those not dependent on AUC). The start value imputation is now handled separately by create_start_impute. Update the description to accurately reflect that this function only removes imputation rules from parameters that are not dependent on AUC.

Copilot uses AI. Check for mistakes.
#'
#' @param data A PKNCAdata object.
#' @param nca_parameters A dataset containing the mapping between PKNCA terms and CDISC terms.
#' @param metadata_nca_parameters A data frame mapping PKNCA parameters (`PKNCA`)
#' and information on their parameter dependencies ('Depends').
#' @returns A PKNCAdata object with imputation rules applied.
#' @import dplyr
#'
apply_imputation <- function(data, nca_parameters = metadata_nca_parameters) {
data <- create_start_impute(data)

rm_impute_obs_params <- function(data, metadata_nca_parameters = metadata_nca_parameters) {
# Don't impute parameters that are not AUC dependent
params_auc_dep <- nca_parameters %>%
params_auc_dep <- metadata_nca_parameters %>%
filter(grepl("auc|aumc", PKNCA) | grepl("auc", Depends)) %>%
pull(PKNCA)

params_not_to_impute <- nca_parameters %>%
filter(!grepl("auc|aumc", PKNCA),
!grepl(paste0(params_auc_dep, collapse = "|"), Depends)) %>%
params_not_to_impute <- metadata_nca_parameters %>%
filter(
!grepl("auc|aumc", PKNCA),
!grepl(paste0(params_auc_dep, collapse = "|"), Depends)
) %>%
pull(PKNCA) %>%
intersect(names(PKNCA::get.interval.cols()))

all_impute_methods <- na.omit(unique(data$intervals$impute))
if (length(all_impute_methods) == 0) {
return(data)
}
all_impute_methods <- all_impute_methods %>%
strsplit(split = ",") %>%
unlist() %>%
trimws() %>%
unique()

data$intervals <- Reduce(function(d, ti_arg) {
interval_remove_impute(
Expand Down
5 changes: 5 additions & 0 deletions R/zzz.R
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"PCTPTNUM",
"PCTPTREF",
"PKNCA",
"PKNCA_impute_method_blq",
"PPANMETH",
"PPORRES",
"PPORRESU",
Expand Down Expand Up @@ -124,6 +125,7 @@
"exclude_half.life",
"facet_title",
"format_fun",
"has_param_to_impute",
"id_list",
"id_plot",
"id_variable_col",
Expand All @@ -132,6 +134,7 @@
"interval_name_col",
"interval_next",
"interval_prev",
"impute",
"is.excluded.hl",
"is.included.hl",
"is.iv",
Expand All @@ -153,6 +156,8 @@
"nom_dose_time",
"nom_interval_next",
"nom_interval_prev",
"param_to_impute_is_not_na",
"param_to_impute_is_not_false",
"pknca_units_tbl",
"ppanmeth_ref_groups",
"ppanmeth_test_groups",
Expand Down
15 changes: 10 additions & 5 deletions inst/shiny/modules/tab_nca/setup.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ setup_ui <- function(id) {
nav_panel(
"Settings",
fluidRow(
column(6,
column(
6,
fileInput(
ns("settings_upload"),
width = "100%",
Expand All @@ -30,7 +31,8 @@ setup_ui <- function(id) {
accept = ".rds"
)
),
column(6,
column(
6,
downloadButton(
ns("settings_download"),
label = "Download settings"
Expand Down Expand Up @@ -123,7 +125,8 @@ setup_server <- function(id, data, adnca_data) {
parameter_selections = parameters_output$selections(),
study_types_df = parameters_output$types_df(),
auc_data = settings()$partial_aucs,
impute = settings()$data_imputation$impute_c0
impute = settings()$data_imputation$impute_c0,
blq_imputation_rule = settings()$data_imputation$blq_imputation_rule
)

if (nrow(final_data$intervals) == 0) {
Expand Down Expand Up @@ -157,8 +160,10 @@ setup_server <- function(id, data, adnca_data) {
# Only parameters required for the slope plots are set in intervals
# NCA dynamic changes/filters based on user selections
slopes_pknca_data <- reactive({
req(adnca_data(), settings(), settings()$profile,
settings()$analyte, settings()$pcspec)
req(
adnca_data(), settings(), settings()$profile,
settings()$analyte, settings()$pcspec
)
log_trace("Updating PKNCA::data object for slopes.")

df <- PKNCA_update_data_object(
Expand Down
Loading
Loading