-
Notifications
You must be signed in to change notification settings - Fork 1
/
nm-file.R
162 lines (146 loc) · 5.29 KB
/
nm-file.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#' Read NONMEM files
#'
#' Reads in a whitespace-delimited NONMEM output file (for example .grd or .ext
#' or a table output) or a NONMEM input data file. Will print the number of rows
#' and columns when the file is loaded. This **printing can be suppressed** by
#' setting `options(bbr.verbose = FALSE)`.
#'
#' @details
#' `nm_file()` is called internally by the family of functions in this help doc,
#' as well as by [nm_tables()] and [nm_join()].
#'
#' `nm_file()` assumes there is only one table per file and therefore
#' `nm_file()` (and family) are _not_ compatible with files that have multiple
#' tables, for example an `.ext` file for a model with multiple estimation
#' methods or a table file from a model using `$SIM`. For these kinds of files,
#' consider using `data.table::fread()` with the `skip` and `nrows` arguments.
#'
#' @return A tibble with the data from the specified file and estimation method.
#'
#' @param .mod Either a `bbi_nonmem_model`, `bbi_nonmem_summary`, or a path to a
#' file to read in. If passing model object to `nm_file()`, must also pass `.suffix` that
#' will be passed through to [build_path_from_model()].
#' @inheritParams build_path_from_model
#' @param ... arguments passed through to methods. (Currently none.)
#' @seealso [nm_tables()], [nm_table_files()], [nm_join()]
#' @export
nm_file <- function(.mod, .suffix = NULL, ...) {
UseMethod("nm_file")
}
#' @export
nm_file.bbi_model <- function(.mod, .suffix = NULL, ...) {
check_model_object(.mod, c(NM_MOD_CLASS, NM_SUM_CLASS))
if (is.null(.suffix)) {
stop("Must pass .suffix to use nm_file.bbi_model")
}
.path <- build_path_from_model(.mod, .suffix)
nm_file(.path, .suffix = NULL)
}
#' @export
nm_file.character <- function(.mod, .suffix = NULL, ...) {
checkmate::assert_string(.mod)
if (!is.null(.suffix)) {
stop("Cannot pass .suffix to use nm_file.character; pass only file path to .mod")
}
nm_file_impl(.mod)
}
#' @describeIn nm_file Reads `.grd` file from a `bbi_nonmem_model` or
#' `bbi_nonmem_summary` object
#' @param .rename If `TRUE`, the default, will rename `.grd` columns to the
#' relevant parameter names. Otherwise will leave column names as is.
#' @export
nm_grd <- function(.mod, .rename = TRUE) {
check_model_object(.mod, c(NM_MOD_CLASS, NM_SUM_CLASS))
grd_df <- nm_file(.mod, .suffix = ".grd")
if (isTRUE(.rename)) {
.s <- model_summary(.mod)
.est_method <- length(.s$parameters_data)
lbl <- c(.s$parameter_names$theta[.s$parameters_data[[.est_method]]$fixed$theta==0],
.s$parameter_names$sigma[.s$parameters_data[[.est_method]]$fixed$sigma==0],
.s$parameter_names$omega[.s$parameters_data[[.est_method]]$fixed$omega==0])
names(grd_df) <- c("ITERATION", lbl)
}
return(grd_df)
}
#' @describeIn nm_file Reads `{get_model_id(.mod)}.tab` file from a
#' `bbi_nonmem_model` or `bbi_nonmem_summary` object
#' @export
nm_tab <- function(.mod) {
check_model_object(.mod, c(NM_MOD_CLASS, NM_SUM_CLASS))
nm_file(.mod, .suffix = ".tab")
}
#' @describeIn nm_file Reads `{get_model_id(.mod)}par.tab` file from a
#' `bbi_nonmem_model` or `bbi_nonmem_summary` object
#' @export
nm_par_tab <- function(.mod) {
check_model_object(.mod, c(NM_MOD_CLASS, NM_SUM_CLASS))
nm_file(.mod, .suffix = "par.tab")
}
#' @describeIn nm_file Reads the input data file from a `bbi_nonmem_model` or
#' `bbi_nonmem_summary` object
#' @importFrom data.table fread
#' @importFrom tibble as_tibble
#' @export
nm_data <- function(.mod) {
check_model_object(.mod, c(NM_MOD_CLASS, NM_SUM_CLASS))
.path <- get_data_path(.mod)
verbose_msg(glue("Reading data file: {basename(.path)}"))
.d <- fread(.path, na.strings = ".", verbose = FALSE)
.d <- remove_dup_cols(.d)
.d <- as_tibble(.d)
names(.d) <- toupper(names(.d))
verbose_msg(glue(" rows: {nrow(.d)}"))
verbose_msg(glue(" cols: {ncol(.d)}"))
verbose_msg("") # for newline
return(.d)
}
#####################################
# PRIVATE HELPERS AND IMPLEMENTATION
#' Implementation function for reading NONMEM files
#' @importFrom tibble as_tibble
#' @importFrom data.table fread
#' @importFrom stringr str_detect
#' @keywords internal
nm_file_impl <- function(.path) {
# read file and find top of table
verbose_msg(glue("Reading {basename(.path)}"))
# get line numbers of TABLE lines
checkmate::assert_file_exists(.path)
# read the file, but catch warning that tells us there are multiple tables
W <- NULL
.d <- withCallingHandlers({
withr::with_options(list(warn = 1), {
data <- fread(
.path,
na.strings = ".",
skip = 1,
verbose = FALSE
)
})
data <- remove_dup_cols(data)
data <- as_tibble(data)
data
},
warning = function(.w) {
if (str_detect(.w$message, "Stopped early.+TABLE NO")) {
W <<- paste(
"nm_file() does not support files with multiple tables in a single file.",
glue("{.path} appears to contain multiple tables and will be skipped."),
sep = "\n"
)
} else {
warning(.w)
}
invokeRestart("muffleWarning")
})
# if found multiple tables, raise custom warning and return NULL
if (!is.null(W)) {
warning(W)
return(NULL)
}
# format, message, and return
verbose_msg(glue(" rows: {nrow(.d)}"))
verbose_msg(glue(" cols: {ncol(.d)}"))
verbose_msg("") # for newline
return(.d)
}