Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

311 condition numbers #323

Merged
merged 7 commits into from
Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# nlmixr2est (development version)

- Breaking change, now calculate condition number based on covariance
and correlation, the names have changed to be more explicit.
`conditionNumber` changed to `conditionNumberCov` and a new metric
`conditionNumberCor` has been added.

- A bug in boundary value detection prevented automatic covariance calculation
with FOCEi estimation (#318)


- Fix `vpcSim` so that it will be a bit more robust when it is
difficult to simulate.

Expand Down
21 changes: 18 additions & 3 deletions R/assert.R
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ assertNlmixrObjDataFrameRow <- function(df, allowNa=FALSE) {
paste(.diff, collapse="', '"), "'",
call.=FALSE)
}
.w <- which(.name == "Condition Number")
.w <- which(.name == "Condition#(Cov)")
if (length(.w) == 1) {
.cn <- df[["Condition Number"]]
.cn <- df[["Condition#(Cov)"]]
if (inherits(.cn, "numeric")) {
if (!is.na(.cn)) {
.cn <- setNames(.cn, NULL)
Expand All @@ -75,6 +75,21 @@ assertNlmixrObjDataFrameRow <- function(df, allowNa=FALSE) {
} else {
.cn <- NA
}
.w <- which(.name == "Condition#(Cor)")
if (length(.w) == 1) {
.cnr <- df[["Condition#(Cor)"]]
if (inherits(.cnr, "numeric")) {
if (!is.na(.cnr)) {
.cnr <- setNames(.cnr, NULL)
} else {
.cnr <- NA
}
} else {
.cnr <- NA
}
} else {
.cnr <- NA
}
.df1 <- df[, .needed]
if (length(.df1[, 1]) == 0) {
stop("missing objective function in objective function data frame", call.=FALSE)
Expand All @@ -87,5 +102,5 @@ assertNlmixrObjDataFrameRow <- function(df, allowNa=FALSE) {
checkmate::assertNumeric(df[[x]], len=1, any.missing=FALSE, .var.name=x)
})
}
list(.df1, .cn)
list(.df1, .cn, .cnr)
}
3 changes: 2 additions & 1 deletion R/broom.R
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ glance.nlmixr2FitCore <- function(x, ...) {
.aic <- AIC(x) ## To calculate AIC if needed
.df <- x$objDf[which(tolower(rownames(x$objDf)) == tolower(x$ofvType)), ]
names(.df) <- gsub("Log-likelihood", "logLik", names(.df))
names(.df) <- gsub("Condition Number", "conditionNumber", names(.df))
names(.df) <- gsub("Condition#(Cor)", "conditionNumberCor", names(.df))
names(.df) <- gsub("Condition#(Cov)", "conditionNumberCov", names(.df))
tibble::as_tibble(.df)
}

Expand Down
3 changes: 2 additions & 1 deletion R/complete.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

.nmObjGetEnvInfo <- list(
ui="rxode2 user interface",
conditionNumber="Condition Number",
conditionNumberCor="Condition Number (Correlation)",
conditionNumberCov="Condition Number (Covariance)",
cov="Covariance of fixed effects",
covMethod="Covariance Method for fixed effects",
etaObf="ETAs and their individual objective function contribution (if applicable)",
Expand Down
9 changes: 9 additions & 0 deletions R/cov.R
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,12 @@ getVarCov.nlmixr2FitCore <- function(obj, ...) {

##' @export
getVarCov.nlmixr2FitCoreSilent <- getVarCov.nlmixr2FitCore


.cov2cor <- function(cov) {
.sd2 <- sqrt(diag(cov))
.cor <- stats::cov2cor(cov)
dimnames(.cor) <- dimnames(cov)
diag(.cor) <- .sd2
.cor
}
21 changes: 15 additions & 6 deletions R/focei.R
Original file line number Diff line number Diff line change
Expand Up @@ -1750,13 +1750,22 @@ nlmixr2Est.posthoc <- function(env, ...) {
ret <- attr(class(ret), ".foceiEnv")
}
.objDf1 <- get("objDf", ret)
if (any(names(.objDf1) == "Condition Number")) {
if (!any(names(objDf) == "Condition Number")) {
objDf[["Condition Number"]] <- NA_real_
if (any(names(.objDf1) == "Condition#(Cov)")) {
if (!any(names(objDf) == "Condition#(Cov)")) {
objDf[["Condition#(Cov)"]] <- NA_real_
}
} else if (any(names(objDf) == "Condition Number")) {
if (!any(names(.objDf1) == "Condition Number")) {
.objDf1[["Condition Number"]] <- NA_real_
} else if (any(names(objDf) == "Condition#(Cov)")) {
if (!any(names(.objDf1) == "Condition#(Cov)")) {
.objDf1[["Condition#(Cov)"]] <- NA_real_
}
}
if (any(names(.objDf1) == "Condition#(Cor)")) {
if (!any(names(objDf) == "Condition#(Cor)")) {
objDf[["Condition#(Cor)"]] <- NA_real_
}
} else if (any(names(objDf) == "Condition#(Cor)")) {
if (!any(names(.objDf1) == "Condition#(Cor)")) {
.objDf1[["Condition#(Cor)"]] <- NA_real_
}
}
assign("objDf", rbind(.objDf1, objDf), envir=ret)
Expand Down
26 changes: 21 additions & 5 deletions R/nlmixrAddObjectiveFunction.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#' @param fit nlmixr fit object
#' @param objDf nlmixr objective function data frame which has column
#' names "OBJF", "AIC", "BIC", "Log-likelihood" and
#' "Condition Number"
#' "Condition#(Cov)" "Condition#(Cor)"
#' @param type Objective Function Type
#' @param etaObf Eta objective function table to add (with focei) to
#' give focei objective function
Expand All @@ -27,32 +27,48 @@ nlmixrAddObjectiveFunctionDataFrame <- function(fit, objDf, type, etaObf=NULL) {
} else if (!is.na(.inRow[[2]])) {
.cn <- .inRow[[2]]
}
.cnr <- NA_real_
if (!is.na(.inRow2[[3]])) {
.cnr <- .inRow2[[3]]
} else if (!is.na(.inRow[[3]])) {
.cnr <- .inRow[[3]]
}
if (is.na(.inRow2[[1]][[1]])) {
# Here the original data frame is NA, that is the objective function has not been calculated
.tmp <- cbind(.inRow[[1]], data.frame("Condition Number"=.cn, check.names=FALSE))
.tmp <- cbind(.inRow[[1]], data.frame("Condition#(Cov)"=.cn, "Condition#(Cor)"=.cnr, check.names=FALSE))
row.names(.tmp) <- type
assign("objDf", .tmp, envir=fit$env)
setOfv(fit, type)
} else {
if (any(.rownames == type)) stop("objective function '", type, "' already present", call.=FALSE)
# Now the original data frame is not NA.
.tmp <- rbind(.inRow[[1]], .inRow2[[1]])
.tmp[["Condition Number"]] <- .cn
.tmp[["Condition#(Cov)"]] <- .cn
.tmp[["Condition#(Cor)"]] <- .cnr
row.names(.tmp) <- c(type, .rownames)
assign("objDf", .tmp, envir=fit$env)
setOfv(fit, type)
}
} else {
if (any(.rownames == type)) stop("objective function '", type, "' already present", call.=FALSE)
## Now there is at least one interesting objective function
.cn <- .cur[["Condition Number"]][1]
.cn <- .cur[["Condition#(Cov)"]][1]
if (is.null(.cn)) {
.cn <- NA_real_
}
if (is.na(.cn) & !is.na(.inRow[[2]])) {
.cn <- .inRow[[2]]
}
.cur <- rbind(.cur[, names(.cur) != "Condition Number"], .inRow[[1]])
.cnr <- .cur[["Condition#(Cor)"]][1]
if (is.null(.cnr)) {
.cnr <- NA_real_
}
if (is.na(.cnr) & !is.na(.inRow[[3]])) {
.cnr <- .inRow[[3]]
}
.cur <- rbind(.cur[, !(names(.cur) %in% c("Condition#(Cor)", "Condition#(Cov)"))], .inRow[[1]])
.cur[["Condition#(Cov)"]] <- .cn
.cur[["Condition#(Cor)"]] <- .cnr
row.names(.cur) <- c(.rownames, type)
assign("objDf", .cur, envir=fit$env)
setOfv(fit, type)
Expand Down
4 changes: 2 additions & 2 deletions R/nmObjGet.R
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,8 @@ nmObjGet.env <- function(x, ...) {
nmObjGet.condition <- function(x, ...) {
.env <- x[[1]]
.objDf <- .env$objDf
if (any(names(.objDf) == "Condition Number")) {
.cn <- .objDf[, "Condition Number"]
if (any(names(.objDf) == "Condition#(Cor)")) {
.cn <- .objDf[, "Condition#(Cor)"]
.cn <- .cn[!is.na(.cn)]
return(.cn)
}
Expand Down
44 changes: 38 additions & 6 deletions src/inner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5877,13 +5877,44 @@ void foceiFinalizeTables(Environment e){
}
LogicalVector skipCov = e["skipCov"];

if (covExists) {
Function loadNamespace("loadNamespace", R_BaseNamespace);
Environment nlmixr2 = loadNamespace("nlmixr2est");
Function getCor = nlmixr2[".cov2cor"];
e["fullCor"] = getCor(e["cov"]);
arma::mat cor = as<arma::mat>(e["fullCor"]);
cor.diag().ones();
arma::vec eigval;
arma::mat eigvec;
eig_sym(eigval, eigvec, cor);
e["eigenCor"] = eigval;
e["eigenVecCor"] = eigvec;
unsigned int k=0;
if (eigval.size() > 0){
double mx=std::fabs(eigval[0]), mn, cur;
mn=mx;
for (k = eigval.size(); k--;){
cur = std::fabs(eigval[k]);
if (cur > mx){
mx=cur;
}
if (cur < mn){
mn=cur;
}
}
e["conditionNumberCor"] = mx/mn;
} else {
e["conditionNumberCor"] = NA_REAL;
}
}

if (covExists && op_focei.eigen){
arma::vec eigval;
arma::mat eigvec;

eig_sym(eigval, eigvec, cov);
e["eigen"] = eigval;
e["eigenVec"] = eigvec;
e["eigenCov"] = eigval;
e["eigenVecCov"] = eigvec;
unsigned int k=0;
if (eigval.size() > 0){
double mx=std::fabs(eigval[0]), mn, cur;
Expand All @@ -5897,9 +5928,9 @@ void foceiFinalizeTables(Environment e){
mn=cur;
}
}
e["conditionNumber"] = mx/mn;
e["conditionNumberCov"] = mx/mn;
} else {
e["conditionNumber"] = NA_REAL;
e["conditionNumberCov"] = NA_REAL;
}
}
arma::vec se1;
Expand Down Expand Up @@ -6255,10 +6286,11 @@ void foceiFinalizeTables(Environment e){
}
}
List objDf;
if (e.exists("conditionNumber")){
if (e.exists("conditionNumberCov")){
objDf = List::create(_["OBJF"] = as<double>(e["objective"]), _["AIC"]=as<double>(e["AIC"]),
_["BIC"] = as<double>(e["BIC"]), _["Log-likelihood"]=as<double>(e["logLik"]),
_["Condition Number"]=as<double>(e["conditionNumber"]));
_["Condition#(Cov)"]=as<double>(e["conditionNumberCov"]),
_["Condition#(Cor)"]=as<double>(e["conditionNumberCor"]));
} else {
objDf = List::create(_["OBJF"] = as<double>(e["objective"]), _["AIC"]=as<double>(e["AIC"]),
_["BIC"] = as<double>(e["BIC"]), _["Log-likelihood"]=as<double>(e["logLik"]));
Expand Down
15 changes: 14 additions & 1 deletion tests/testthat/test-saem-aic.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,31 @@ nmTest({

fit <-
suppressMessages(
nlmixr(one.cmt, theo_sd, est = "saem", control = list(calcTables = FALSE, print = 0))
nlmixr(one.cmt, theo_sd, est = "saem",
control = saemControl(calcTables = FALSE, print = 0, nBurn=10, nEm=10))
)

expect_s3_class(fit, "nlmixr2FitCore")
expect_false(inherits(fit, "data.frame"))
expect_false(inherits(fit, "nlmixrFitData"))

expect_error(suppressMessages(setOfv(fit, "focei")), NA)
expect_true(any(names(fit$objDf) == "Condition#(Cov)"))
expect_true(any(names(fit$objDf) == "Condition#(Cor)"))
expect_error(suppressMessages(setOfv(fit, "foce")), NA)
expect_true(any(names(fit$objDf) == "Condition#(Cov)"))
expect_true(any(names(fit$objDf) == "Condition#(Cor)"))
expect_error(suppressMessages(setOfv(fit, "fo")), NA)
expect_true(any(names(fit$objDf) == "Condition#(Cov)"))
expect_true(any(names(fit$objDf) == "Condition#(Cor)"))
expect_error(suppressMessages(setOfv(fit, "gauss3_1.6")), NA)
expect_true(any(names(fit$objDf) == "Condition#(Cov)"))
expect_true(any(names(fit$objDf) == "Condition#(Cor)"))
expect_error(suppressMessages(setOfv(fit, "laplace2")), NA)
expect_true(any(names(fit$objDf) == "Condition#(Cov)"))
expect_true(any(names(fit$objDf) == "Condition#(Cor)"))
expect_error(suppressMessages(setOfv(fit, "laplace3")), NA)
expect_true(any(names(fit$objDf) == "Condition#(Cov)"))
expect_true(any(names(fit$objDf) == "Condition#(Cor)"))
})
})