@@ -82,7 +82,8 @@ format_pkncadata_intervals <- function(pknca_conc,
8282 )))
8383
8484 # Based on dose times create a data frame with start and end times
85- dose_intervals <- left_join(sub_pknca_dose ,
85+ dose_intervals <- left_join(
86+ sub_pknca_dose ,
8687 sub_pknca_conc ,
8788 by = intersect(names(sub_pknca_dose ), c(conc_groups , " DOSNOA" )),
8889 relationship = " many-to-many"
@@ -128,11 +129,74 @@ format_pkncadata_intervals <- function(pknca_conc,
128129 mutate(type_interval = " main" )
129130}
130131
132+ # ' Derive study types from a PKNCAdata object
133+ # '
134+ # ' Extracts concentration and dose data, merges dose duration if needed,
135+ # ' and calls [detect_study_types()] to classify each group.
136+ # '
137+ # ' @param data A PKNCAdata object.
138+ # '
139+ # ' @returns A deduplicated data frame with grouping columns and a `type` column.
140+ # '
141+ # ' @importFrom dplyr mutate left_join filter select slice_max distinct across all_of
142+ # ' @noRd
143+ # ' @keywords internal
144+ .derive_study_types <- function (data ) {
145+ conc_data <- data $ conc $ data
146+ conc_groups <- group_vars(data $ conc )
147+ dose_groups <- group_vars(data $ dose )
148+ groups <- intersect(dose_groups , conc_groups )
149+ groups <- groups [vapply(groups , function (col ) {
150+ ! is.null(col ) && length(unique(conc_data [[col ]])) > 1
151+ }, logical (1 ))]
152+
153+ # Blank METABFL unconditionally so metabolite-specific types are not
154+ # assigned at the interval level.
155+ conc_data $ METABFL <- " "
156+
157+ # Dose duration may live in dose data only; merge it for detect_study_types.
158+ duration_col <- data $ dose $ columns $ duration
159+ if (! is.null(duration_col ) && ! duration_col %in% names(conc_data )) {
160+ dose_data <- data $ dose $ data
161+ if (duration_col %in% names(dose_data )) {
162+ conc_time <- data $ conc $ columns $ time
163+ dose_time <- data $ dose $ columns $ time
164+ join_by <- intersect(dose_groups , conc_groups )
165+
166+ dose_subset <- dose_data [, unique(c(join_by , dose_time , duration_col )), drop = FALSE ]
167+ dose_subset <- unique(dose_subset )
168+ names(dose_subset )[names(dose_subset ) == dose_time ] <- " .dose_time"
169+
170+ conc_data <- conc_data %> %
171+ mutate(.ROWID = seq_len(n())) %> %
172+ left_join(dose_subset , by = join_by , relationship = " many-to-many" ) %> %
173+ filter(.dose_time < = .data [[conc_time ]] | is.na(.dose_time )) %> %
174+ slice_max(.dose_time , n = 1 , by = .ROWID , with_ties = FALSE ) %> %
175+ select(- " .ROWID" , - " .dose_time" )
176+ } else {
177+ conc_data [[duration_col ]] <- 0
178+ }
179+ }
180+
181+ study_types_df <- detect_study_types(
182+ conc_data ,
183+ groups ,
184+ metabfl_column = " METABFL" ,
185+ route_column = data $ dose $ columns $ route ,
186+ volume_column = data $ conc $ columns $ volume
187+ )
188+
189+ # Deduplicate by grouping columns to prevent interval row duplication
190+ # when detect_study_types produces multiple types per group.
191+ grouping_cols <- setdiff(names(study_types_df ), " type" )
192+ study_types_df %> %
193+ distinct(across(all_of(grouping_cols )), .keep_all = TRUE )
194+ }
195+
131196# ' Update an intervals data frame with user-selected parameters by study type
132197# '
133198# ' @param data A PKNCAdata object containing intervals and dosing data.
134199# ' @param parameter_selections A named list of selected PKNCA parameters by study type.
135- # ' @param study_types_df A data frame mapping analysis profiles to their study type.
136200# ' @param int_parameters A data frame containing partial AUC ranges.
137201# ' @param impute Logical indicating whether to impute start values for parameters.
138202# ' @param blq_imputation_rule A list defining the Below Limit of Quantification (BLQ)
@@ -143,22 +207,30 @@ format_pkncadata_intervals <- function(pknca_conc,
143207# ' which does not specify any BLQ imputation in any interval.
144208# '
145209# ' @importFrom dplyr left_join mutate across where select all_of if_else bind_rows filter
210+ # ' @importFrom dplyr group_by ungroup slice_max distinct
146211# ' @importFrom purrr pmap
147212# ' @returns An updated PKNCAdata object with parameter intervals based on user selections.
148213# ' @export
149214update_main_intervals <- function (
150215 data ,
151- parameter_selections ,
152- study_types_df , int_parameters ,
216+ parameter_selections = NULL ,
217+ int_parameters = NULL ,
153218 impute = TRUE ,
154219 blq_imputation_rule = NULL
155220) {
221+ if (is.null(parameter_selections )) parameter_selections <- list ()
222+ if (is.null(int_parameters )) {
223+ int_parameters <- data.frame (
224+ parameter = character (), start_auc = numeric (), end_auc = numeric ()
225+ )
226+ }
227+
156228 all_pknca_params <- setdiff(names(PKNCA :: get.interval.cols()), c(" start" , " end" ))
157229
158- # Determine the grouping columns from the study_types_df
159- grouping_cols <- setdiff(names(study_types_df ), c(" type" ))
230+ study_types_df <- .derive_study_types(data )
231+
232+ grouping_cols <- setdiff(names(study_types_df ), " type" )
160233 missing_columns <- setdiff(grouping_cols , colnames(data $ intervals ))
161- # check for grouping cols in intervals
162234 if (length(missing_columns ) > 0 ) {
163235 stop(paste(" Missing required columns:" , paste(missing_columns , collapse = " , " )))
164236 }
0 commit comments