#' Extract model lines from a rxui model
#'
#' @param x model to extract lines from
#'
#' @param ... variables to extract. When it is missing, it will
#' extract the entire model (conditioned on the endpoint option
#' below)
#'
#' @param expression return expressions (if `TRUE`) or strings (if
#' `FALSE`)
#'
#' @param endpoint include endpoint. This can be:
#'
#' - `NA` -- Missing means include both the endpoint and non-endpoint lines
#'
#' - `TRUE` -- Only include endpoint lines
#'
#' - `FALSE` -- Only include non-endpoint lines
#'
#' @param lines is a boolean. When `TRUE` this will add the lines as
#' an attribute to the output value ie `attr(, "lines")`
#'
#' @param envir Environment for evaluating variables
#'
#' @return expressions or strings of extracted lines. Note if there is
#' a duplicated lhs expression in the line, it will return both
#' lines
#'
#' @export
#'
#' @author Matthew L. Fidler
#'
#' @examples
#'
#' one.compartment <- function() {
#' ini({
#' tka <- 0.45 # Log Ka
#' tcl <- 1 # Log Cl
#' tv <- 3.45 # Log V
#' eta.ka ~ 0.6
#' eta.cl ~ 0.3
#' eta.v ~ 0.1
#' add.sd <- 0.7
#' })
#' model({
#' ka <- exp(tka + eta.ka)
#' cl <- exp(tcl + eta.cl)
#' v <- exp(tv + eta.v)
#' d/dt(depot) <- -ka * depot
#' d/dt(center) <- ka * depot - cl / v * center
#' cp <- center / v
#' cp ~ add(add.sd)
#' })
#' }
#'
#' f <- one.compartment()
#'
#' modelExtract(f, cp)
#'
#' modelExtract(one.compartment, d/dt(depot))
#'
#' # from variable
#' var <- "d/dt(depot)"
#'
#' modelExtract(one.compartment, var)
#'
#' modelExtract(f, endpoint=NA, lines=TRUE, expression=TRUE)
#'
modelExtract <- function(x, ..., expression=FALSE, endpoint=FALSE, lines=FALSE, envir=parent.frame()) {
checkmate::assertLogical(expression, any.missing=FALSE, len=1)
checkmate::assertLogical(lines, any.missing=FALSE, len=1)
checkmate::assertLogical(endpoint, any.missing=TRUE, len=1)
UseMethod("modelExtract")
}
#' Common extract model lines
#'
#' @param modelLines Model lines, in this case it is the variables
#' @param rxui rxode2 parsed ui
#' @param expression Should an expression list be returned
#' @param endpoint Should this be an endpoint (yes: TRUE, no: FALSE, both: TRUE)
#' @noRd
#' @author Matthew L. Fidler
.modelExtractCommon <- function(modelLines, rxui, expression=FALSE, endpoint=FALSE, lines=FALSE) {
.lstExpr <- rxui$lstExpr
.isNull <- length(modelLines) == 0L ||
all(vapply(seq_along(modelLines),
function(i) {
is.null(modelLines[[i]])
}, logical(1)))
if (.isNull) {
.ret <- seq_along(.lstExpr)
} else {
.ret <- do.call(`c`, lapply(seq_along(modelLines),
function(i) {
.w <- .getModelLineFromExpression(modelLines[[i]],
rxui, errorLine=FALSE,
returnAllLines=TRUE)
.w <- .w[.w>0]
.w
}))
}
.ret <- sort(unique(.ret))
.endPointLines <- rxui$predDf
if (!is.null(.endPointLines)) {
.endPointLines <- .endPointLines$line
if (is.na(endpoint)) {
# do both
} else if (endpoint) {
.ret <- .ret[.ret %in% .endPointLines]
} else {
.ret <- .ret[!(.ret %in% .endPointLines)]
}
}
.lines <- .ret
.ret <- lapply(.ret,
function(i) {
.lstExpr[[i]]
})
if (!expression) {
.ret <- vapply(seq_along(.ret),
function(i) {
deparse1(.ret[[i]])
}, character(1))
}
if (lines) {
attr(.ret, "lines") <- .lines
}
.ret
}
#' Quote call info for extraction variables
#'
#' @param callInfo Call information
#' @return list of expressions
#' @noRd
#' @author Matthew L. Fidler
.quoteCallVars <- function(callInfo, ..., envir=parent.frame()) {
if (length(callInfo) == 0L) return(NULL)
.env <- new.env(parent=emptyenv())
.env$alag <- list()
.env$lag <- list()
c(lapply(seq_along(callInfo),
function(i) {
.name <- names(callInfo)[i]
.cur <- callInfo[[i]]
if (is.name(.cur)) {
.curChar <- as.character(.cur)
if (exists(.curChar, envir=envir)) {
.cur <- get(.curChar, envir=envir)
}
}
if (is.list(.cur)) {
.tmp <- eval(.cur)
.tmp <- as.vector(.cur)
.tmp <- setNames(unlist(.tmp), NULL)
.cur <- .tmp
}
if (is.call(.cur) &&
identical(.cur[[1]], quote(`$`))) {
.list <- list(...)
.tmp <- .list[[i]]
.cur <- .tmp
}
if (inherits(.cur, "character")) {
.cur <- str2lang(.cur)
}
if (is.null(.name)) {
} else if (.name %in% c("expression", "endpoint", "envir", "lines")) {
return(NULL)
}
if (is.name(.cur)) {
return(str2lang(paste0("-",deparse1(.cur))))
} else if (is.call(.cur) &&
(.matchesLangTemplate(.cur, str2lang("d/dt(.name)")) ||
.matchesLangTemplate(.cur, str2lang("f(.name)")) ||
.matchesLangTemplate(.cur, str2lang(".name(0)")) ||
.matchesLangTemplate(.cur, str2lang("rate(.name)")) ||
.matchesLangTemplate(.cur, str2lang("dur(.name)")))) {
return(str2lang(paste0("-", deparse1(.cur))))
} else if (is.call(.cur) &&
.matchesLangTemplate(.cur, str2lang("alag(.name)"))) {
.env$lag <- c(.env$lag,
list(str2lang(paste0("-", sub("alag", "lag", deparse1(.cur))))))
return(str2lang(paste0("-", deparse1(.cur))))
} else if (is.call(.cur) &&
.matchesLangTemplate(.cur, str2lang("lag(.name)"))) {
.env$alag <- c(.env$alag,
list(str2lang(paste0("-", sub("lag", "alag", deparse1(.cur))))))
return(str2lang(paste0("-", deparse1(.cur))))
}
stop("unknown variable expression: ", deparse1(.cur),
call.=FALSE)
}),
.env$alag,
.env$lag)
}
#' @export
#' @rdname modelExtract
modelExtract.function <- function(x, ..., expression=FALSE, endpoint=FALSE, lines=FALSE, envir=parent.frame()) {
.modelLines <- .quoteCallVars(match.call(expand.dots = TRUE)[-(1:2)], ..., envir=envir)
.ret <- rxode2(x)
.modelExtractCommon(.modelLines, .ret, expression=expression, endpoint=endpoint, lines=lines)
}
#' @export
#' @rdname modelExtract
modelExtract.rxUi <- function(x, ..., expression=FALSE, endpoint=FALSE, lines=FALSE, envir=parent.frame()) {
.modelLines <- .quoteCallVars(match.call(expand.dots = TRUE)[-(1:2)], ..., envir=envir)
.modelExtractCommon(.modelLines, x, expression=expression, endpoint=endpoint, lines=lines)
}
#' @export
#' @rdname modelExtract
modelExtract.rxode2 <- function(x, ..., expression=FALSE, endpoint=FALSE, lines=FALSE, envir=parent.frame()) {
.modelLines <- .quoteCallVars(match.call(expand.dots = TRUE)[-(1:2)], ..., envir=envir)
x <- as.function(x)
.ret <- rxode2(x)
.modelExtractCommon(.modelLines, .ret, expression=expression, endpoint=endpoint, lines=lines)
}
#' @export
#' @rdname modelExtract
modelExtract.rxModelVars <- modelExtract.rxode2
#' @export
#' @rdname modelExtract
modelExtract.default <- function(x, ..., expression=FALSE, endpoint=FALSE, lines=FALSE, envir=parent.frame()) {
stop("rxode2 does not know how to handle this modelExtract object",
call.=FALSE)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.