diff --git a/.drone.yml b/.drone.yml
index a5ee6e8e..c2453b6e 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -1,54 +1,156 @@
+---
kind: pipeline
type: docker
-name: mrgsolve
+name: cran-latest
+
+platform:
+ os: linux
+ arch: amd64
steps:
-- name: Pull mpn container from ECR
+- name: pull
image: omerxx/drone-ecr-auth
+ commands:
+ - $(aws ecr get-login --no-include-email --region us-east-1)
+ - docker pull 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-4.1:cran-latest
+ - docker pull 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-4.0:cran-latest
+ - docker pull 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-3.6:cran-latest
volumes:
- name: docker.sock
path: /var/run/docker.sock
+
+- name: "Check package: R 4.1"
+ pull: never
+ image: 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-4.1:cran-latest
commands:
- - $(aws ecr get-login --no-include-email --region us-east-1)
- - docker pull 906087756158.dkr.ecr.us-east-1.amazonaws.com/mpn:2020-03-24
+ - R -s -e 'devtools::install_deps(upgrade = '"'"'always'"'"', dependencies = TRUE)'
+ - make drone
-- name: R40-new
- image: "906087756158.dkr.ecr.us-east-1.amazonaws.com/mpn-complete:2020-06-08"
+- name: "Check package: R 4.0"
pull: never
- environment:
- R_LIBS_USER: "/opt/rpkgs/4.0"
- _MRGSOLVE_SKIP_MODLIB_BUILD_: false
+ image: 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-4.0:cran-latest
commands:
+ - R -s -e 'devtools::install_deps(upgrade = '"'"'always'"'"', dependencies = TRUE)'
- make drone
-- name: R36
- image: "906087756158.dkr.ecr.us-east-1.amazonaws.com/mpn:2020-03-24"
+- name: "Check package: R 3.6"
pull: never
- environment:
- R_LIBS_USER: "/opt/rpkgs/3.6/2020-03-24"
- _MRGSOLVE_SKIP_MODLIB_BUILD_: false
+ image: 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-3.6:cran-latest
commands:
+ - R -s -e 'devtools::install_deps(upgrade = '"'"'always'"'"', dependencies = TRUE)'
- make drone
-
-- name: release
- when:
- event:
- - tag
- status:
- - success
- image: "906087756158.dkr.ecr.us-east-1.amazonaws.com/mpn:2020-03-24"
+
+volumes:
+- name: docker.sock
+ host:
+ path: /var/run/docker.sock
+
+trigger:
+ event:
+ exclude:
+ - promote
+
+---
+kind: pipeline
+type: docker
+name: coverage
+
+platform:
+ os: linux
+ arch: amd64
+
+steps:
+- name: pull
+ image: omerxx/drone-ecr-auth
+ commands:
+ - $(aws ecr get-login --no-include-email --region us-east-1)
+ - docker pull 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-4.1:latest
+ volumes:
+ - name: docker.sock
+ path: /var/run/docker.sock
+
+volumes:
+- name: docker.sock
+ host:
+ path: /var/run/docker.sock
+- name: cache
+ temp: {}
+
+trigger:
+ event:
+ exclude:
+ - promote
+
+depends_on:
+- cran-latest
+
+---
+kind: pipeline
+type: docker
+name: release
+
+platform:
+ os: linux
+ arch: amd64
+
+steps:
+- name: pull
+ image: omerxx/drone-ecr-auth
+ commands:
+ - $(aws ecr get-login --no-include-email --region us-east-1)
+ - docker pull 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-4.1:latest
+ volumes:
+ - name: docker.sock
+ path: /var/run/docker.sock
+
+- name: Build package
pull: never
- environment:
- R_LIBS_USER: "/opt/rpkgs/3.6/2020-03-24"
+ image: 906087756158.dkr.ecr.us-east-1.amazonaws.com/r-dev-ci-mpn-4.1:latest
commands:
- - git config --global user.email "drone@metrumrg.com"
- - git config --global user.name "Drony"
+ - git config --global user.email drone@metrumrg.com
+ - git config --global user.name Drony
- git fetch --tags
- - R -e "pkgpub::create_tagged_repo()"
- - aws s3 sync /tmp/${DRONE_TAG} s3://mpn.metworx.dev/releases/${DRONE_REPO_NAME}/${DRONE_TAG}
- - aws s3 sync /tmp/${DRONE_TAG} s3://mpn.metworx.dev/releases/${DRONE_REPO_NAME}/latest_tag
+ - R -s -e 'pkgpub::create_tagged_repo(.dir = '"'"'/ephemeral'"'"')'
+ environment:
+ NOT_CRAN: true
+ volumes:
+ - name: cache
+ path: /ephemeral
+
+- name: "Publish package: ${DRONE_TAG}"
+ pull: if-not-exists
+ image: plugins/s3
+ settings:
+ bucket: mpn.metworx.dev
+ source: /ephemeral/${DRONE_TAG}/**/*
+ strip_prefix: /ephemeral/${DRONE_TAG}/
+ target: /releases/${DRONE_REPO_NAME}/${DRONE_TAG}
+ volumes:
+ - name: cache
+ path: /ephemeral
+
+- name: "Publish package: latest_tag"
+ pull: if-not-exists
+ image: plugins/s3
+ settings:
+ bucket: mpn.metworx.dev
+ source: /ephemeral/${DRONE_TAG}/**/*
+ strip_prefix: /ephemeral/${DRONE_TAG}/
+ target: /releases/${DRONE_REPO_NAME}/latest_tag
+ volumes:
+ - name: cache
+ path: /ephemeral
volumes:
- name: docker.sock
host:
path: /var/run/docker.sock
+- name: cache
+ temp: {}
+
+trigger:
+ event:
+ - tag
+
+depends_on:
+- cran-latest
diff --git a/DESCRIPTION b/DESCRIPTION
index 88e532d0..9c58ace5 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,7 +1,7 @@
Type: Package
Package: mrgsolve
Title: Simulate from ODE-Based Models
-Version: 1.0.0
+Version: 1.0.1
Authors@R:
c(person(given = "Kyle T", family = "Baron",
role = c("aut", "cre"),
@@ -38,12 +38,12 @@ Depends:
R (>= 3.1.2),
methods
Imports:
- Rcpp (>= 0.12.12),
- dplyr (>= 0.8.1),
- magrittr (>= 1.5),
- tibble (>= 2.1.1),
- rlang (>= 0.3.4),
- tidyselect (>= 0.2.5),
+ Rcpp (>= 1.0.7),
+ dplyr (>= 1.0.8),
+ magrittr (>= 2.0.1),
+ tibble (>= 3.1.6),
+ rlang (>= 1.0.1),
+ tidyselect (>= 1.1.1),
lifecycle,
glue
Suggests:
@@ -56,9 +56,9 @@ Suggests:
data.table,
pmxTools
LinkingTo:
- Rcpp (>= 0.12.12),
- RcppArmadillo (>= 0.7.900.2.0),
- BH (>= 1.62.0-1)
+ Rcpp (>= 1.0.7),
+ RcppArmadillo (>= 0.10.7.3.0),
+ BH (>= 1.75.0-0)
RdMacros:
lifecycle
Encoding: UTF-8
@@ -83,6 +83,7 @@ Collate:
'annot.R'
'chain.R'
'class_build.R'
+ 'class_evd.R'
'events.R'
'class_rx.R'
'compile.R'
diff --git a/Makefile b/Makefile
index 66aaac64..584e5d28 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ check:
check-only:
make doc
- R CMD check ${TARBALL} --no-manual --no-test
+ R CMD check ${TARBALL} --no-manual --no-tests --no-install
cran: export _MRGSOLVE_SKIP_MODLIB_BUILD_=no
cran:
@@ -128,9 +128,10 @@ modlib: export _MRGSOLVE_SKIP_MODLIB_BUILD_=no
modlib:
Rscript -e 'testthat::test_file("inst/maintenance/unit/test-modlib.R")'
-# possibly no longer in use
+# this is in use
drone:
make house
+ #R -s -e 'devtools::install_deps(upgrade = '"'"'always'"'"', dependencies=TRUE)'
R CMD build --md5 $(PKGDIR)
R CMD check --as-cran ${TARBALL}
export _MRGSOLVE_SKIP_MODLIB_BUILD_=false
diff --git a/NAMESPACE b/NAMESPACE
index 6c3883eb..dcc39a29 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -67,6 +67,7 @@ export("%then%")
export(Req)
export(allparam)
export(as.ev)
+export(as.evd)
export(as.tbl.mrgsims)
export(as_bmat)
export(as_cmat)
@@ -103,6 +104,7 @@ export(ev_rep)
export(ev_repeat)
export(ev_rx)
export(ev_seq)
+export(evd)
export(expand.ev)
export(expand.idata)
export(expand_observations)
@@ -161,6 +163,7 @@ export(stime)
export(tgrid)
export(touch_funs)
export(tscale)
+export(uctran)
export(valid_data_set)
export(valid_data_set.matrix)
export(valid_idata_set)
@@ -192,6 +195,7 @@ exportMethods(data_set)
exportMethods(dim)
exportMethods(ev)
exportMethods(ev_rx)
+exportMethods(evd)
exportMethods(head)
exportMethods(idata_set)
exportMethods(init)
diff --git a/NEWS.md b/NEWS.md
index eb42a3f1..a59ae716 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,19 @@
+# mrgsolve 1.0.1
+
+- Add `LOG()`, `EXP()`, `SQRT()` macros when `nm-vars` plugin is invoked
+ #931, #936.
+
+- Use `evd()` to create an event object which renders nmtran names
+ in upper case (e.g. `TIME` rather than `time`) #935, #919.
+
+- Fixed bug where `rate` was not getting set for modeled events #934.
+
+- Fixed bug where `self.stop_id()` and `self.stop_id_cf()` had reversed
+ behavior as documented #927, #928.
+
+- Refactored EVID=3 behavior to leave `NEWIND` as-is #934.
+
+
# mrgsolve 1.0.0
- New model syntax: `THETA(n)` is interpreted as `THETAn` in the model code;
diff --git a/R/chain.R b/R/chain.R
index b60a3f36..feb9d029 100644
--- a/R/chain.R
+++ b/R/chain.R
@@ -195,7 +195,7 @@ obsaug <- function(x,value=TRUE,...) {
##' idata_set(idata) %>%
##' design(list(des1, des2),"amt") %>%
##' data_set(data) %>%
-##' mrgsim %>%
+##' mrgsim() %>%
##' plot(RESP~time|GRP)
##'
##' @export
diff --git a/R/class_ev.R b/R/class_ev.R
index c36c0a25..00e3a0a5 100644
--- a/R/class_ev.R
+++ b/R/class_ev.R
@@ -1,4 +1,4 @@
-# Copyright (C) 2013 - 2019 Metrum Research Group
+# Copyright (C) 2013 - 2022 Metrum Research Group
#
# This file is part of mrgsolve.
#
@@ -15,14 +15,79 @@
# You should have received a copy of the GNU General Public License
# along with mrgsolve. If not, see .
+
+# A series of functions for dealing with ev/data.frame -------
+
+#' Just get the data.frame part
+#'
+#' This is an internal function.
+#'
+#' If an event object, return `data` slot; otherwise, call as.data.frame. This
+#' is supposed to be optimized for handling event objects.
+#'
+#' @param x An R object.
+#'
+#' @noRd
+to_data_frame <- function(x) {
+ # Just return the data frame
+ if(is.ev(x)) {
+ x@data
+ } else {
+ as.data.frame(x)
+ }
+}
+
+#' Convert an event object to data set
+#'
+#' Call this function when `x` is already known to be an event object.
+#'
+#' @param x An event object.
+#' @param id The subject ID.
+#'
+#' @noRd
+ev_to_ds <- function(x, id = 1) {
+ # Specifically for simulating a (known) ev object
+ ans <- x@data
+ if(nrow(ans)==0) return(ans)
+ if(match("ID", names(ans), 0)==0) ans$ID <- id
+ recase_ev(ans, x@case)
+}
+
+#' Create a data set from and event object or data frame
+#'
+#' This is more general applicability.
+#'
+#' @noRd
+As_data_set <- function(x, id = 1) {
+ # Possibly handle data.frame or
+ if(is.ev(x)) return(ev_to_ds(x, id = id))
+ ans <- as.data.frame(x)
+ if(nrow(ans)==0) return(ans)
+ if(match("ID", names(ans), 0) ==0) ans$ID <- id
+ ans
+}
+
+ev_proto <- list(data = data.frame(), case = 0L)
+ev_slots <- c(data = "data.frame", case = "integer")
+ev_initialize <- function(.Object, case = 0L, ...) {
+ .Object <- callNextMethod()
+ if(!case %in% c(0, 1)) {
+ stop("Event object case must be either 0 or 1.")
+ }
+ .Object@case <- case
+ .Object
+}
+
##' S4 events class
##' @slot data a data frame of events
+##' @slot case indicates how to handle column naming upon coerce to data.frame
##' @export
##' @keywords internal
-setClass("ev", slots=c(data="data.frame"))
+setClass("ev", prototype = ev_proto, slots = ev_slots)
+setMethod("initialize", "ev", ev_initialize)
is.ev <- function(x) {
- inherits(x,"ev")
+ inherits(x, "ev")
}
##' dplyr verbs for event objects
@@ -62,14 +127,14 @@ mutate.ev <- function(.data, ...) {
##' @rdname ev_dplyr
##' @export
select.ev <- function(.data, ...) {
- .data@data <- as.data.frame(dplyr::select(.data@data,...))
+ .data@data <- as.data.frame(dplyr::select(.data@data, ...))
.data
}
##' @rdname ev_dplyr
##' @export
filter.ev <- function(.data, ...) {
- .data@data <- as.data.frame(dplyr::filter(.data@data,...))
+ .data@data <- as.data.frame(dplyr::filter(.data@data, ...))
.data
}
@@ -125,19 +190,19 @@ as.matrix.ev <- function(x,...) {
##' @export
as.data.frame.ev <- function(x, row.names = NULL, optional = FALSE,
add_ID = NULL, ...) {
- ans <- x@data
- if(is.numeric(add_ID) & !has_ID(ans) & nrow(ans) > 0) {
- ans[["ID"]] <- add_ID[1]
- }
- return(ans)
+ ev_to_ds(x, id = add_ID)
}
-##' @rdname ev_methods
-##' @export
-##' @keywords internal
+#' @rdname ev_methods
+#' @export
+#' @keywords internal
setMethod("show", "ev", function(object) {
- cat("Events:\n")
- print(as.data.frame(object))
+ header <- "Events:\n"
+ if(object@case==1) {
+ header <- "Events Data:\n"
+ }
+ cat(header)
+ print(object@data)
return(invisible(NULL))
})
@@ -160,19 +225,6 @@ setMethod("[[", "ev", function(x, i, exact=TRUE) {
x@data[[i]]
})
-As_data_set <- function(x) {
- if(!is.data.frame(x)) {
- if(is.ev(x)) {
- x <- x@data
- } else {
- x <- as.data.frame(x)
- }
- }
- if(nrow(x)==0) return(x)
- if(!has_ID(x)) x[["ID"]] <- 1
- return(x)
-}
-
finalize_ev_data <- function(data) {
if("tinf" %in% names(data)) {
tinf <- data[["tinf"]]
diff --git a/R/class_evd.R b/R/class_evd.R
new file mode 100644
index 00000000..f9c0165a
--- /dev/null
+++ b/R/class_evd.R
@@ -0,0 +1,77 @@
+# Note: `evd` isn't really a separate class; it could be implemented that
+# way, but for not it's just an `ev()` object with a specific `case`
+# attribute.
+
+
+#' Create an event object with data-like names
+#'
+#' This function calls [ev()] to create an event object and then sets the
+#' case attribute so that it renders nmtran data names in upper case. An
+#' object created with [evd()] can be used in the same way as an object
+#' created with [ev()].
+#'
+#' Note that `evd` isn't a separate class; it is just an `ev` object with
+#' a specific `case` attribute. See examples which illustrate the difference.
+#'
+#' @param x An mrgmod object.
+#' @param ... Arguments passed to [ev()].
+#'
+#' @examples
+#' a <- evd(amt = 100)
+#' b <- ev(amt = 300)
+#' a
+#' as.data.frame(a)
+#' as_data_set(a, b)
+#' as_data_set(b, a)
+#' as.data.frame(seq(a, b))
+#'
+#' @seealso [ev()], [lctran()], [uctran()]
+#'
+#' @md
+#' @export
+setGeneric("evd", function(x, ...) standardGeneric("evd"))
+
+#' @rdname evd
+#' @export
+setMethod("evd", "mrgmod", function(x, ...) {
+ x <- ev(x, ...)
+ x@args[["events"]] <- set_ev_case(x@args[["events"]], 1L)
+ x
+})
+
+#' @rdname evd
+#' @export
+setMethod("evd", "missing", function(...) {
+ set_ev_case(ev(...), 1L)
+})
+
+#' @rdname evd
+#' @export
+setMethod("evd", "ev", function(x, ...) {
+ set_ev_case(x, 1L)
+})
+
+
+#' @param x An event object.
+#' @rdname evd
+#' @export
+as.evd <- function(x) {
+ if(!is.ev(x)) stop("evnt must be an ev object.")
+ x@case <- 1L
+ x
+}
+
+set_ev_case <- function(x, case) {
+ if(!is.ev(x)) return(x)
+ x@case <- case
+ x
+}
+
+# This actually changes names of the result
+# For now, 0 = as-is; 1 = all uppercase
+recase_ev <- function(data, case = 0) {
+ if(case==0) return(data)
+ convert <- names(data) %in% GLOBALS$TRAN_LOWER
+ names(data)[convert] <- toupper(names(data)[convert])
+ data
+}
diff --git a/R/data_set.R b/R/data_set.R
index 202cad8c..46e7dc3c 100644
--- a/R/data_set.R
+++ b/R/data_set.R
@@ -103,13 +103,12 @@ setGeneric("data_set", function(x,data,...) {
standardGeneric("data_set")
})
-
##' @rdname data_set
##' @export
setMethod("data_set",c("mrgmod", "data.frame"), function(x,data,.subset=TRUE,.select=TRUE,object=NULL,need=NULL,...) {
if(is.character(need)) {
- suppressMessages(inventory(x,data,need))
+ suppressMessages(inventory(x, data, need))
}
if(!missing(.subset)) {
data <- dplyr::filter(data,`!!`(enquo(.subset)))
@@ -118,10 +117,10 @@ setMethod("data_set",c("mrgmod", "data.frame"), function(x,data,.subset=TRUE,.se
data <- dplyr::select(data,`!!!`(.select))
}
if(nrow(data) ==0) {
- stop("Zero rows in data after filtering.", call.=FALSE)
+ stop("Zero rows in data after filtering.", call. = FALSE)
}
if(is.character(object)) {
- data <- data_hooks(data,object,x@envir,param(x),...)
+ data <- data_hooks(data, object, x@envir, param(x), ...)
}
x@args[["data"]] <- data
return(x)
@@ -129,125 +128,181 @@ setMethod("data_set",c("mrgmod", "data.frame"), function(x,data,.subset=TRUE,.se
##' @rdname data_set
##' @export
-setMethod("data_set",c("mrgmod", "ANY"), function(x,data,...) {
- return(data_set(x,as.data.frame(data),...))
+setMethod("data_set",c("mrgmod", "ANY"), function(x, data, ...) {
+ return(data_set(x, as.data.frame(data), ...))
})
##' @rdname data_set
##' @export
-setMethod("data_set", c("mrgmod", "ev"), function(x,data,...) {
- return(data_set(x,As_data_set(data),...))
+setMethod("data_set", c("mrgmod", "ev"), function(x, data, ...) {
+ return(data_set(x, As_data_set(data), ...))
})
##' @rdname data_set
##' @export
-setMethod("data_set", c("mrgmod", "missing"), function(x, object,...) {
+setMethod("data_set", c("mrgmod", "missing"), function(x, object, ...) {
object <- data_hooks(object=object,envir=x@envir,param=param(x),...)
- return(data_set(x,as.data.frame(object),...))
+ return(data_set(x, as.data.frame(object) ,...))
})
-##' Convert select upper case column names to lower case
-##'
-##' Previous data set requirements included lower case names for data items
-##' like `AMT` and `EVID`. Lower case is no longer required.
-##'
-##' @param data an nmtran-like data frame
-##'
-##' @return A data.frame with renamed columns
-##'
-##' @details
-##' Columns that will be renamed with lower case versions: `AMT`,
-##' `II`, `SS`, `CMT`, `ADDL`, `RATE`, `EVID`,
-##' `TIME`. If a lower case version of these names exist in the data
-##' set, the column will not be renamed.
-##'
-##' @examples
-##' data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2)
-##' lctran(data)
-##'
-##' @return
-##' The input data set, with select columns made lower case.
-##'
-##' @md
-##' @export
-lctran <- function(data) {
+#' Change the case of nmtran-like data items
+#'
+#' Previous data set requirements included lower case names for data items
+#' like `AMT` and `EVID`. Lower case is no longer required. However, it is still
+#' a requirement that nmtran like data column names are either all lower case
+#' or all upper case.
+#'
+#' Columns that will be renamed with lower or upper case versions:
+#'
+#' - `AMT / amt`
+#' - `II / ii`
+#' - `SS / ss`
+#' - `CMT / cmt`
+#' - `ADDL / addl`
+#' - `RATE / rate`
+#' - `EVID / evid`
+#' - `TIME / time`
+#'
+#' If both lower and upper case versions of the name are present in the data
+#' frame, no changes will be made.
+#'
+#' @param data A data set with nmtran-like format.
+#' @param warn If `TRUE`, a warning will be issued when there are both upper
+#' and lower case versions of any nmtran-like column in the data frame.
+#'
+#' @return
+#' A data frame with possibly renamed columns.
+#'
+#' @examples
+#' data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2, WT = 80)
+#' lctran(data)
+#'
+#' data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2, wt = 80)
+#' uctran(data)
+#'
+#' # warning
+#' data <- data.frame(TIME = 1, time = 2, CMT = 5)
+#' lctran(data)
+#'
+#' @return
+#' The input data set, with select columns made lower case.
+#'
+#' @md
+#' @export
+lctran <- function(data, warn = TRUE) {
+ if(!is.data.frame(data)) {
+ stop("`data` must be a data.frame.")
+ }
n <- names(data)
- infrom <- is.element(n,tran_upper)
- haslower <- is.element(tolower(n),n)
+ infrom <- n %in% GLOBALS$TRAN_UPPER
+ haslower <- tolower(n) %in% n
change <- infrom & !haslower
- if(sum(change) > 0) names(data)[change] <- tolower(n[change])
+ if(any(change)) names(data)[change] <- tolower(n[change])
+ if(isTRUE(warn) && any(dup <- infrom & haslower)) {
+ warning(
+ "There are both upper and lower case versions ",
+ "of some nmtran names in the data set"
+ )
+ }
data
}
+#' @rdname lctran
+#' @export
+uctran <- function(data, warn = TRUE) {
+ if(!is.data.frame(data)) {
+ stop("`data` must be a data.frame.")
+ }
+ n <- names(data)
+ infrom <- n %in% GLOBALS$TRAN_LOWER
+ hasupper <- toupper(n) %in% n
+ change <- infrom & !hasupper
+ if(any(change)) names(data)[change] <- toupper(n[change])
+ if(isTRUE(warn) && any(dup <- infrom & hasupper)) {
+ warning(
+ "There are both upper and lower case versions ",
+ "of some nmtran names in the data set."
+ )
+ }
+ data
+}
-data_hooks <- function(data,object,envir,param=list(),...) {
+data_hooks <- function(data, object, envir, param = list(), ...) {
param <- as.list(param)
- envir <- combine_list(as.list(param),as.list(envir))
+ envir <- combine_list(as.list(param), as.list(envir))
objects <- cvec_cs(object)
args <- list(...)
if(missing(data)) {
- data <- eval(tparse(objects[1]),envir=envir)
+ data <- eval(tparse(objects[1]), envir = envir)
if(is.function(data)) {
- data <- do.call(data,args,envir=as.environment(envir))
+ data <- do.call(data, args, envir = as.environment(envir))
}
objects <- objects[-1]
}
for(f in objects) {
args$data <- data
- data <- do.call(f,args,envir=as.environment(envir))
+ data <- do.call(f, args, envir = as.environment(envir))
}
return(data)
}
-
-##' Create a simulation data set from ev objects
-##'
-##' The goal is to take a series of event objects and combine them
-##' into a single data set that can be passed to [data_set()].
-##'
-##' @param x ev objects
-##' @param ... more ev objects
-##'
-##' @details
-##' Each event object is added to the data frame as an `ID`
-##' or set of `ID`s that are distinct from the `ID`s
-##' in the other event objects. Note that including `ID`
-##' argument to the [ev()] call where `length(ID)`
-##' is greater than one will render that set of
-##' events for all of `ID`s that are requested.
-##'
-##' To get a data frame with one row (event) per `ID`, look at [expand.ev()].
-##'
-##' @return
-##' A data frame suitable for passing into [data_set()].
-##'
-##' @examples
-##'
-##' as_data_set(ev(amt = c(100,200), cmt=1, ID = seq(3)),
-##' ev(amt = 300, time = 24, ID = seq(2)),
-##' ev(amt = 1000, ii = 8, addl = 10, ID = seq(3)))
-##'
-##' # Instead of this, use expand.ev
-##' as_data_set(ev(amt = 100), ev(amt = 200), ev(amt = 300))
-##'
-##' @md
-##' @rdname as_data_set
-##' @export
+#' Create a simulation data set from ev objects
+#'
+#' The goal is to take a series of event objects and combine them
+#' into a single data set that can be passed to [data_set()].
+#'
+#' @param x ev objects
+#' @param ... more ev objects
+#'
+#' @details
+#' Each event object is added to the data frame as an `ID` or set of `ID`s
+#' that are distinct from the `ID`s in the other event objects. Note that
+#' including `ID` argument to the [ev()] call where `length(ID)` is greater
+#' than one will render that set of events for all of `ID`s that are requested.
+#'
+#' When determining the case for output names, the `case` attribute for
+#' the first `ev` object passed will be used to set the case for the output
+#' data.frame.
+#'
+#' To get a data frame with one row (event) per `ID`, look at [expand.ev()].
+#'
+#' @return
+#' A data frame suitable for passing into [data_set()].
+#'
+#' @examples
+#' a <- ev(amt = c(100,200), cmt=1, ID = seq(3))
+#' b <- ev(amt = 300, time = 24, ID = seq(2))
+#' c <- ev(amt = 1000, ii = 8, addl = 10, ID = seq(3))
+#'
+#' as_data_set(a, b, c)
+#'
+#' d <- evd(amt = 500)
+#'
+#' as_data_set(d, a)
+#'
+#' # Instead of this, use expand.ev
+#' as_data_set(ev(amt = 100), ev(amt = 200), ev(amt = 300))
+#'
+#' @seealso [expand.ev()], [ev()]
+#'
+#' @md
+#' @rdname as_data_set
+#' @export
setGeneric("as_data_set", function(x,...) standardGeneric("as_data_set"))
-##' @rdname as_data_set
-setMethod("as_data_set","ev", function(x,...) {
+#' @rdname as_data_set
+setMethod("as_data_set", "ev", function(x, ...) {
other_ev <- list(...)
if(length(other_ev)==0) {
return(check_ev(x))
}
- do.call(collect_ev,c(list(x),other_ev))
+ do.call(collect_ev, c(list(x), other_ev))
})
-##' @rdname as_data_set
-setMethod("as_data_set","data.frame", function(x,...) {
- as_data_set(as.ev(x),...)
+#' @rdname as_data_set
+setMethod("as_data_set","data.frame", function(x, ...) {
+ as_data_set(as.ev(x) ,...)
})
##' Replicate a list of events into a data set
@@ -283,7 +338,7 @@ setMethod("as_data_set","data.frame", function(x,...) {
##'
##'
##' @export
-ev_assign <- function(l,idata,evgroup,join=FALSE) {
+ev_assign <- function(l, idata, evgroup, join = FALSE) {
idata <- as.data.frame(idata)
@@ -314,12 +369,12 @@ ev_assign <- function(l,idata,evgroup,join=FALSE) {
}
l <- lapply(l,function(x) {
- x[,colnames(l[[1]]),drop=FALSE]
+ x[,colnames(l[[1]]), drop=FALSE]
})
evgroup <- idata[,evgroup]
uevgroup <- sort(unique(evgroup))
- evgroup <- match(evgroup,uevgroup)
+ evgroup <- match(evgroup, uevgroup)
if(length(l) != length(uevgroup)) {
stop("For this idata set, please provide exactly ",
@@ -328,20 +383,18 @@ ev_assign <- function(l,idata,evgroup,join=FALSE) {
}
x <- do.call(rbind,l[evgroup])
- dimnames(x) <- list(NULL,colnames(x))
+ dimnames(x) <- list(NULL, colnames(x))
x <- as.data.frame(x)
n <- (sapply(l,nrow))[evgroup]
- ID <- rep(idata[["ID"]],times=n)
+ ID <- rep(idata[["ID"]], times = n)
x[["ID"]] <- ID
if(join) {
- nu <- sapply(idata,is.numeric)
- x <- dplyr::left_join(x,idata[,nu,drop=FALSE],by="ID")
+ nu <- sapply(idata, is.numeric)
+ x <- left_join(x,idata[,nu,drop=FALSE],by="ID")
}
-
return(x)
-
}
##' @param ... used to pass arguments from \code{assign_ev}
diff --git a/R/events.R b/R/events.R
index 14f1996b..2387eefb 100644
--- a/R/events.R
+++ b/R/events.R
@@ -1,4 +1,4 @@
-# Copyright (C) 2013 - 2021 Metrum Research Group
+# Copyright (C) 2013 - 2022 Metrum Research Group
#
# This file is part of mrgsolve.
#
@@ -16,94 +16,94 @@
# along with mrgsolve. If not, see .
-##' Event objects for simulating PK and other interventions
-##'
-##' An event object specifies dosing or other interventions that get implemented
-##' during simulation. Event objects do similar things as \code{\link{data_set}},
-##' but simpler and quicker.
-##'
-##' @param x a model object
-##' @param time event time
-##' @param amt dose amount
-##' @param evid event ID
-##' @param cmt compartment
-##' @param ID subject ID
-##' @param replicate logical; if \code{TRUE}, events will be replicated for
-##' each individual in \code{ID}
-##' @param until the expected maximum \bold{observation} time for this regimen
-##' @param tinf infusion time; if greater than zero, then the \code{rate} item
-##' will be derived as \code{amt/tinf}
-##' @param realize_addl if \code{FALSE} (default), no change to \code{addl}
-##' doses. If \code{TRUE}, \code{addl} doses are made explicit with
-##' \code{\link{realize_addl}}
-##' @param object passed to show
-##' @param ... other items to be incorporated into the event object; see
-##' details
-##'
-##' @details
-##' \itemize{
-##' \item Required items in events objects include
-##' \code{time}, \code{amt}, \code{evid} and \code{cmt}.
-##' \item If not supplied, \code{evid} is assumed to be 1.
-##' \item If not supplied, \code{cmt} is assumed to be 1.
-##' \item If not supplied, \code{time} is assumed to be 0.
-##' \item If \code{amt} is not supplied, an error will be generated.
-##' \item If \code{total} is supplied, then \code{addl} will be set
-##' to \code{total} - 1.
-##' \item Other items can include \code{ii}, \code{ss}, and \code{addl}
-##' (see \code{\link{data_set}} for details on all of these items).
-##' \item \code{ID} may be specified as a vector.
-##' \item If replicate is \code{TRUE} (default), then the events
-##' regimen is replicated for each \code{ID}; otherwise, the number of
-##' event rows must match the number of \code{ID}s entered
-##' }
-##' @return events object
-##'
-##' @seealso \code{\link{ev_rep}}, \code{\link{ev_days}},
-##' \code{\link{ev_repeat}}, \code{\link{ev_assign}},
-##' \code{\link{ev_seq}}, \code{\link{mutate.ev}},
-##' \code{\link{as.ev}}, \code{\link{ev_methods}}
-##'
-##' @examples
-##' mod <- mrgsolve::house()
-##'
-##' mod <- mod %>% ev(amt = 1000, time = 0, cmt = 1)
-##'
-##' loading <- ev(time = 0, cmt = 1, amt = 1000)
-##'
-##' maint <- ev(time = 12, cmt = 1, amt = 500, ii = 12, addl = 10)
-##'
-##' c(loading, maint)
-##'
-##' reduced_load <- dplyr::mutate(loading, amt = 750)
-##'
-##' @export
-setGeneric("ev", function(x,...) {
+#' Event objects for simulating PK and other interventions
+#'
+#' An event object specifies dosing or other interventions that get implemented
+#' during simulation. Event objects do similar things as \code{\link{data_set}},
+#' but simpler and quicker.
+#'
+#' @param x a model object
+#' @param time event time
+#' @param amt dose amount
+#' @param evid event ID
+#' @param cmt compartment
+#' @param ID subject ID
+#' @param replicate logical; if \code{TRUE}, events will be replicated for
+#' each individual in \code{ID}
+#' @param until the expected maximum \bold{observation} time for this regimen
+#' @param tinf infusion time; if greater than zero, then the \code{rate} item
+#' will be derived as \code{amt/tinf}
+#' @param realize_addl if \code{FALSE} (default), no change to \code{addl}
+#' doses. If \code{TRUE}, \code{addl} doses are made explicit with
+#' \code{\link{realize_addl}}
+#' @param object passed to show
+#' @param ... other items to be incorporated into the event object; see
+#' details
+#'
+#' @details
+#' \itemize{
+#' \item Required items in events objects include
+#' \code{time}, \code{amt}, \code{evid} and \code{cmt}.
+#' \item If not supplied, \code{evid} is assumed to be 1.
+#' \item If not supplied, \code{cmt} is assumed to be 1.
+#' \item If not supplied, \code{time} is assumed to be 0.
+#' \item If \code{amt} is not supplied, an error will be generated.
+#' \item If \code{total} is supplied, then \code{addl} will be set
+#' to \code{total} - 1.
+#' \item Other items can include \code{ii}, \code{ss}, and \code{addl}
+#' (see \code{\link{data_set}} for details on all of these items).
+#' \item \code{ID} may be specified as a vector.
+#' \item If replicate is \code{TRUE} (default), then the events
+#' regimen is replicated for each \code{ID}; otherwise, the number of
+#' event rows must match the number of \code{ID}s entered
+#' }
+#' @return events object
+#'
+#' @seealso \code{\link{evd}}, \code{\link{ev_rep}}, \code{\link{ev_days}},
+#' \code{\link{ev_repeat}}, \code{\link{ev_assign}},
+#' \code{\link{ev_seq}}, \code{\link{mutate.ev}},
+#' \code{\link{as.ev}}, \code{\link{ev_methods}}
+#'
+#' @examples
+#' mod <- mrgsolve::house()
+#'
+#' mod <- mod %>% ev(amt = 1000, time = 0, cmt = 1)
+#'
+#' loading <- ev(time = 0, cmt = 1, amt = 1000)
+#'
+#' maint <- ev(time = 12, cmt = 1, amt = 500, ii = 12, addl = 10)
+#'
+#' c(loading, maint)
+#'
+#' reduced_load <- dplyr::mutate(loading, amt = 750)
+#'
+#' @export
+setGeneric("ev", function(x, ...) {
standardGeneric("ev")
})
-##' @rdname ev
-##' @export
-setMethod("ev", "mrgmod", function(x,object=NULL,...) {
+#' @rdname ev
+#' @export
+setMethod("ev", "mrgmod", function(x, object = NULL, ...) {
if(is.null(object)) {
x@args[["events"]] <- ev(...)
return(x)
}
if(is.character(object)) {
- object <- eval(parse(text = object),envir = x@envir)
+ object <- eval(parse(text = object), envir = x@envir)
}
x@args[["events"]] <- object
x
})
-##' @rdname ev
-##' @export
+#' @rdname ev
+#' @export
setMethod("ev", "missing", function(time=0, amt=0, evid=1, cmt=1, ID=numeric(0),
replicate=TRUE, until=NULL, tinf=NULL,
realize_addl=FALSE, ...) {
if(length(match.call())==1) {
- return(new("ev", data=data.frame()[0,]))
+ return(new("ev", data = data.frame()[0,]))
}
if(any(evid==0)) {
@@ -114,7 +114,7 @@ setMethod("ev", "missing", function(time=0, amt=0, evid=1, cmt=1, ID=numeric(0),
wstop("argument \"amt\" is missing")
}
- l <- list(time=time, cmt=cmt, amt=amt, evid=evid)
+ l <- list(time = time, cmt = cmt, amt = amt, evid = evid)
if(is.numeric(tinf) && length(tinf) > 0) l[["tinf"]] <- tinf
if(is.numeric(until) && length(until) > 0) l[["until"]] <- until
@@ -129,7 +129,7 @@ setMethod("ev", "missing", function(time=0, amt=0, evid=1, cmt=1, ID=numeric(0),
names(l) <- c(na1,na2)
}
- data <- as.data.frame(as_tibble(l))
+ data <- as.data.frame(as_tibble(l), stringsAsFactors = FALSE)
if(all(c("rate", "tinf") %in% names(data))) {
wstop("input can include either rate or tinf, not both")
@@ -160,10 +160,6 @@ setMethod("ev", "missing", function(time=0, amt=0, evid=1, cmt=1, ID=numeric(0),
data <- arrange__(data,.dots=c("ID", "time"))
rownames(data) <- NULL
} else {
- # data <- data.frame(.Call(`_mrgsolve_EXPAND_EVENTS`,
- # match("ID", colnames(data),0),
- # data.matrix(data),
- # ID, PACKAGE="mrgsolve" ))
data <- expand_event_object(data,ID)
}
@@ -177,50 +173,51 @@ setMethod("ev", "missing", function(time=0, amt=0, evid=1, cmt=1, ID=numeric(0),
}
if(realize_addl) data <- realize_addl(data)
- return(new("ev", data=data))
+ return(new("ev", data = data))
})
-##' @rdname ev
-##' @export
-setMethod("ev", "ev", function(x, realize_addl=FALSE,...) {
+#' @rdname ev
+#' @export
+setMethod("ev", "ev", function(x, realize_addl = FALSE, ...) {
+ x <- set_ev_case(x, 0L)
if(realize_addl) {
return(realize_addl(x))
- } else {
- return(x)
- }
+ }
+ x
})
-
-##' Coerce an object to class ev
-##'
-##' @param x an object to coerce
-##' @param keep_id if \code{TRUE}, \code{ID} column is retained if it exists
-##' @param clean if \code{TRUE}, only dosing or ID information is retained in
-##' the result
-##' @param ... not used
-##'
-##' @examples
-##' data <- data.frame(amt = 100)
-##'
-##' as.ev(data)
-##'
-##' @export
-setGeneric("as.ev", function(x,...) {
+#' Coerce an object to class ev
+#'
+#' @param x An object to coerce.
+#' @param keep_id If `TRUE`, `ID` column is retained if it exists.
+#' @param clean If `TRUE`, only dosing or ID information is retained in
+#' the result.
+#' @param ... Not used.
+#'
+#' @examples
+#' data <- data.frame(amt = 100)
+#'
+#' as.ev(data)
+#'
+#' @return
+#' An object with class ev.
+#'
+#' @md
+#' @export
+setGeneric("as.ev", function(x, ...) {
standardGeneric("as.ev")
})
-##' @rdname as.ev
-##' @export
-setMethod("as.ev", "data.frame", function(x,keep_id=TRUE,clean = FALSE,...) {
+df_to_ev <- function(x, keep_id = TRUE, clean = FALSE, ...) {
- if(nrow(x)==0) {
- return(new("ev",data=data.frame()))
+ if(nrow(x) == 0) {
+ return(new("ev", data = data.frame()))
}
- x <- as.data.frame(x)
+ x <- as.data.frame(x, stringsAsFactors = FALSE)
convert <- c("TIME", GLOBALS[["CARRY_TRAN_UC"]])
- upper <- intersect(convert,names(x))
+ upper <- intersect(convert, names(x))
if(length(upper) > 0) {
where <- match(upper, names(x))
@@ -241,7 +238,7 @@ setMethod("as.ev", "data.frame", function(x,keep_id=TRUE,clean = FALSE,...) {
x[["evid"]] <- na2zero(x[["evid"]])
x <- x[x[["evid"]] != 0,]
if(nrow(x)==0) {
- wstop("no dosing events found; could not coerce to ev object")
+ wstop("no dosing events found; could not coerce to ev object.")
}
}
@@ -250,137 +247,155 @@ setMethod("as.ev", "data.frame", function(x,keep_id=TRUE,clean = FALSE,...) {
if(clean) {
keep <- c("ID", GLOBALS[["CARRY_TRAN_LC"]])
keep <- intersect(keep, names(x))
- x <- x[,keep]
+ x <- x[,keep, drop = FALSE]
}
x <- finalize_ev(x)
- new("ev", data=x)
-})
+ new("ev", data = x)
+}
-##' @rdname as.ev
-##' @export
-setMethod("as.ev", "ev", function(x,...) {
+#' @rdname as.ev
+#' @export
+setMethod("as.ev", "data.frame", df_to_ev)
+
+#' @rdname as.ev
+#' @export
+setMethod("as.ev", "ev", function(x, ...) {
x
})
check_ev <- function(x) {
- x <- as.data.frame(x)
- if(!has_name("ID", x)) x[["ID"]] <- 1
+ if(!inherits(x, c("ev", "data.frame"))) {
+ stop("All items must have class ev or data.frame.")
+ }
+ x <- to_data_frame(x)
+ if(!"ID" %in% names(x)) x[["ID"]] <- 1
return(x)
}
collect_ev <- function(...) {
x <- list(...)
- tran <- c("ID","time", "cmt", "evid",
- "amt", "ii", "addl", "rate", "ss")
- x <- lapply(x,check_ev)
- y <- lapply(x, "[[","ID")
- mx <- sapply(y,function(xx) length(unique(xx)))
- mx <- cumsum(c(0,mx[-length(mx)]))
- y <- mapply(y,mx, FUN=function(yi,mxi) return(yi+mxi), SIMPLIFY=FALSE)
+ tran <- c("ID","time", "cmt", "evid", "amt", "ii", "addl", "rate", "ss")
+ is_evnt <- vapply(x, is.ev, TRUE)
+ if(any(is_evnt)) {
+ w <- which(is_evnt)[1]
+ case <- x[[w]]@case
+ } else {
+ case <- 0
+ }
+ x <- lapply(x, check_ev)
+ ids <- lapply(x, "[[", "ID")
+ nid <- sapply(ids, function(tid) length(unique(tid)))
+ idn <- cumsum(c(0, nid[-length(nid)]))
+ new_ids <- Map(f = `+`, ids, idn)
x <- bind_rows(x)
- x[["ID"]] <- unlist(y,use.names=FALSE)
- tran <- intersect(tran,names(x))
+ x[["ID"]] <- unlist(new_ids, use.names = FALSE)
+ tran <- intersect(tran, names(x))
what <- names(x) %in% tran
-
- x <- mutate_at(x,which(what),list(~na2zero(.)))
-
+ for(col in which(what)) {
+ x[[col]] <- na2zero(x[[col]])
+ }
na.check <- which(!what)
-
if(length(na.check) > 0) {
- if(any(is.na(unlist(x[,na.check])))) {
+ if(anyNA(x[, na.check])) {
warning("missing values in some columns",call.=FALSE)
}
}
- x <- dplyr::select(x,c(match(tran,names(x)),seq_along(names(x))))
-
- if(!any(c("time", "TIME") %in% names(x))) {
+ take <- unique(c(match(tran,names(x)),seq_along(names(x))))
+ x <- x[, take, drop = FALSE]
+ if(is.na(timename(x))) {
wstop("no time or TIME column in the data set")
}
-
- if(!any(c("cmt", "CMT") %in% names(x))) {
+ if(is.na(cmtname(x))) {
wstop("no cmt or CMT column in the data set")
}
-
- if(!has_ID(x)) {
+ if(!"ID" %in% names(x)) {
wstop("no ID column in the data set")
}
+ if(case > 0) {
+ x <- recase_ev(x, case)
+ }
return(x)
}
-##' Operations for ev objects
-##'
-##' @param e1 object on left hand side of operator (lhs)
-##' @param e2 object on right hand side of operator (rhs)
-##' @name ev_ops
-##'
-##' @aliases +,ev,ev-method
-##' @docType methods
-##'
-##' @details
-##' All operations involving \code{\link[=mrgmod-class]{mrgmod}}
-##' objects have been deprecated.
-##'
-##' @rdname ev_ops
-##' @keywords internal
+#' Operations for ev objects
+#'
+#' @param e1 object on left hand side of operator (lhs)
+#' @param e2 object on right hand side of operator (rhs)
+#' @name ev_ops
+#'
+#' @aliases +,ev,ev-method
+#' @docType methods
+#'
+#' @details
+#' All operations involving \code{\link[=mrgmod-class]{mrgmod}}
+#' objects have been deprecated.
+#'
+#' @rdname ev_ops
+#' @keywords internal
setMethod("+", signature(e1="ev", e2="ev"), function(e1,e2) {
+ #stop("e1 + e2 operation is now deprecated")
return(add.ev(e1,e2))
})
-##' @rdname ev_ops
-##' @export
-##' @keywords internal
+#' @rdname ev_ops
+#' @export
+#' @keywords internal
setGeneric("%then%", function(e1,e2) standardGeneric("%then%"))
-##' @rdname ev_ops
-##' @export
-##' @keywords internal
-setMethod("%then%",c("ev", "ev"), function(e1,e2) {
- left <- as.data.frame(e1)
- if(!has_name("ii",left) | !has_name("addl",left)) {
- stop("both ii and addl are required in lhs",call.=FALSE)
- }
- y <- max(with(left, time + ii*addl + ii))
- e2@data$time <- y
- e1 + e2
+#' @rdname ev_ops
+#' @export
+#' @keywords internal
+setMethod("%then%",c("ev", "ev"), function(e1, e2) {
+ ev_seq(e1, e2)
+ # left <- e1@data
+ # if(!has_name("ii",left) | !has_name("addl",left)) {
+ # stop("both ii and addl are required in lhs",call.=FALSE)
+ # }
+ # y <- max(with(left, time + ii*addl + ii))
+ # e2@data$time <- y
+ # add.ev(e1,e2)
})
-##' @rdname ev_ops
-##' @export
-##' @keywords internal
+#' @rdname ev_ops
+#' @export
+#' @keywords internal
setMethod("+", c("ev", "numeric"), function(e1, e2) {
- e1@data$time <- e1@data$time + e2
- e1
+ stop("e1 + numeric operation is deprecated")
+ # e1@data$time <- e1@data$time + e2
+ # e1
})
-##' @param x an ev object
-##' @param recursive not used
-##' @param ... other ev objects to collect
-##'
-##' @rdname ev_ops
-##' @export
-setMethod("c", "ev", function(x,...,recursive=TRUE) {
+#' @param x an ev object
+#' @param recursive not used
+#' @param ... other ev objects to collect
+#'
+#' @rdname ev_ops
+#' @export
+setMethod("c", "ev", function(x, ..., recursive = TRUE) {
y <- list(...)
if(length(y)==0) return(x)
+ case <- x@case
for(i in seq_along(y)) {
- x <- add.ev(x,y[[i]])
+ x <- add.ev(x, y[[i]])
}
+ x@case <- case
return(x)
})
-add.ev <- function(e1,e2) {
+add.ev <- function(e1, e2) {
short <- setdiff(names(e1@data), names(e2@data))
long <- setdiff(names(e2@data), names(e1@data))
if(any(short=="ID") | any(long=="ID")) {
- stop("ID found in one ev object but not the other")
+ stop("ID found in one ev object but not the other.")
}
- if(length(short)>0) {
- add <- as.list(rep(0,length(short)))
+ if(length(short) > 0) {
+ add <- as.list(rep(0, length(short)))
names(add) <- short
e2@data <- cbind(e2@data, add)
}
@@ -390,79 +405,89 @@ add.ev <- function(e1,e2) {
names(add) <- long
e1@data <- cbind(e1@data, add)
}
+
e1@data <- as.data.frame(bind_rows(e1@data, e2@data))
- if(has_name("ID", e1@data)) {
- e1@data<- e1@data[order(e1@data$ID, e1@data$time),]
+ if("ID" %in% names(e1@data)) {
+ e1@data <- e1@data[order(e1@data$ID, e1@data$time), ]
} else {
- e1@data<- e1@data[order(e1@data$time),]
+ e1@data <- e1@data[order(e1@data$time), ]
}
return(e1)
}
-
-
-##' Replicate an event object
-##'
-##' An event sequence can be replicated a certain number of
-##' times in a certain number of IDs.
-##'
-##' @param x event object
-##' @param ID numeric vector if IDs
-##' @param n passed to \code{\link{ev_repeat}}
-##' @param wait passed to \code{\link{ev_repeat}}
-##' @param as.ev if \code{TRUE} an event object is returned
-##' @param id deprecated; use \code{ID} instead
-##'
-##' @seealso \code{\link{ev_repeat}}
-##'
-##' @examples
-##'
-##' e1 <- c(ev(amt=100), ev(amt=200, ii=24, addl=2, time=72))
-##'
-##' ev_rep(e1, 1:5)
-##'
-##' @return
-##' A single data.frame or event object as
-##' determined by the value of \code{as.ev}.
-##'
-##' @export
+#' Replicate an event object
+#'
+#' An event sequence can be replicated a certain number of
+#' times in a certain number of IDs.
+#'
+#' @param x event object
+#' @param ID numeric vector if IDs
+#' @param n passed to \code{\link{ev_repeat}}
+#' @param wait passed to \code{\link{ev_repeat}}
+#' @param as.ev if \code{TRUE} an event object is returned
+#' @param id deprecated; use \code{ID} instead
+#'
+#' @seealso \code{\link{ev_repeat}}
+#'
+#' @examples
+#'
+#' e1 <- c(ev(amt=100), ev(amt=200, ii=24, addl=2, time=72))
+#'
+#' ev_rep(e1, 1:5)
+#'
+#' @return
+#' A single data.frame or event object as
+#' determined by the value of \code{as.ev}.
+#'
+#' @export
ev_rep <- function(x, ID = 1, n = NULL, wait = 0, as.ev = FALSE, id = NULL) {
if(!missing(id)) {
warning("id argument is deprecated; use ID instead")
ID <- id
}
- x <- expand_event_object(as.data.frame(x),ID)
+ if(!inherits(x, c("data.frame", "ev"))) {
+ stop("x must be a data.frame or ev object.")
+ }
+ case <- ifelse(is.ev(x), x@case, 0)
+ data <- expand_event_object(to_data_frame(x), ID)
if(!is.null(n)) {
if(n > 1) {
- x <- ev_repeat(x,n=n,wait=wait)
+ data <- ev_repeat(data, n = n, wait = wait)
}
}
- if(as.ev) return(as.ev(x))
- return(x)
+ if(isTRUE(as.ev)) {
+ set_ev_case(as.ev(data), case)
+ } else {
+ recase_ev(data, case)
+ }
}
-##' Repeat a block of dosing events
-##'
-##' @param x event object or dosing data frame
-##' @param n number of times to repeat
-##' @param wait time to wait between repeats
-##' @param as.ev if \code{TRUE}, an event object is
-##' returned; otherwise a data.frame is returned
-##'
-##' @return
-##' See \code{as.ev} argument.
-##'
-##' @export
-ev_repeat <- function(x,n,wait=0,as.ev=FALSE) {
- x <- As_data_set(x)
+#' Repeat a block of dosing events
+#'
+#' @param x event object or dosing data frame
+#' @param n number of times to repeat
+#' @param wait time to wait between repeats
+#' @param as.ev if \code{TRUE}, an event object is
+#' returned; otherwise a data.frame is returned
+#'
+#' @return
+#' See \code{as.ev} argument.
+#'
+#' @export
+ev_repeat <- function(x, n, wait = 0, as.ev = FALSE) {
+ if(!inherits(x, c("data.frame", "ev"))) {
+ stop("x must be a data frame or ev object.")
+ }
+ case <- ifelse(is.ev(x), x@case, 1)
+ x <- to_data_frame(x)
if(!exists("ii", x)) {
x["ii"] <- 0
}
if(!exists("addl", x)) {
x["addl"] <- 0
}
- start <- x[1,"time"]
+ start <- x[1, "time"]
end <- x$time + x$ii*x$addl + x$ii
end <- max(end) + wait
out <- vector("list", n)
@@ -471,30 +496,29 @@ ev_repeat <- function(x,n,wait=0,as.ev=FALSE) {
nxt$time <- start + nxt$time + end*(i-1)
out[[i]] <- nxt
}
- out <- bind_rows(out)
- if(exists("ID", out)) {
- out <- arrange__(out, c("ID", "time"))
- out <- select__(out, unique(c("ID",colnames(out))))
+ out <- as.data.frame(bind_rows(out))
+ if("ID" %in% names(out)) {
+ out <- out[order(out$ID, out$time),, drop = FALSE]
+ out <- out[, unique(c("ID", colnames(out))), drop = FALSE]
+ rownames(out) <- NULL
}
- if(as.ev) {
- return(as.ev(out))
+ if(isTRUE(as.ev)) {
+ set_ev_case(as.ev(out), case)
} else {
- return(out)
+ recase_ev(out, case)
}
}
-
-
#' Schedule a series of event objects
#'
#' Use this function when you want to schedule two or more event objects in time
#' according the dosing interval (`ii`) and additional doses (`addl`).
#'
-#' @param ... event objects or numeric arguments named `wait` or `ii` to
-#' implement a period of no-dosing activity in the sequence (see details)
-#' @param ID numeric vector of subject IDs
-#' @param .dots a list of event objects that replaces `...`
-#' @param id deprecated; use `ID`
+#' @param ... Event objects or numeric arguments named `wait` or `ii` to
+#' implement a period of no-dosing activity in the sequence (see details).
+#' @param ID Numeric vector of subject IDs.
+#' @param .dots A list of event objects that replaces `...`.
+#' @param id Deprecated; use `ID`.
#'
#' @details
#'
@@ -573,6 +597,7 @@ ev_seq <- function(..., ID = NULL, .dots = NULL, id = NULL) {
if(is.null(names(evs))) {
names(evs) <- rep(".", length(evs))
}
+ case <- NULL
start <- 0
.ii <- NA_real_
ii <- 0
@@ -586,7 +611,7 @@ ev_seq <- function(..., ID = NULL, .dots = NULL, id = NULL) {
when = "0.11.3",
what = "ev_seq(.ii='has been renamed to ii')",
details = c(
- i = "Use `ii` instead of `.ii` to set time between doses."
+ i = "Use `ii` instead of `.ii` to set time between doses."
)
)
this_name <- "ii"
@@ -622,11 +647,12 @@ ev_seq <- function(..., ID = NULL, .dots = NULL, id = NULL) {
)
stop(err)
}
- e <- as.data.frame(evs[[i]])
- if(is.null(e[["ii"]])) {
+ if(is.null(case)) case <- evs[[i]]@case
+ e <- evs[[i]]@data
+ if(!("ii" %in% names(e))) {
e[["ii"]] <- 0
}
- if(is.null(e[["addl"]])) {
+ if(!("addl" %in% names(e))) {
e[["addl"]] <- 0
}
after <- ifelse(is.null(e[[".after"]]), 0, e[[".after"]])
@@ -642,19 +668,19 @@ ev_seq <- function(..., ID = NULL, .dots = NULL, id = NULL) {
}
out <- bind_rows(out)
out[[".after"]] <- NULL
- if(exists("rate", out)) {
+ if("rate" %in% names(out)) {
out[["rate"]] <- na2zero(out[["rate"]])
}
- if(exists("ss",out)) {
+ if("ss" %in% names(out)) {
out[["ss"]] <- na2zero(out[["ss"]])
}
if(is.numeric(ID)) {
- out <- ev_rep(out,ID)
+ out <- ev_rep(out, ID)
}
ans <- as.data.frame(out, stringsAsFactors = FALSE)
ans <- ans[order(ans$time),, drop = FALSE]
rownames(ans) <- NULL
- as.ev(ans)
+ set_ev_case(as.ev(ans), case)
}
#' @export
diff --git a/R/model_include.R b/R/model_include.R
index 5c940cac..049f81c4 100644
--- a/R/model_include.R
+++ b/R/model_include.R
@@ -173,4 +173,8 @@ plugins[["autodec"]] <- list(
name = "autodec", code = "// auto-dec plugin"
)
+plugins[["evtools"]] <- list(
+ name = "evtools", code = "#define _MRGSOLVE_USING_EVTOOLS_"
+)
+
# nocov end
diff --git a/R/mrgsolve.R b/R/mrgsolve.R
index 377e16b9..8a2e73e8 100644
--- a/R/mrgsolve.R
+++ b/R/mrgsolve.R
@@ -20,33 +20,21 @@
tran_upper <- c("AMT", "II", "SS", "CMT", "ADDL", "RATE", "EVID","TIME")
-nodataset <- matrix(
- 0, nrow=0, ncol=8,
- dimnames=list(
- NULL,
- c("ID", "time", "evid", "amt", "cmt","addl", "ii", "ss")
- )
-)
-
null_idata <- matrix(
0,
- nrow=0, ncol=1,
- dimnames=list(NULL, c("ID"))
+ nrow = 0, ncol = 1,
+ dimnames = list(NULL, c("ID"))
)
-
-no_idata_set <- function() {
- structure(null_idata,class="valid_idata_set")
-}
-
null_data <- matrix(
0,
- nrow=0, ncol=3,
+ nrow = 0, ncol = 3,
dimnames=list(NULL, c("ID", "time", "cmt"))
)
-no_data_set <- function() {
- structure(matrix(1,dimnames=list(NULL, "ID")), class="valid_data_set")
-}
+class(null_idata) <- c("valid_idata_set", "matrix")
+class(null_data) <- c("valid_data_set", "matrix")
+no_idata_set <- function() null_idata
+no_data_set <- function() null_data
tgrid_matrix <- function(x) {
n <- length(x)
@@ -71,13 +59,13 @@ tgrid_id <- function(col,idata) {
return(match(col,sort(unique(col)))-1)
}
-
-validate_idata <- function(idata) {
- if(is.null(idata)) return(invisible(TRUE))
- if(!(is.data.frame(idata) | is.matrix(idata)))
- wstop("idata needs to be either NULL, data.frame, or matrix.")
- return(invisible(TRUE))
-}
+# TODO: remove
+# validate_idata <- function(idata) {
+# if(is.null(idata)) return(invisible(TRUE))
+# if(!(is.data.frame(idata) | is.matrix(idata)))
+# wstop("idata needs to be either NULL, data.frame, or matrix.")
+# return(invisible(TRUE))
+# }
#' Simulate from a model object
@@ -221,7 +209,7 @@ mrgsim <- function(x, data=NULL, idata=NULL, events=NULL, nid=1, ...) {
if(have_data) {
if(is.ev(data)) {
- data <- as.data.frame(data, add_ID = 1)
+ data <- ev_to_ds(data)
}
if(have_idata) {
return(mrgsim_di(x, data = data, idata = idata, ...))
@@ -239,7 +227,7 @@ mrgsim <- function(x, data=NULL, idata=NULL, events=NULL, nid=1, ...) {
#' @rdname mrgsim
#' @export
-mrgsim_df <- function(...,output="df") mrgsim(...,output=output)
+mrgsim_df <- function(...,output="df") mrgsim(..., output = output)
#' mrgsim variant functions
#'
@@ -285,9 +273,8 @@ mrgsim_e <- function(x, events, idata = NULL, data = NULL, ...) {
}
wstop("invalid 'events' argument")
}
- events <- as.data.frame(events, add_ID = 1)
+ events <- ev_to_ds(events)
args <- list(...)
- # x <- do.call(update, c(x,args))
args <- combine_list(x@args,args)
do.call(
do_mrgsim,
@@ -300,7 +287,7 @@ mrgsim_e <- function(x, events, idata = NULL, data = NULL, ...) {
mrgsim_d <- function(x, data, idata = NULL, events = NULL, ...) {
if(!is.mrgmod(x)) mod_first()
if(is.ev(data)) {
- data <- as_data_set(data)
+ data <- ev_to_ds(data)
}
args <- list(...)
args <- combine_list(x@args,args)
@@ -325,13 +312,13 @@ mrgsim_ei <- function(x, events, idata, data = NULL, ...) {
wstop("invalid 'events' argument")
}
expand <- !has_ID(events) & nrow(idata) > 0
- events <- as.data.frame(events, add_ID = 1)
+ events <- ev_to_ds(events)
idata <- as.data.frame(idata)
if(!has_ID(idata)) {
- idata[["ID"]] <- seq_len(nrow(idata))
+ idata$ID <- seq_len(nrow(idata))
}
if(expand) {
- events <- expand_event_object(events,idata[["ID"]])
+ events <- expand_event_object(events, idata[["ID"]])
}
args <- list(...)
args <- combine_list(x@args,args)
@@ -345,7 +332,7 @@ mrgsim_ei <- function(x, events, idata, data = NULL, ...) {
#' @export
mrgsim_di <- function(x, data, idata, events = NULL, ...) {
if(!is.mrgmod(x)) mod_first()
- data <- as.data.frame(data, add_ID = 1)
+ data <- As_data_set(data)
idata <- as.data.frame(idata)
if(!has_ID(idata)) {
idata <- bind_col(idata, "ID", seq_len(nrow(idata)))
@@ -366,7 +353,7 @@ mrgsim_i <- function(x, idata, data = NULL, events = NULL, ...) {
if(!has_ID(idata)) {
idata <- bind_col(idata, "ID", seq_len(nrow(idata)))
}
- data <- matrix(idata[["ID"]], ncol = 1, dimnames = list(NULL, "ID"))
+ data <- matrix(idata$ID, ncol = 1, dimnames = list(NULL, "ID"))
args <- list(...)
args <- combine_list(x@args,args)
do.call(
@@ -776,7 +763,7 @@ qsim <- function(x,
if(!is.mrgmod(x)) mod_first()
if(is.ev(data)) {
- data <- as.data.frame.ev(data, add_ID = 1)
+ data <- as.data.frame.ev(data, add_ID = 1, final = TRUE)
}
## data
diff --git a/inst/base/modelheader.h b/inst/base/modelheader.h
index 0f38a8cb..40c9d078 100644
--- a/inst/base/modelheader.h
+++ b/inst/base/modelheader.h
@@ -93,6 +93,14 @@ typedef double capture;
#define A_0(a) _A_0_[a-1]
#define DADT(a) _DADT_[a-1]
#define T _ODETIME_[0]
+#define EXP(a) exp(a)
+#define LOG(a) log(a)
+#define SQRT(a) sqrt(a)
+#endif
+
+// EVTOOLS PLUGIN
+#ifdef _MRGSOLVE_USING_EVTOOLS_
+#include "mrgsolve-evtools.h"
#endif
// These are the fundamental macros for
@@ -128,10 +136,11 @@ typedef double capture;
#define CFONSTOP() (self.CFONSTOP = true); // Carry forward on stop
#define SYSTEMNOTADVANCING (self.SYSTEMOFF)
#define SOLVINGPROBLEM (self.solving)
-#define _SETINIT if(self.newind <=1) // Convenience
-#define _STOP_ID() (self.SYSTEMOFF=2);
-#define _STOP_ID_CF() (self.SYSTEMOFF=1);
-#define _STOP_ERROR() (self.SYSTEMOFF=9);
+#define _SETINIT if(self.newind <= 1) // Convenience
+#define _STOP_ID() (self.SYSTEMOFF = 1); // Stop this ID, log record, and fill NA after that
+#define _STOP_ID_CF() (self.SYSTEMOFF = 2); // Stop this ID and carry forward
+#define _STOP_ID_NA() (self.SYSTEMOFF = 3); // Fill na
+#define _STOP_ERROR() (self.SYSTEMOFF = 9); // CRUMP
// Macro to insert dxdt_CMT = 0; for all compartments
#define DXDTZERO() for(int _i_ = 0; _i_ < _nEQ; ++_i_) _DADT_[_i_] = 0;
diff --git a/inst/base/mrgsolv.h b/inst/base/mrgsolv.h
index ad6c4ab0..d44d99ce 100644
--- a/inst/base/mrgsolv.h
+++ b/inst/base/mrgsolv.h
@@ -102,8 +102,8 @@ class databox {
bool CFONSTOP; ///< carry forward on stop indicator
void* envir; ///< model environment
void stop() {SYSTEMOFF=9;}///< stops the problem when the next record is started
- void stop_id() {SYSTEMOFF=1;}///< stops solving for the current id, filling with NA
- void stop_id_cf(){SYSTEMOFF=2;}///< stops solving for the current id, filling last value
+ void stop_id() {SYSTEMOFF=2;}///< stops solving for the current id, filling with NA
+ void stop_id_cf(){SYSTEMOFF=1;}///< stops solving for the current id, filling last value
std::vector mevector;///< a collection of model events to pass back
void mevent(double time, int evid);///< constructor for evdata objects
double mtime(double time);///< creates evdata object for simple model event time
diff --git a/inst/base/mrgsolve-evtools.h b/inst/base/mrgsolve-evtools.h
new file mode 100644
index 00000000..1c73ca02
--- /dev/null
+++ b/inst/base/mrgsolve-evtools.h
@@ -0,0 +1,7 @@
+
+namespace mrgsolve {
+ namespace evt {
+ void push(databox& self, mrg::evdata ev) {
+ self.mevector.push_back(ev);
+ }
+}
diff --git a/inst/maintenance/unit/test-nm-vars.R b/inst/maintenance/unit/test-nm-vars.R
index 58a02a8d..7d2cb463 100644
--- a/inst/maintenance/unit/test-nm-vars.R
+++ b/inst/maintenance/unit/test-nm-vars.R
@@ -196,6 +196,9 @@ test_that("nm-vars functional test", {
[ main ]
F1 = F1I;
A_0(3) = 50;
+ capture a = EXP(CL);
+ capture b = LOG(VC);
+ capture c = SQRT(KA);
[ ode ]
DADT(1) = -KA * A(1);
DADT(2) = KA * A(1) - (CL/VC) * A(2);
@@ -212,4 +215,7 @@ test_that("nm-vars functional test", {
expect_equal(out1$GUT, out2$A1, tolerance = tol)
expect_equal(out1$CENT, out2$A2, tolerance = tol)
expect_equal(out1$RESP[1], out2$A3[1], tolerance = tol)
+ expect_equal(out2$a[1], exp(mod2$CL), tolerance = 1e-3)
+ expect_equal(out2$b[1], log(mod2$VC), tolerance = 1e-3)
+ expect_equal(out2$c[1], sqrt(mod2$KA), tolerance = 1e-3)
})
diff --git a/inst/maintenance/unit/test-on-off.R b/inst/maintenance/unit/test-on-off.R
index a2fd2d04..ac126335 100644
--- a/inst/maintenance/unit/test-on-off.R
+++ b/inst/maintenance/unit/test-on-off.R
@@ -25,10 +25,12 @@ context("test-on-off")
ode_on_off_1 <- '
[ param ] R = 1, F1 = 1
-[ cmt ] A
+[ cmt ] A
[ main ] F_A = F1;
[ ode ] dxdt_A = R;
+[ capture ] NEWIND
'
+mod <- mcode("ode_on_off_1", ode_on_off_1)
test_that("compartment is turned on when F is zero", {
data <- c(
@@ -40,7 +42,7 @@ test_that("compartment is turned on when F is zero", {
# run out to 24 hours
# with events at time 4 and 6, there would be duplicate records at
# those times
- mod <- mcode("ode_on_off_1", ode_on_off_1)
+
out <- mrgsim(mod, data)
ans <- out$A
time <- out$time
@@ -88,3 +90,14 @@ test_that("compartment with active infusion can be turned off", {
expect_equal(comp[[1]], comp[[2]])
expect_equal(comp[[1]], comp[[3]])
})
+
+test_that("evid 3 doesn't change NEWIND", {
+ dose <- ev(amt = 0, evid = 3, cmt = 1, time = 5)
+ out <- mrgsim(mod, dose, output = "df")
+ expect_equal(out$NEWIND[1], 0)
+ out <- out[-1,]
+ expect_true(all(out$NEWIND==2))
+ expect_equal(out$A[5], 5)
+ expect_equal(out$A[6], 0)
+ expect_equal(out$A[7], 1)
+})
diff --git a/inst/maintenance/unit/test-stop-id.R b/inst/maintenance/unit/test-stop-id.R
new file mode 100644
index 00000000..87dc9618
--- /dev/null
+++ b/inst/maintenance/unit/test-stop-id.R
@@ -0,0 +1,64 @@
+library(testthat)
+library(mrgsolve)
+
+Sys.setenv(R_TESTS="")
+options("mrgsolve_mread_quiet"=TRUE)
+
+context("test-stop-id")
+
+code <- '
+$PROB
+We can make the simulation stop for the current ID and fill either
+NA or the last value; OR we can just make the simulation stop with error.
+$PARAM STOP_CF = 0, STOP_NA = 0, STOP_CRUMP = 0
+$CMT A
+$ODE dxdt_A = 1;
+$MAIN
+if(NEWIND <=1) capture stopped = 0;
+$ERROR
+if(STOP_CF==1) {
+ if(TIME==4) {
+ self.stop_id_cf();
+ stopped = 1;
+ }
+}
+if(STOP_NA==1) {
+ if(TIME==3) {
+ self.stop_id();
+ stopped = 1;
+ }
+}
+if(STOP_CRUMP==1) {
+ self.stop();
+}
+'
+
+mod <- mcode("test-stop-id", code, end = 6)
+
+test_that("Stop the current ID and carry forward", {
+ mod <- param(mod, STOP_CF = 1, STOP_NA = 0, STOP_CRUMP = 0)
+ out <- mrgsim_df(mod)
+ pre <- out[out$time <= 3,,drop=FALSE]
+ post <- out[out$time > 3,,drop=FALSE]
+ expect_true(all(pre$stopped==0))
+ expect_true(all(post$stopped==1))
+ expect_equal(pre$A, c(0,1,2,3))
+ expect_true(all(post$A==4))
+})
+
+test_that("Stop the current ID and fill NA", {
+ mod <- param(mod, STOP_NA = 1, STOP_CF = 0, STOP_CRUMP = 0)
+ out <- mrgsim_df(mod)
+ pre <- out[!is.na(out$time),,drop=FALSE]
+ post <- out[is.na(out$time),,drop=FALSE]
+ expect_equal(nrow(pre), 4)
+ expect_equal(pre$A, c(0,1,2,3))
+ expect_equal(pre$stopped, c(0,0,0,1))
+ expect_equal(nrow(post), 3)
+ expect_true(all(is.na(post)))
+})
+
+test_that("Stop the entire simulation", {
+ mod <- param(mod, STOP_NA = 0, STOP_CF = 0, STOP_CRUMP = 1)
+ expect_error(mrgsim_df(mod), regexp="the problem was stopped at user request.")
+})
diff --git a/man/as.ev.Rd b/man/as.ev.Rd
index 193b263d..50fbec40 100644
--- a/man/as.ev.Rd
+++ b/man/as.ev.Rd
@@ -13,14 +13,17 @@ as.ev(x, ...)
\S4method{as.ev}{ev}(x, ...)
}
\arguments{
-\item{x}{an object to coerce}
+\item{x}{An object to coerce.}
-\item{...}{not used}
+\item{...}{Not used.}
-\item{keep_id}{if \code{TRUE}, \code{ID} column is retained if it exists}
+\item{keep_id}{If \code{TRUE}, \code{ID} column is retained if it exists.}
-\item{clean}{if \code{TRUE}, only dosing or ID information is retained in
-the result}
+\item{clean}{If \code{TRUE}, only dosing or ID information is retained in
+the result.}
+}
+\value{
+An object with class ev.
}
\description{
Coerce an object to class ev
diff --git a/man/as_data_set.Rd b/man/as_data_set.Rd
index 251b130d..451d3bf0 100644
--- a/man/as_data_set.Rd
+++ b/man/as_data_set.Rd
@@ -25,22 +25,32 @@ The goal is to take a series of event objects and combine them
into a single data set that can be passed to \code{\link[=data_set]{data_set()}}.
}
\details{
-Each event object is added to the data frame as an \code{ID}
-or set of \code{ID}s that are distinct from the \code{ID}s
-in the other event objects. Note that including \code{ID}
-argument to the \code{\link[=ev]{ev()}} call where \code{length(ID)}
-is greater than one will render that set of
-events for all of \code{ID}s that are requested.
+Each event object is added to the data frame as an \code{ID} or set of \code{ID}s
+that are distinct from the \code{ID}s in the other event objects. Note that
+including \code{ID} argument to the \code{\link[=ev]{ev()}} call where \code{length(ID)} is greater
+than one will render that set of events for all of \code{ID}s that are requested.
+
+When determining the case for output names, the \code{case} attribute for
+the first \code{ev} object passed will be used to set the case for the output
+data.frame.
To get a data frame with one row (event) per \code{ID}, look at \code{\link[=expand.ev]{expand.ev()}}.
}
\examples{
+a <- ev(amt = c(100,200), cmt=1, ID = seq(3))
+b <- ev(amt = 300, time = 24, ID = seq(2))
+c <- ev(amt = 1000, ii = 8, addl = 10, ID = seq(3))
+
+as_data_set(a, b, c)
-as_data_set(ev(amt = c(100,200), cmt=1, ID = seq(3)),
- ev(amt = 300, time = 24, ID = seq(2)),
- ev(amt = 1000, ii = 8, addl = 10, ID = seq(3)))
+d <- evd(amt = 500)
+as_data_set(d, a)
+
# Instead of this, use expand.ev
as_data_set(ev(amt = 100), ev(amt = 200), ev(amt = 300))
}
+\seealso{
+\code{\link[=expand.ev]{expand.ev()}}, \code{\link[=ev]{ev()}}
+}
diff --git a/man/design.Rd b/man/design.Rd
index 1a88aaeb..02b3d353 100644
--- a/man/design.Rd
+++ b/man/design.Rd
@@ -53,7 +53,7 @@ mod \%>\%
idata_set(idata) \%>\%
design(list(des1, des2),"amt") \%>\%
data_set(data) \%>\%
- mrgsim \%>\%
+ mrgsim() \%>\%
plot(RESP~time|GRP)
}
diff --git a/man/ev-class.Rd b/man/ev-class.Rd
index a6e7249c..e5cf04bc 100644
--- a/man/ev-class.Rd
+++ b/man/ev-class.Rd
@@ -11,6 +11,8 @@ S4 events class
\describe{
\item{\code{data}}{a data frame of events}
+
+\item{\code{case}}{indicates how to handle column naming upon coerce to data.frame}
}}
\keyword{internal}
diff --git a/man/ev.Rd b/man/ev.Rd
index f6cc92ef..25d5675a 100644
--- a/man/ev.Rd
+++ b/man/ev.Rd
@@ -97,7 +97,7 @@ reduced_load <- dplyr::mutate(loading, amt = 750)
}
\seealso{
-\code{\link{ev_rep}}, \code{\link{ev_days}},
+\code{\link{evd}}, \code{\link{ev_rep}}, \code{\link{ev_days}},
\code{\link{ev_repeat}}, \code{\link{ev_assign}},
\code{\link{ev_seq}}, \code{\link{mutate.ev}},
\code{\link{as.ev}}, \code{\link{ev_methods}}
diff --git a/man/ev_seq.Rd b/man/ev_seq.Rd
index 7a535cc3..415b83a2 100644
--- a/man/ev_seq.Rd
+++ b/man/ev_seq.Rd
@@ -10,14 +10,14 @@ ev_seq(..., ID = NULL, .dots = NULL, id = NULL)
\method{seq}{ev}(...)
}
\arguments{
-\item{...}{event objects or numeric arguments named \code{wait} or \code{ii} to
-implement a period of no-dosing activity in the sequence (see details)}
+\item{...}{Event objects or numeric arguments named \code{wait} or \code{ii} to
+implement a period of no-dosing activity in the sequence (see details).}
-\item{ID}{numeric vector of subject IDs}
+\item{ID}{Numeric vector of subject IDs.}
-\item{.dots}{a list of event objects that replaces \code{...}}
+\item{.dots}{A list of event objects that replaces \code{...}.}
-\item{id}{deprecated; use \code{ID}}
+\item{id}{Deprecated; use \code{ID}.}
}
\value{
A single event object sorted by \code{time}.
diff --git a/man/evd.Rd b/man/evd.Rd
new file mode 100644
index 00000000..aa756174
--- /dev/null
+++ b/man/evd.Rd
@@ -0,0 +1,48 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/class_evd.R
+\name{evd}
+\alias{evd}
+\alias{evd,mrgmod-method}
+\alias{evd,missing-method}
+\alias{evd,ev-method}
+\alias{as.evd}
+\title{Create an event object with data-like names}
+\usage{
+evd(x, ...)
+
+\S4method{evd}{mrgmod}(x, ...)
+
+\S4method{evd}{missing}(x, ...)
+
+\S4method{evd}{ev}(x, ...)
+
+as.evd(x)
+}
+\arguments{
+\item{x}{An event object.}
+
+\item{...}{Arguments passed to \code{\link[=ev]{ev()}}.}
+}
+\description{
+This function calls \code{\link[=ev]{ev()}} to create an event object and then sets the
+case attribute so that it renders nmtran data names in upper case. An
+object created with \code{\link[=evd]{evd()}} can be used in the same way as an object
+created with \code{\link[=ev]{ev()}}.
+}
+\details{
+Note that \code{evd} isn't a separate class; it is just an \code{ev} object with
+a specific \code{case} attribute. See examples which illustrate the difference.
+}
+\examples{
+a <- evd(amt = 100)
+b <- ev(amt = 300)
+a
+as.data.frame(a)
+as_data_set(a, b)
+as_data_set(b, a)
+as.data.frame(seq(a, b))
+
+}
+\seealso{
+\code{\link[=ev]{ev()}}, \code{\link[=lctran]{lctran()}}, \code{\link[=uctran]{uctran()}}
+}
diff --git a/man/lctran.Rd b/man/lctran.Rd
index 49b3a2f6..06907fb0 100644
--- a/man/lctran.Rd
+++ b/man/lctran.Rd
@@ -2,30 +2,55 @@
% Please edit documentation in R/data_set.R
\name{lctran}
\alias{lctran}
-\title{Convert select upper case column names to lower case}
+\alias{uctran}
+\title{Change the case of nmtran-like data items}
\usage{
-lctran(data)
+lctran(data, warn = TRUE)
+
+uctran(data, warn = TRUE)
}
\arguments{
-\item{data}{an nmtran-like data frame}
+\item{data}{A data set with nmtran-like format.}
+
+\item{warn}{If \code{TRUE}, a warning will be issued when there are both upper
+and lower case versions of any nmtran-like column in the data frame.}
}
\value{
-A data.frame with renamed columns
+A data frame with possibly renamed columns.
The input data set, with select columns made lower case.
}
\description{
Previous data set requirements included lower case names for data items
-like \code{AMT} and \code{EVID}. Lower case is no longer required.
+like \code{AMT} and \code{EVID}. Lower case is no longer required. However, it is still
+a requirement that nmtran like data column names are either all lower case
+or all upper case.
}
\details{
-Columns that will be renamed with lower case versions: \code{AMT},
-\code{II}, \code{SS}, \code{CMT}, \code{ADDL}, \code{RATE}, \code{EVID},
-\code{TIME}. If a lower case version of these names exist in the data
-set, the column will not be renamed.
+Columns that will be renamed with lower or upper case versions:
+\itemize{
+\item \code{AMT / amt}
+\item \code{II / ii}
+\item \code{SS / ss}
+\item \code{CMT / cmt}
+\item \code{ADDL / addl}
+\item \code{RATE / rate}
+\item \code{EVID / evid}
+\item \code{TIME / time}
+}
+
+If both lower and upper case versions of the name are present in the data
+frame, no changes will be made.
}
\examples{
-data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2)
+data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2, WT = 80)
+lctran(data)
+
+data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2, wt = 80)
+uctran(data)
+
+# warning
+data <- data.frame(TIME = 1, time = 2, CMT = 5)
lctran(data)
}
diff --git a/src/datarecord.cpp b/src/datarecord.cpp
index 95478cf4..a94d4330 100644
--- a/src/datarecord.cpp
+++ b/src/datarecord.cpp
@@ -179,10 +179,7 @@ void datarecord::implement(odeproblem* prob) {
prob->on(i);
prob->rate0(i,0.0);
}
- {
- prob->newind(1);
- prob->init_call(Time);
- }
+ prob->init_call(Time);
break;
case 8: // replace
prob->y(eq_n, Amt);
diff --git a/src/devtran.cpp b/src/devtran.cpp
index 1458f67e..b928fa76 100644
--- a/src/devtran.cpp
+++ b/src/devtran.cpp
@@ -414,9 +414,14 @@ Rcpp::List DEVTRAN(const Rcpp::List parin,
this_rec->id(id);
if(prob.systemoff()) {
+ // This starts a loop that will finish the remaining records
+ // for an individual; no other calls to any model functions will
+ // be made
+ // SYSTEMOFF = 1 --> copy model results to the line
+ // SYSTEMOFF != 0, !=1 --> fill NA
unsigned short int status = prob.systemoff();
if(status==9) CRUMP("the problem was stopped at user request.");
- if(status==999) CRUMP("999 sent from the model");
+ if(status==999) CRUMP("999 sent from the model.");
if(this_rec->output()) {
if(status==1) {
ans(crow,0) = this_rec->id();
@@ -430,7 +435,7 @@ Rcpp::List DEVTRAN(const Rcpp::List parin,
} else {
for(int k=0; k < ans.ncol(); ++k) {
ans(crow,k) = NA_REAL;
- }
+ }
}
++crow;
}
@@ -582,7 +587,7 @@ Rcpp::List DEVTRAN(const Rcpp::List parin,
CRUMP("Compartment number in modeled event out of range.");
}
}
- rec_ptr new_ev = NEWREC(this_cmt,this_evid,this_amt,this_time,0.0);
+ rec_ptr new_ev = NEWREC(this_cmt,this_evid,this_amt,this_time,mt[mti].rate);
new_ev->phantom_rec();
if(mt[mti].now) {
new_ev->implement(&prob);
diff --git a/tests/testthat/test-data_set.R b/tests/testthat/test-data_set.R
index 53669de6..888df1a1 100644
--- a/tests/testthat/test-data_set.R
+++ b/tests/testthat/test-data_set.R
@@ -178,4 +178,30 @@ test_that("add position argument to expand observations issue-565", {
expect_equal(dat1$time,dat2$time)
})
+test_that("Convert names to lower case with lctran", {
+ data <- data.frame(time = 1, EVID = 2, ss = 2, foo = 5, BAR = 2)
+ ans <- lctran(data)
+ expect_equal(
+ names(ans),
+ c("time", "evid", "ss", "foo", "BAR")
+ )
+ data <- data.frame(time = 1, EVID = 2, ss = 2, TIME = 5, BAR = 2)
+ expect_warning(
+ lctran(data),
+ regexp = "There are both upper and lower case"
+ )
+})
+test_that("Convert names to upper case with uctran", {
+ data <- data.frame(time = 1, EVID = 2, ss = 2, foo = 5, BAR = 2)
+ ans <- uctran(data)
+ expect_equal(
+ names(ans),
+ c("TIME", "EVID", "SS", "foo", "BAR")
+ )
+ data <- data.frame(time = 1, EVID = 2, ss = 2, TIME = 5, BAR = 2)
+ expect_warning(
+ uctran(data),
+ regexp = "There are both upper and lower case"
+ )
+})
diff --git a/tests/testthat/test-evd.R b/tests/testthat/test-evd.R
new file mode 100644
index 00000000..a1d5edbd
--- /dev/null
+++ b/tests/testthat/test-evd.R
@@ -0,0 +1,148 @@
+# Copyright (C) 2013 - 2022 Metrum Research Group
+#
+# This file is part of mrgsolve.
+#
+# mrgsolve is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# mrgsolve is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with mrgsolve. If not, see .
+
+library(testthat)
+library(mrgsolve)
+library(dplyr)
+
+Sys.setenv(R_TESTS="")
+options("mrgsolve_mread_quiet"=TRUE)
+
+context("test-evd")
+
+test_that("create evd object", {
+ a <- evd(amt = 100)
+ expect_is(a, "ev")
+ expect_equal(a@case, 1)
+ b <- ev(amt = 100)
+ expect_equal(b@case, 0)
+
+ mod <- house()
+ mod <- evd(mod, amt = 100)
+ expect_identical(mod@args$events, a)
+
+ c <- ev(a)
+ expect_is(c, "ev")
+ expect_equal(c@case, 0L)
+
+ d <- evd(b)
+ expect_is(d, "ev")
+ expect_equal(d@case, 1L)
+})
+
+test_that("evd object has all lower case names", {
+ a <- ev(amt = 100, ii = 12, addl = 23)
+ b <- evd(amt = 100, ii = 12, addl = 23)
+ expect_identical(names(a), names(b))
+ expect_identical(names(a), tolower(names(b)))
+})
+
+test_that("evd object realize names", {
+ a <- evd(amt = 100, ii = 12, addl = 23, ss = 1, rate = 2,
+ cmt = 5, time = 12, evid = 3, kyle = 0)
+
+ b <- as.data.frame(a, add_ID = 1)
+ tnames <- seq(length(names(a))-2)
+ expect_identical(names(b)[tnames], toupper(names(a)[tnames]))
+ c <- mrgsolve:::ev_to_ds(a)
+ expect_identical(b, c)
+ expect_identical(names(b)[tnames], toupper(names(a))[tnames])
+})
+
+test_that("evd object simulated names", {
+ a <- evd(amt = 100)
+ idata <- data.frame(ID = 1)
+ mod <- update(house(), end = -1)
+ out1 <- mrgsim(mod, a)
+ out2 <- mrgsim_e(mod, a)
+ out3 <- mrgsim_ei(mod, a, idata)
+ out4 <- qsim(mod, a)
+ out5 <- mrgsim_q(mod, a)
+ out6 <- mrgsim_d(mod, a)
+ out7 <- mrgsim_di(mod, a, idata)
+ x <- names(out1)
+ expect_equal(x, toupper(x))
+ expect_equal(x, names(out2))
+ expect_equal(x, names(out3))
+ expect_equal(x, names(out4))
+ expect_equal(x, names(out5))
+ expect_equal(x, names(out6))
+ expect_equal(x, names(out7))
+})
+
+test_that("evd object carry out tran names", {
+ a <- evd(amt = 100, ii = 12, addl = 2, rate = 1)
+ mod <- update(house(), end = -1)
+ out1 <- mrgsim(mod, a, carry_out = "AMT, II, ADDL, RATE,CMT")
+ out2 <- mrgsim(mod, a, carry_out = "amt, ii, addl, rate, cmt")
+ expect_equal(names(out1), toupper(names(out1)))
+ expect_equal(names(out2)[1:2], c("ID", "TIME"))
+ carried <- names(out2)[seq(3,7)]
+ expect_equal(carried, tolower(carried))
+})
+
+test_that("coerce ev object to evd", {
+ a <- ev(amt = 100)
+ b <- as.evd(a)
+ expect_identical(a@case, 0L)
+ expect_identical(b@case, 1L)
+})
+
+test_that("ev operations with evd objects", {
+ a <- evd(amt = 100)
+ b <- ev(amt = 200)
+ c <- evd(amt = 300)
+
+ e <- ev_seq(a, b, a)
+ expect_is(e, "ev")
+ expect_equal(e@case, 1L)
+ ee <- as.data.frame(e)
+ expect_equal(names(ee), toupper(names(ee)))
+
+ e2 <- ev_seq(b, a, b)
+ expect_is(e2, "ev")
+ expect_equal(e2@case, 0L)
+ ee2 <- as.data.frame(e2)
+ expect_equal(names(ee2), tolower(names(ee2)))
+
+ e3 <- c(a, b)
+ expect_is(e3, "ev")
+ expect_equal(e3@case, 1L)
+
+ e4 <- c(b, a)
+ expect_is(e4, "ev")
+ expect_equal(e4@case, 0L)
+
+ d <- ev_rep(a, seq(3))
+ expect_is(d, "data.frame")
+ expect_equal(names(d), toupper(names(d)))
+
+ d2 <- ev_rep(a, seq(3), n = 2)
+ expect_is(d2, "data.frame")
+ expect_equal(names(d2), toupper(names(d2)))
+
+ d3 <- as_data_set(a, b, c)
+ expect_is(d3, "data.frame")
+ expect_equal(names(d3), toupper(names(d3)))
+
+ d4 <- as_data_set(b, a, b)
+ expect_is(d4, "data.frame")
+ check <- names(d4)[-1]
+ expect_equal(check, tolower(check))
+ expect_equal(names(d4)[1], "ID")
+
+})