metrumresearchgroup / nmrec

R package to read, parse, and modify NONMEM control records
https://metrumresearchgroup.github.io/nmrec/
Other
1 stars 0 forks source link

Potential helper function: `set_table_columns` #6

Open barrettk opened 1 year ago

barrettk commented 1 year ago

@seth127 and I discussed the possibility of a helper function for amending columns in a $TABLE record. It was suggested that this functionality may be useful for bbr and/or bbr.bayes in an upcoming patch (discussed briefly here and here).

We discussed the idea of adding a replace argument, though this becomes tricky to implement when also allowing for column removals without needing more arguments. Given that we want to limit the arguments and simplify the function as much as possible, I opted to not include this for the prototype function, pasted below:

Function

set_table_columns ```r #' Get table columns from table record #' #' @param record An [nmrec_record] object. #' @param column a column to add or remove #' @param include Logical (T/F). If `TRUE`, add this column. If `FALSE`, remove the column. #' @param idx Index of new column. If specified, and `include = TRUE`, append the new column at this location #' #' @export set_table_columns <- function(record, column, include = TRUE, idx = NULL) { stopifnot(is.logical(include)) if (record[["name"]] != "table") { abort( sprintf("set_table_columns is only meant for %s records", record[["name"]]), nmrec_error() ) } table_cols <- get_record_option(record, "list1") if(!is.null(table_cols)){ old_cols <- strsplit(table_cols$value, " ")[[1]] if (any(old_cols == column) && isTRUE(include)) { abort( sprintf("Column %s is already present in %s record", column, record[["name"]]), nmrec_error() ) } if (isTRUE(include)) { if(is.null(idx)){ idx <- length(old_cols) }else{ stopifnot(idx %in% seq_along(old_cols)) idx <- idx - 1 # `append()` appends *after* specified id; shift by 1 } new_cols <- paste(append(old_cols, column, after = idx), collapse = " ") } else if (isFALSE(include) && any(old_cols == column)) { new_cols <- paste(old_cols[-which(old_cols == column)], collapse = " ") }else{ abort( sprintf("Attempted to remove non-existant column: %s", column), nmrec_error() ) } set_record_option(record, "list1", value = new_cols) } } ```

Use cases

Setup

ctl <- parse_ctl(get("bayes1", envir = nmrec_examples))
recs <- select_records(ctl, "table")
record <- recs[[1]]
> record
$TABLE ID TIME PRED RES WRES CPRED CWRES EPRED ERES EWRES NOAPPEND
       ONEHEADER FILE=example1.TAB NOPRINT

Examples

> # Add new column
> set_table_columns(record, "PRED3", include = TRUE)
> record
$TABLE ID TIME PRED RES WRES CPRED CWRES EPRED ERES EWRES PRED3 NOAPPEND
       ONEHEADER FILE=example1.TAB NOPRINT

> # Remove new column
> set_table_columns(record, "PRED3", include = FALSE)
> record
$TABLE ID TIME PRED RES WRES CPRED CWRES EPRED ERES EWRES NOAPPEND
       ONEHEADER FILE=example1.TAB NOPRINT

> # Add new column to index=3
> set_table_columns(record, "PRED3", include = TRUE, idx = 3)
> record
$TABLE ID TIME PRED3 PRED RES WRES CPRED CWRES EPRED ERES EWRES NOAPPEND
       ONEHEADER FILE=example1.TAB NOPRINT
kyleam commented 1 year ago

It was suggested that this functionality may be useful for bbr and/or bbr.bayes in an upcoming patch

Can you be more specific about what these use cases are? What's the proposed functionality?

barrettk commented 1 year ago

I would need @seth127 to chime in for the specifics. I said something to the effect of "I can only see the use of this helper if [x] were to happen", where x would be something like bbr or bbr.bayes creating and managing its own column for tracking something across runs, or otherwise providing additional info.

From that discussion, it seemed like that was very much a possibility in a future patch. That being said, I'm of the perspective that this would live on the back-burner until such a time arrived (if ever).

seth127 commented 1 year ago

@kyleam the one I had in mind was the nmbayes thing, related to run_sims() about automatically adding a new column one of the tables, so that we could reliably join against it down the line. I didn't think through it too deeply, but it seemed like that might be a use case for something like this.

Either way, I'm fine putting this on the back burner for now. @barrettk had already worked out some of this code/interface in working through #5, so I asked him to drop it in an issue for possibly revisiting later, if use cases arise.

kyleam commented 1 year ago

@seth127

the one I had in mind was the nmbayes thing

Thanks. I think we ended up landing on writing a dedicated table line, but we'll see how it plays out.

[...] so I asked him to drop it in an issue for possibly revisiting later, if use cases arise.

Sure. I was asking so that 1) I know what was discussed offline and 2) to capture specifics in this issue for posterity.

seth127 commented 1 year ago

Just so it's here for posterity, this is how a user can do something like adding a new column to the table record now:

cols_opt <- get_record_option(record, "list1")
cols_opt$values <- paste(cols_opt$values, "PRED2")

This gets a bit more complicated if you want to do something like "replace PRED with PRED2", but you're still just working with a (fairly simple) unparsed string at that point, so you can use regex or whatever.

I'll also mention, with the change in #5 something like the following could be possible. Though, per discussions (here for example) it looks like we might not actually implement that.

old_cols <- get_record_option(record, "list1")$value
# add a new column (or whatever manipulation you want to do...)
set_record_option(record, "list1", paste(old_cols, "PRED2"))

(And either way, this wouldn't change the fact that you'd need to lean on regex to do anything more complicated than appending a new column onto the end.)

barrettk commented 1 year ago

@seth127 just FYI the second method is no longer possible. We removed support for "list1" from set_record_option.

You will now have to do something like this (basically what you're suggesting in the first method):

tbls <- nmrec::select_records(ctl, "table")
tbl1 <- tbls[[1]]
tbl1_cols <- nmrec::get_record_option(tbl1, "list1")
old_cols <- tbl1_cols$format()
tbl1_cols$value <- paste(old_cols, "PRED2")