### Fit a general nonlinear mixed effects model ### ### Copyright 2006-2024 The R Core team ### Copyright 1997-2003 Jose C. Pinheiro, ### Douglas M. Bates ### ### This program 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. ### ### This program 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. ### ### A copy of the GNU General Public License is available at ### http://www.r-project.org/Licenses/ nlme <- function(model, data = sys.frame(sys.parent()), fixed, random = fixed, groups, start, correlation = NULL, weights = NULL, subset, method = c("ML", "REML"), na.action = na.fail, naPattern, control = list(), verbose= FALSE) { UseMethod("nlme") } nlme.nlsList <- function(model, data = sys.frame(sys.parent()), fixed, random = fixed, groups, start, correlation = NULL, weights = NULL, subset, method = c("ML", "REML"), na.action = na.fail, naPattern, control = list(), verbose= FALSE) { ## control parameters controlvals <- nlmeControl() controlvals[names(control)] <- control thisCall <- as.list(match.call())[-1] ## checking the use of arguments defined within the function if (any(!is.na(match(names(thisCall), c("fixed", "data", "start"))))) { warning("'nlme.nlsList' will redefine 'fixed', 'data', and 'start'") } method <- match.arg(method) REML <- method == "REML" ## add model, data, and optionally groups from the call that created model last.call <- as.list(attr(model, "call"))[-1] last.call$control <- NULL last.call$pool <- NULL thisCall[names(last.call)] <- last.call thisModel <- last.call[["model"]] thisCall[["model"]] <- eval(parse(text=paste( deparse (getResponseFormula (thisModel)[[2]]), c_deparse(getCovariateFormula(thisModel)[[2]]),sep="~"))) ## create "fixed" and "start" cf <- na.omit(coef(model)) start <- list(fixed = unlist(lapply(cf, median, na.rm = TRUE))) pnames <- names(start$fixed) <- names(cf) thisCall[["fixed"]] <- lapply(as.list(pnames), function(el) eval(parse(text = paste(el, 1, sep = "~")))) if (missing(random)) { random <- thisCall[["fixed"]] } reSt <- reStruct(random, data = NULL) if (missing(groups)) { thisCall[["groups"]] <- groups <- getGroupsFormula(model) } if (length(reSt) > 1 || length(groups[[2]]) > 1) { stop("can only fit \"nlsList\" objects with single grouping variable") } ranForm <- formula(reSt)[[1]] if (!is.list(ranForm)) { ranForm <- list(ranForm) } mData <- thisCall[["data"]] if (is.null(mData)) { # will try to construct allV <- unique(unlist(lapply(ranForm, function(el) all.vars(el[[3]])))) if (length(allV) > 0) { alist <- lapply(as.list(allV), as.name) names(alist) <- allV alist <- c(as.list(quote(data.frame)), alist) mode(alist) <- "call" mData <- eval(alist, sys.parent(1)) } } else if (mode(mData) == "name" || mode(mData) == "call") { mData <- eval.parent(mData) } reSt <- reStruct(random, REML = REML, data = mData) names(reSt) <- deparse(groups[[2]]) ## convert list of "name" objects to "character" vector rnames <- sapply(lapply(ranForm, `[[`, 2L), deparse) ## if the random effects are a subset of the coefficients, ## construct initial estimates for their var-cov matrix if (all(match(rnames, pnames, 0))) { madRes <- mad(resid(model), na.rm = TRUE) madRan <- unlist(lapply(cf, mad, na.rm = TRUE)) madRan <- madRan[rnames] if (isInitialized(reSt)) { warning("initial value for 'reStruct' overwritten in 'nlme.nlsList'") } matrix(reSt) <- diag((madRan/madRes)^2, ncol = length(rnames)) } thisCall[["start"]] <- start thisCall[["random"]] <- reSt val <- do.call(nlme.formula, thisCall, envir = parent.frame()) val$origCall <- match.call() val } nlme.formula <- function(model, data = sys.frame(sys.parent()), fixed, random, groups, start, correlation = NULL, weights = NULL, subset, method = c("ML", "REML"), na.action = na.fail, naPattern, control = list(), verbose= FALSE) { ## This is the method that actually does the fitting finiteDiffGrad <- function(model, data, pars) { dframe <- data.frame(data, pars) base <- eval(model, dframe) nm <- colnames(pars) grad <- array(base, c(length(base), length(nm)), list(NULL, nm)) ssize <- sqrt(.Machine$double.eps) for (i in nm) { diff <- pp <- pars[ , i] diff[pp == 0] <- ssize diff[pp != 0] <- pp[pp != 0] * ssize dframe[[i]] <- pp + diff grad[ , i] <- (base - eval(model, dframe))/diff dframe[[i]] <- pp } grad } ## keeping the call Call <- match.call() ## control parameters controlvals <- nlmeControl() if (!missing(control)) { controlvals[names(control)] <- control } ## ## checking arguments ## if (!inherits(model, "formula")) stop("'model' must be a formula") if (length(model)!=3) stop("model formula must be of the form \"resp ~ pred\"") ## if (length(attr(terms(model), "offset"))) ## stop("offset() terms are not supported") method <- match.arg(method) REML <- method == "REML" if (missing(random)) { random <- fixed } reSt <- reStruct(random, REML = REML, data = NULL) if (missing(groups)) { groups <- getGroupsFormula(reSt) } if (is.null(groups)) { if (inherits(data, "groupedData")) { groups <- getGroupsFormula(data) namGrp <- rev(names(getGroupsFormula(data, asList = TRUE))) Q <- length(namGrp) if (length(reSt) != Q) { # may need to repeat reSt if (length(reSt) != 1) stop("incompatible lengths for 'random' and grouping factors") randL <- vector("list", Q) names(randL) <- rev(namGrp) for(i in 1:Q) randL[[i]] <- random reSt <- reStruct(randL, REML = REML, data = NULL) } } else { ## will assume single group groups <- ~ 1 names(reSt) <- namGrp <- "1" } } else { g.exp <- eval(parse(text = paste0("~1 |", deparse(groups[[2]])))) namGrp <- rev(names(getGroupsFormula(g.exp, asList = TRUE))) } names(reSt) <- namGrp ## ## checking if self-starting formula is given ## if (missing(start) && is.call(model[[3]]) && !is.null(attr(eval(model[[3]][[1]]), "initial"))) { nlmeCall <- Call nlsLCall <- nlmeCall[c("","model","data")] nlsLCall[[1]] <- quote(nlme::nlsList) nm <- names(nlmeCall) for(i in c("fixed", "data", "groups", "start")) if(i %in% nm) nlmeCall[[i]] <- NULL nlmeCall[[1]] <- quote(nlme::nlme.nlsList) ## checking if "data" is not equal to sys.frame(sys.parent()) if (is.null(dim(data))) { stop("'data' must be given explicitly to use 'nlsList'") } nlsLObj <- eval.parent(nlsLCall) nlmeCall[["model"]] <- nlsLObj val <- eval.parent(nlmeCall) val$origCall <- NULL return(val) } if (is.numeric(start)) { # assume it is the fixed effects start <- list(fixed = start) } nlmeModel <- call("-", model[[2]], model[[3]]) ## ## save writing list(...) when only one element ## if (!is.list(fixed)) fixed <- list(fixed) fixed <- do.call(c, lapply(fixed, function(fix.i) { if (is.name(fix.i[[2]])) list(fix.i) else ## multiple parameters on left hand side eval(parse(text = paste0("list(", paste(paste(all.vars(fix.i[[2]]), deparse (fix.i[[3]]), sep = "~"), collapse = ","), ")"))) })) fnames <- lapply(fixed, function(fix.i) { this <- eval(fix.i) if (!inherits(this, "formula")) stop ("'fixed' must be a formula or list of formulae") if (length(this) != 3) stop ("formulae in 'fixed' must be of the form \"parameter ~ expr\"") if (!is.name(this[[2]])) stop ("formulae in 'fixed' must be of the form \"parameter ~ expr\"") as.character(this[[2]]) }) names(fixed) <- fnames ranForm <- formula(reSt) # random effects formula(s) Q <- length(ranForm) # number of groups names(ranForm) <- namGrp rnames <- vector("list", Q) names(rnames) <- namGrp for(i in 1:Q) { rnames[[i]] <- character(length(ranForm[[i]])) for (j in seq_along(ranForm[[i]])) { this <- eval(ranForm[[i]][[j]]) if (!inherits(this, "formula")) stop ("'random' must be a formula or list of formulae") if (length(this) != 3) stop ("formulae in 'random' must be of the form \"parameter ~ expr\"") if (!is.name(this[[2]])) stop ("formulae in 'random' must be of the form \"parameter ~ expr\"") rnames[[i]][j] <- deparse(this[[2]]) } names(ranForm[[i]]) <- rnames[[i]] } ## all parameter names pnames <- unique(c(fnames, unlist(rnames))) ## ## If data is a pframe, copy the parameters in the frame to frame 1 ## Doesn't exist in R ## if (inherits(data, "pframe")) { ## pp <- parameters(data) ## for (i in names(pp)) { ## assign(i, pp[[i]]) ## } ## attr(data,"parameters") <- NULL ## class(data) <- "data.frame" ## } ## check if corStruct is present and assign groups to its formula, ## if necessary if (!is.null(correlation)) { if(!is.null(corGrpsForm <- getGroupsFormula(correlation, asList = TRUE))) { corGrpsForm <- unlist(lapply(corGrpsForm, function(el) deparse(el[[2]]))) corQ <- length(corGrpsForm) lmeGrpsForm <- unlist(lapply(splitFormula(groups), function(el) deparse(el[[2]]))) lmeQ <- length(lmeGrpsForm) if (corQ <= lmeQ) { if (any(corGrpsForm != lmeGrpsForm[1:corQ])) { stop("incompatible formulas for groups in \"random\" and \"correlation\"") } if (corQ < lmeQ) { warning("cannot use smaller level of grouping for \"correlation\" than for \"random\". Replacing the former with the latter.") frm <- paste("~", c_deparse(getCovariateFormula(formula(correlation))[[2]]), "|", deparse(groups[[2]])) attr(correlation, "formula") <- eval(parse(text = frm)) } } else { if (any(lmeGrpsForm != corGrpsForm[1:lmeQ])) { stop("incompatible formulas for groups in \"random\" and \"correlation\"") } } } else { ## using the same grouping as in random frm <- paste("~", c_deparse(getCovariateFormula(formula(correlation))[[2]]), "|", deparse(groups[[2]])) attr(correlation, "formula") <- eval(parse(text = frm)) corQ <- lmeQ <- 1 } } else { corQ <- lmeQ <- 1 } ## create an nlme structure containing the random effects model and plug-ins nlmeSt <- nlmeStruct(reStruct = reSt, corStruct = correlation, varStruct = varFunc(weights)) ## extract a data frame with enough information to evaluate ## form, fixed, random, groups, correlation, and weights mfArgs <- list(formula = asOneFormula(formula(nlmeSt), model, fixed, groups, omit = c(pnames, "pi")), data = data, na.action = na.action) if (!missing(subset)) { mfArgs[["subset"]] <- asOneSidedFormula(Call[["subset"]])[[2]] } mfArgs$drop.unused.levels <- TRUE dataMix <- do.call(model.frame, mfArgs) origOrder <- row.names(dataMix) # preserve the original order ## ## Evaluating the groups expression ## grps <- getGroups(dataMix, eval(parse(text = paste("~1", deparse(groups[[2]]), sep = "|")))) N <- dim(dataMix)[1] # number of observations ## ## evaluating the naPattern expression, if any ## if (missing(naPattern)) naPat <- rep(TRUE, N) else naPat <- as.logical(eval(asOneSidedFormula(naPattern)[[2]], dataMix)) origOrderShrunk <- origOrder[naPat] ## ordering data by groups if (inherits(grps, "factor")) { # single level ord <- order(grps) #"order" treats a single named argument peculiarly grps <- data.frame(grps) row.names(grps) <- origOrder names(grps) <- as.character(deparse((groups[[2]]))) } else { ord <- do.call(order, grps) ## making group levels unique for(i in 2:ncol(grps)) { grps[, i] <- as.factor(paste(as.character(grps[, i-1]), as.character(grps[,i]), sep = "/")) } } if (corQ > lmeQ) { ## may have to reorder by the correlation groups ord <- do.call(order, getGroups(dataMix, getGroupsFormula(correlation))) } grps <- grps[ord, , drop = FALSE] dataMix <- dataMix[ord, ,drop = FALSE] ## revOrder <- match(origOrder, row.names(dataMix)) # putting in orig. order naPat <- naPat[ord] # ordered naPat dataMixShrunk <- dataMix[naPat, , drop=FALSE] ## ordShrunk <- ord[naPat] grpShrunk <- grps[naPat,, drop = FALSE] revOrderShrunk <- match(origOrderShrunk, row.names(dataMixShrunk)) yShrunk <- eval(model[[2]], dataMixShrunk) ## ## defining list with parameter information ## contr <- list() plist <- vector("list", length(pnames)) names(plist) <- pnames for (nm in pnames) { this <- list(fixed = !is.null(fixed[[nm]]), random = as.list(lapply(ranForm, function(el, nm) !is.null(el[[nm]]), nm = nm))) if (this[["fixed"]]) { ## Peter Dalgaard claims the next line should be this[["fixed"]][[3]][[1]] != "1" ## but the current version seems to work ok. if (fixed[[nm]][[3]] != "1") { as1F <- asOneSidedFormula(fixed[[nm]][[3]]) this[["fixed"]] <- model.matrix(as1F, model.frame(as1F, dataMix)) auxContr <- attr(this[["fixed"]], "contrasts") contr <- c(contr, auxContr[is.na(match(names(auxContr), names(contr)))]) } } if (any(unlist(this[["random"]]))) { for(i in 1:Q) { wch <- which(!is.na(match(rnames[[i]], nm))) if (length(wch) == 1) { # only one formula for nm at level i if ((rF.i <- ranForm[[i]][[nm]][[3]]) != "1") { this[["random"]][[i]] <- model.matrix(asOneSidedFormula(rF.i), model.frame(asOneSidedFormula(rF.i), dataMix)) auxContr <- attr(this[["random"]][[i]], "contrasts") contr <- c(contr, auxContr[is.na(match(names(auxContr), names(contr)))]) } } else if (length(wch) > 0) { # multiple formulas this[["random"]][[i]] <- th.ran.i <- as.list(lapply(ranForm[[i]][wch], function(el, data) { if (el[[3]] == "1") TRUE else model.matrix(asOneSidedFormula(el[[3]]), model.frame(asOneSidedFormula(el[[3]]), data)) }, data = dataMix)) for(j in seq_along(th.ran.i)) { if (is.matrix(th.ran.i[[j]])) { auxContr <- attr(th.ran.i[[j]], "contrasts") contr <- c(contr, auxContr[is.na(match(names(auxContr), names(contr)))]) } } } } } plist[[nm]] <- this } ## Ensure that all elements of are matrices contrMat <- function(nm, contr, data) { ## nm is a term in a formula, and can be a call x <- eval(parse(text=nm), data) levs <- levels(x) val <- do.call(contr[[nm]], list(n = length(levs))) rownames(val) <- levs val } nms <- names(contr)[sapply(contr, is.character)] contr[nms] <- lapply(nms, contrMat, contr = contr, data = dataMix) if (is.null(sfix <- start$fixed)) stop ("'start' must have a component called 'fixed'") ## ## Fixed effects names ## fn <- character(0) currPos <- 0 fixAssign <- list() for(nm in fnames) { if (is.logical(f <- plist[[nm]]$fixed)) { currPos <- currPos + 1 currVal <- list(currPos) if (all(unlist(lapply(plist[[nm]]$random, is.logical)))) { fn <- c(fn, nm) names(currVal) <- nm } else { aux <- paste(nm, "(Intercept)", sep=".") fn <- c(fn, aux) names(currVal) <- aux } fixAssign <- c(fixAssign, currVal) } else { currVal <- attr(f, "assign") fTerms <- terms(asOneSidedFormula(fixed[[nm]][[3]]), data=data) namTerms <- attr(fTerms, "term.labels") if (attr(fTerms, "intercept") > 0) { namTerms <- c("(Intercept)", namTerms) } namTerms <- factor(currVal, labels = namTerms) currVal <- split(order(currVal), namTerms) names(currVal) <- paste(nm, names(currVal), sep = ".") fixAssign <- c(fixAssign, lapply(currVal, function(el) el + currPos )) currPos <- currPos + length(unlist(currVal)) fn <- c(fn, paste(nm, colnames(f), sep = ".")) } } fLen <- length(fn) if (fLen == 0L || length(sfix) != fLen) stop ("starting values for the 'fixed' component are not the correct length") names(sfix) <- fn ## ## Random effects names ## rn <- wchRnames <- vector("list", Q) names(rn) <- names(wchRnames) <- namGrp for(i in 1:Q) { rn[[i]] <- character(0) uRnames <- unique(rnames[[i]]) wchRnames[[i]] <- integer(length(uRnames)) names(wchRnames[[i]]) <- uRnames for(j in seq_along(rnames[[i]])) { nm <- rnames[[i]][j] wchRnames[[i]][nm] <- wchRnames[[i]][nm] + 1 r <- plist[[nm]]$random[[i]] if(is.list(r)) r <- r[[wchRnames[[i]][nm]]] if (is.logical(r)) { if (r) { rn[[i]] <- c(rn[[i]], if (is.logical(plist[[nm]]$fixed)) nm else paste(nm,"(Intercept)",sep=".")) } # else: keep rn[[i]] } else { rn[[i]] <- c(rn[[i]], paste(nm, colnames(r), sep = ".")) } } } Names(nlmeSt$reStruct) <- rn rNam <- unlist(rn) # unlisted names of random effects rlength <- lengths(rn) # number of random effects per stratum rLen <- sum(rlength) # total number of random effects pLen <- rLen + fLen # total number of parameters ncols <- c(rlength, fLen, 1) Dims <- MEdims(grpShrunk, ncols) if (max(Dims$ZXlen[[1]]) < Dims$qvec[1]) { warning(gettextf("fewer observations than random effects in all level %s groups", Q), domain = NA) } sran <- vector("list", Q) names(sran) <- namGrp if (!is.null(sran0 <- start$random)) { if (inherits(sran0, "data.frame")) { sran0 <- list(as.matrix(sran0)) } else { if (!is.list(sran0)) { if (!is.matrix(sran0)) { stop("starting values for random effects should be a list, or a matrix") } sran0 <- list(as.matrix(sran0)) } } if (is.null(namSran <- names(sran0))) { if (length(sran) != Q) { stop(gettextf("list with starting values for random effects must have names or be of length %d", Q), domain = NA) } names(sran0) <- rev(namGrp) # assume given in outer-inner order } else { if (any(noMatch <- is.na(match(namSran, namGrp)))) { stop(sprintf(ngettext(sum(noMatch), "group name not matched in starting values for random effects: %s", "group names not matched in starting values for random effects: %s"), paste(namSran[noMatch], collapse=", ")), domain = NA) } } } for(i in 1:Q) { if (is.null(sran[[i]] <- sran0[[namGrp[i]]])) { sran[[i]] <- array(0, c(rlength[i], Dims$ngrps[i]), list(rn[[i]], unique(as.character(grps[, Q-i+1])))) } else { if (!is.matrix(sran[[i]])) stop("starting values for the random components should be a list of matrices") dimsran <- dim(sran[[i]]) if (dimsran[1] != Dims$ngrps[i]) { stop(gettextf("number of rows in starting values for random component at level %s should be %d", namGrp[i], Dims$ngrps[i]), domain = NA) } if (dimsran[2] != rlength[i]) { stop(gettextf("number of columns in starting values for random component at level %s should be %d", namGrp[i], rlength[i]), domain = NA) } dnamesran <- dimnames(sran[[i]]) if (is.null(dnamesran[[1]])) { stop("starting values for random effects must include group levels") } else { levGrps <- unique(as.character(grps[, Q-i+1])) if(!all(sort(dnamesran[[1]]) == sort(levGrps))) { stop(gettextf("groups levels mismatch in 'random' and starting values for 'random' at level %s", namGrp[i]), domain = NA) } sran[[i]] <- sran[[i]][levGrps, , drop = FALSE] } if (!is.null(dnamesran[[2]])) { if(!all(sort(dnamesran[[2]]) == sort(rn[[i]]))) { ## first try to resolve it for(j in seq_len(rlength[i])) { if (is.na(match(dnamesran[[2]][j], rn[[i]]))) { if (!is.na(mDn <- match(paste(dnamesran[[2]][j], "(Intercept)", sep="."), rn[[i]]))) { dnamesran[[2]][j] <- rn[[i]][mDn] } else { if (!is.na(mDn <- match(dnamesran[[2]][j], paste(rn[[i]], "(Intercept)", sep = ".")))) { dnamesran[[2]][j] <- rn[[i]][mDn] } else { stop (gettextf("names mismatch in 'random' and starting values for 'random' at level %s", namGrp[i]), domain = NA) } } } } dimnames(sran[[i]]) <- dnamesran } sran[[i]] <- sran[[i]][, rn[[i]], drop = FALSE] } else { dimnames(sran[[i]])[[2]] <- rn[[i]] } sran[[i]] <- t(sran[[i]]) } } names(sran) <- namGrp nPars <- length(unlist(sran)) + fLen # total number of PNLS parameters ## ## defining values of constants used in calculations ## NReal <- sum(naPat) ## ## Creating the fixed and random effects maps ## fmap <- list() n1 <- 1 for(nm in fnames) { if (is.logical(f <- plist[[nm]]$fixed)) { fmap[[nm]] <- n1 n1 <- n1 + 1 } else { fmap[[nm]] <- n1:(n1+ncol(f) - 1) n1 <- n1 + ncol(f) } } rmap <- rmapRel <- vector("list", Q) names(rmap) <- names(rmapRel) <- namGrp n1 <- 1 startRan <- 0 for(i in 1:Q) { wchRnames[[i]][] <- 0 rmap[[i]] <- rmapRel[[i]] <- list() for(nm in rnames[[i]]) { wchRnames[[i]][nm] <- wchRnames[[i]][nm] + 1 r <- plist[[nm]]$random[[i]] if(is.list(r)) r <- r[[wchRnames[[i]][nm]]] if (is.logical(r)) { val <- n1 n1 <- n1 + 1 } else { val <- n1:(n1+ncol(r) - 1) n1 <- n1 + ncol(r) } if (is.null(rmap[[i]][[nm]])) { rmap[[i]][[nm]] <- val rmapRel[[i]][[nm]] <- val - startRan } else { rmap[[i]][[nm]] <- c(rmap[[i]][[nm]], list(val)) rmapRel[[i]][[nm]] <- c(rmapRel[[i]][[nm]], list(val - startRan)) } } startRan <- startRan + ncols[i] } ## ## defining the nlFrame, i.e., nlEnv, an environment in R : ## grpsRev <- rev(lapply(grps, as.character)) bmap <- c(0, cumsum(unlist(lapply(sran, function(el) length(as.vector(el)))))) nlEnv <- list2env( list(model = nlmeModel, data = dataMix, groups = grpsRev, plist = plist, beta = as.vector(sfix), bvec = unlist(sran), b = sran, X = array(0, c(N, fLen), list(NULL, fn)), Z = array(0, c(N, rLen), list(NULL, rNam)), fmap = fmap, rmap = rmap, rmapRel = rmapRel, bmap = bmap, level = Q, N = N, Q = Q, naPat = naPat, .parameters = c("bvec", "beta"), finiteDiffGrad = finiteDiffGrad)) modelExpression <- ~ { pars <- getParsNlme(plist, fmap, rmapRel, bmap, groups, beta, bvec, b, level, N) res <- eval(model, data.frame(data, pars)) if (!length(grad <- attr(res, "gradient"))) { grad <- finiteDiffGrad(model, data, pars) } for (nm in names(plist)) { gradnm <- grad[, nm] if (is.logical(f <- plist[[nm]]$fixed)) { if(f) X[, fmap[[nm]]] <- gradnm # else f == FALSE =^= 0 } else X[, fmap[[nm]]] <- gradnm * f for(i in 1:Q) { if (is.logical(r <- plist[[nm]]$random[[i]])) { if (r) Z[, rmap[[i]][[nm]]] <- gradnm # else r == FALSE =^= 0 } else { rm.i <- rmap[[i]][[nm]] if (!is.list(rm.i)) { Z[, rm.i] <- gradnm * r } else { for(j in seq_along(rm.i)) { Z[, rm.i[[j]]] <- if (is.logical(rr <- r[[j]])) gradnm else gradnm * rr } } } } } result <- c(Z[naPat, ], X[naPat, ], res[naPat]) result[is.na(result)] <- 0 result }## {modelExpression} modelResid <- ~ eval(model, data.frame(data, getParsNlme(plist, fmap, rmapRel, bmap, groups, beta, bvec, b, level, N)))[naPat] ww <- eval(modelExpression[[2]], envir = nlEnv) w <- ww[NReal * pLen + (1:NReal)] ZX <- array(ww[1:(NReal*pLen)], c(NReal, pLen), list(row.names(dataMixShrunk), c(rNam, fn))) w <- w + as.vector(ZX[, rLen + (1:fLen), drop = FALSE] %*% sfix) if (!is.null(start$random)) { startRan <- 0 for(i in 1:Q) { w <- w + as.vector((ZX[, startRan + 1:ncols[i], drop = FALSE] * t(sran[[i]])[as.character(grpShrunk[, Q-i+1]),,drop = FALSE]) %*% rep(1, ncols[i])) startRan <- startRan + ncols[i] } } ## creating the condensed linear model attr(nlmeSt, "conLin") <- list(Xy = array(c(ZX, w), dim = c(NReal, sum(ncols)), dimnames = list(row.names(dataMixShrunk), c(colnames(ZX), deparse(model[[2]])))), dims = Dims, logLik = 0, ## 17-11-2015; Fixed sigma patch; SH Heisterkamp; Quantitative Solutions sigma = controlvals$sigma, auxSigma = 0) ## additional attributes of nlmeSt attr(nlmeSt, "resp") <- yShrunk attr(nlmeSt, "model") <- modelResid attr(nlmeSt, "local") <- nlEnv attr(nlmeSt, "NReal") <- NReal ## initialization nlmeSt <- Initialize(nlmeSt, dataMixShrunk, grpShrunk, control = controlvals) parMap <- attr(nlmeSt, "pmap") decomp <- length(nlmeSt) == 1 && !needUpdate(nlmeSt) if(decomp) { # can do one decomposition ## need to save conLin for calculating updating between steps oldConLin <- attr(nlmeSt, "conLin") } numIter <- 0 # number of iterations pnlsSettings <- c(controlvals$pnlsMaxIter, controlvals$minScale, controlvals$pnlsTol, 0, 0, 0) nlModel <- nonlinModel(modelExpression, nlEnv) ##---------------------------------------------------------------------------- repeat { ## alternating algorithm numIter <- numIter + 1 ## LME step if (needUpdate(nlmeSt)) { # updating varying weights nlmeSt <- update(nlmeSt, dataMixShrunk) } if (decomp) { attr(nlmeSt, "conLin") <- MEdecomp(oldConLin) } oldPars <- coef(nlmeSt) if (controlvals$opt == "nlminb") { control <- list(trace = controlvals$msVerbose, iter.max = controlvals$msMaxIter) keep <- c("eval.max", "abs.tol", "rel.tol", "x.tol", "xf.tol", "step.min", "step.max", "sing.tol", "scale.init", "diff.g") control <- c(control, controlvals[names(controlvals) %in% keep]) optRes <- nlminb(c(coef(nlmeSt)), function(nlmePars) -logLik(nlmeSt, nlmePars), control = control) aConv <- coef(nlmeSt) <- optRes$par convIter <- optRes$iterations if(optRes$convergence && controlvals$msWarnNoConv) warning(paste(sprintf( "Iteration %d, LME step: nlminb() did not converge (code = %d).", numIter, optRes$convergence), if(convIter >= control$iter.max) "Do increase 'msMaxIter'!" else if(!is.null(msg <- optRes$message)) paste("PORT message:", msg))) } else { ## nlm(.) aNlm <- nlm(f = function(nlmePars) -logLik(nlmeSt, nlmePars), p = c(coef(nlmeSt)), hessian = TRUE, print.level = controlvals$msVerbose, gradtol = if(numIter == 1) controlvals$msTol else 100*.Machine$double.eps, iterlim = if(numIter < 10) 10 else controlvals$msMaxIter, check.analyticals = FALSE) aConv <- coef(nlmeSt) <- aNlm$estimate convIter <- aNlm$iterations if(aNlm$code > 2 && controlvals$msWarnNoConv) warning(paste(sprintf( "Iteration %d, LME step: nlm() did not converge (code = %d).", numIter, aNlm$code), if(aNlm$code == 4) "Do increase 'msMaxIter'!")) } nlmeFit <- attr(nlmeSt, "lmeFit") <- MEestimate(nlmeSt, grpShrunk) if (verbose) { cat("\n**Iteration", numIter) cat(sprintf("\nLME step: Loglik: %s, %s iterations: %d\n", format(nlmeFit$logLik), controlvals$opt, convIter)) print(nlmeSt) } ## PNLS step if (is.null(correlation)) { cF <- 1.0 cD <- 1 } else { cF <- corFactor(nlmeSt$corStruct) cD <- Dim(nlmeSt$corStruct) } vW <- if(is.null(weights)) 1.0 else varWeights(nlmeSt$varStruct) if (verbose) cat(" Beginning PNLS step: .. ") work <- .C(fit_nlme, thetaPNLS = as.double(c(as.vector(unlist(sran)), sfix)), pdFactor = as.double(pdFactor(nlmeSt$reStruct)), as.integer(unlist(rev(grpShrunk))), as.integer(unlist(Dims)), as.integer(attr(nlmeSt$reStruct, "settings"))[-(1:3)], as.double(cF), as.double(vW), as.integer(unlist(cD)), settings = as.double(pnlsSettings), additional = double(NReal * (pLen + 1)), as.integer(!is.null(correlation)), as.integer(!is.null(weights)), ## 17-11-2015; Fixed sigma patch; SH Heisterkamp; Quantitative Solutions as.double(controlvals$sigma), nlModel, NAOK = TRUE) if (verbose) cat(" completed fit_nlme() step.\n") if (work$settings[4] == 1) { ## convResult <- 2 msg <- "step halving factor reduced below minimum in PNLS step" if (controlvals$returnObject) warning(msg) else stop(msg) } ## dim(work$pdFactor) <- dim(pdMatrix(nlmeSt$reStruct[[1]])) ## matrix(nlmeSt$reStruct[[1]]) <- crossprod(work$pdFactor) ## fix from Setzer.Woodrow@epamail.epa.gov for nested grouping factors i.pdF <- 1 for (i in seq_along(nlmeSt$reStruct)) { d.i <- dim(pdMatrix(nlmeSt$reStruct[[i]])) ni.pdF <- i.pdF + prod(d.i) pdF <- array(work$pdFactor[i.pdF:(ni.pdF -1)], dim = d.i) matrix(nlmeSt$reStruct[[i]]) <- crossprod(pdF) i.pdF <- ni.pdF } oldPars <- c(sfix, oldPars) for(i in 1:Q) sran[[i]][] <- work$thetaPNLS[(bmap[i]+1):bmap[i+1]] sfix[] <- work$thetaPNLS[nPars + 1 - (fLen:1)] if (verbose) { cat("PNLS step: RSS = ", format(work$settings[6]), "\n fixed effects: ") for (i in 1:fLen) cat(format(sfix[i])," ") cat("\n iterations:", work$settings[5],"\n") } aConv <- c(sfix, coef(nlmeSt)) # 2nd part added by SDR 04/19/2002 w[] <- work$additional[(NReal * pLen) + 1:NReal] ZX[] <- work$additional[1:(NReal * pLen)] w <- w + as.vector(ZX[, rLen + (1:fLen), drop = FALSE] %*% sfix) startRan <- 0 for(i in 1:Q) { gr.i <- as.character(grpShrunk[, Q-i+1]) w <- w + as.vector((ZX[, startRan + 1:ncols[i], drop = FALSE] * t(sran[[i]])[gr.i,, drop = FALSE]) %*% rep(1, ncols[i])) startRan <- startRan + ncols[i] } if (decomp) { oldConLin$Xy[] <- c(ZX, w) oldConLin$logLik <- 0 } else { attr(nlmeSt, "conLin")$Xy[] <- c(ZX, w) attr(nlmeSt, "conLin")$logLik <- 0 } conv <- abs((oldPars - aConv)/ ifelse(abs(aConv) < controlvals$tolerance, 1, aConv)) aConv <- c(fixed = max(conv[1:fLen])) conv <- conv[-(1:fLen)] for(i in names(nlmeSt)) { if (any(parMap[,i])) { aConv <- c(aConv, max(conv[parMap[,i]])) names(aConv)[length(aConv)] <- i } } if (verbose) { cat(sprintf("Convergence crit. (must all become <= tolerance = %g):\n", controlvals$tolerance)) print(aConv) } if ((max(aConv) <= controlvals$tolerance) || (aConv["fixed"] <= controlvals$tolerance && convIter == 1)) { ## convResult <- 0 break } if (numIter >= controlvals$maxIter) { ## convResult <- 1 msg <- gettextf( "maximum number of iterations (maxIter = %d) reached without convergence", controlvals$maxIter) if (controlvals$returnObject) { warning(msg, domain=NA) ; break } else stop(msg, domain=NA) } } ## end{ repeat } (nlme steps) ---------------------------------------------- ## wrapping up nlmeFit <- if (decomp) MEestimate(nlmeSt, grpShrunk, oldConLin) else MEestimate(nlmeSt, grpShrunk) ## degrees of freedom for fixed effects tests fixDF <- getFixDF(ZX[, rLen + (1:fLen), drop = FALSE], grpShrunk, attr(nlmeSt, "conLin")$dims$ngrps, fixAssign) ## 17-11-2015; Fixed sigma patch; SH Heisterkamp; Quantitative Solutions attr(fixDF, "varFixFact") <- varFix <- nlmeFit$sigma * nlmeFit$varFix varFix <- crossprod(varFix) dimnames(varFix) <- list(fn, fn) ## ## fitted.values and residuals (in original order) ## Resid <- if (decomp) resid(nlmeSt, level = 0:Q, oldConLin)[revOrderShrunk, ] else resid(nlmeSt, level = 0:Q)[revOrderShrunk, ] Fitted <- yShrunk[revOrderShrunk] - Resid rownames(Resid) <- rownames(Fitted) <- origOrderShrunk grpShrunk <- grpShrunk[revOrderShrunk, , drop = FALSE] attr(Resid, "std") <- nlmeFit$sigma/(varWeights(nlmeSt)[revOrderShrunk]) ## inverting back reStruct nlmeSt$reStruct <- solve(nlmeSt$reStruct) attr(nlmeSt, "fixedSigma") <- (controlvals$sigma > 0) ## saving part of dims dims <- attr(nlmeSt, "conLin")$dims[c("N", "Q", "qvec", "ngrps", "ncol")] ## getting the approximate var-cov of the parameters apVar <- if (controlvals$apVar) lmeApVar(nlmeSt, nlmeFit$sigma, .relStep = controlvals[[".relStep"]], minAbsPar = controlvals[["minAbsParApVar"]], natural = controlvals[["natural"]]) else "Approximate variance-covariance matrix not available" ## putting sran in the right format sran <- lapply(sran, t) ## getting rid of condensed linear model, fit, and other attributes ###- oClass <- class(nlmeSt) attributes(nlmeSt) <- attributes(nlmeSt)[ c("names", "class", "pmap", "fixedSigma")] ##- class(nlmeSt) <- oClass ## ## creating the nlme object ## isGrpd <- inherits(data, "groupedData") structure(class = c("nlme", "lme"), list(modelStruct = nlmeSt, dims = dims, contrasts = contr, coefficients = list(fixed = sfix, random = rev(sran)), varFix = varFix, sigma = nlmeFit$sigma, apVar = apVar, logLik = nlmeFit$logLik, numIter = numIter, groups = grpShrunk, call = Call, method = method, fitted = Fitted, residuals = Resid, plist = plist, map = list(fmap=fmap,rmap=rmap,rmapRel=rmapRel,bmap=bmap), fixDF = fixDF , formula = model # => reliable formula.nlme() [PR#18599] ), ## saving labels and units for plots units = if(isGrpd) attr(data, "units"), labels = if(isGrpd) attr(data, "labels")) } ## {nlme.formula} ### ##' Calculate the parameters from the fixed and random effects getParsNlme <- function(plist, fmap, rmapRel, bmap, groups, beta, bvec, b, level, N) { pars <- array(0, c(N, length(plist)), list(NULL, names(plist))) ## for random effects below iQ <- if (level > 0) { Q <- length(groups) (Q - level + 1L):Q } else integer() # empty for (nm in names(plist)) { ## 1) Fixed effects if (is.logical(f <- plist[[nm]]$fixed)) { if (f) pars[, nm] <- beta[fmap[[nm]]] ## else pars[, nm] <- 0 (as f == FALSE) } else pars[, nm] <- f %*% beta[fmap[[nm]]] ## 2) Random effects for(i in iQ) if(!is.null(rm.i. <- rmapRel[[i]][[nm]])) { b.i <- b[[i]] b.i[] <- bvec[(bmap[i] + 1):bmap[i+1]] ## NB: some groups[[i]] may be *new* levels, i.e. non-matching: gr.i <- match(groups[[i]], colnames(b.i)) # column numbers + NA if (is.logical(r <- plist[[nm]]$random[[i]])) { if (r) pars[, nm] <- pars[, nm] + b.i[rm.i., gr.i] ## else r == FALSE =^= 0 } else if (!is.list(r)) { pars[, nm] <- pars[, nm] + (r * t(b.i)[gr.i, rm.i., drop=FALSE]) %*% rep(1, ncol(r)) } else { b.i.gi <- b.i[, gr.i, drop=FALSE] for(j in seq_along(rm.i.)) { if (is.logical(rr <- r[[j]])) { if(rr) pars[, nm] <- pars[, nm] + b.i.gi[rm.i.[[j]], ] ## else rr == FALSE =^= 0 } else pars[, nm] <- pars[, nm] + (rr * t(b.i.gi[rm.i.[[j]], , drop=FALSE])) %*% rep(1, ncol(rr)) } } } # for( i ) if(!is.null(rm.i. ..)) } pars } ### ### Methods for standard generics ### formula.nlme <- function(x, ...) x$formula %||% eval(x$call[["model"]]) predict.nlme <- function(object, newdata, level = Q, asList = FALSE, na.action = na.fail, naPattern = NULL, ...) { ## ## method for predict() designed for objects inheriting from class nlme ## Q <- object$dims$Q if (missing(newdata)) { # will return fitted values val <- fitted(object, level, asList) if (length(level) == 1) return(val) return(data.frame(object[["groups"]][,level[level != 0], drop = FALSE], predict = val)) } maxQ <- max(level) # maximum level for predictions nlev <- length(level) newdata <- as.data.frame(newdata) if (maxQ > 0) { # predictions with random effects whichQ <- Q - (maxQ-1):0 reSt <- object$modelStruct$reStruct[whichQ] ## nlmeSt <- nlmeStruct(reStruct = reSt) groups <- getGroupsFormula(reSt) if (any(is.na(match(all.vars(groups), names(newdata))))) { ## groups cannot be evaluated in newdata stop("cannot evaluate groups for desired levels on 'newdata'") } } else { reSt <- NULL } ## use xlev to make sure factor levels are the same as in contrasts ## and to support character-type 'newdata' for factors contr <- object$contrasts dataMix <- model.frame(formula = asOneFormula( formula(object), object$call$fixed, formula(reSt), naPattern, omit = c(names(object$plist), "pi", deparse(getResponseFormula(object)[[2]]))), data = newdata, na.action = na.action, drop.unused.levels = TRUE, xlev = lapply(contr, rownames)) origOrder <- row.names(dataMix) # preserve the original order whichRows <- match(origOrder, row.names(newdata)) if (maxQ > 0) { ## sort the model.frame by groups and get the matrices and parameters ## used in the estimation procedures grps <- getGroups(newdata, # (unused levels are dropped here) eval(substitute(~ 1 | GRPS, list(GRPS = groups[[2]])))) ## ordering data by groups if (inherits(grps, "factor")) { # single level grps <- grps[whichRows] oGrps <- data.frame(grps) ## checking if there are missing groups if (any(naGrps <- is.na(grps))) { grps[naGrps] <- levels(grps)[1L] # input with existing level } ord <- order(grps) #"order" treats a single named argument peculiarly grps <- data.frame(grps) row.names(grps) <- origOrder names(grps) <- names(oGrps) <- as.character(deparse((groups[[2L]]))) } else { grps <- oGrps <- grps[whichRows, ] ## checking for missing groups if (any(naGrps <- is.na(grps))) { ## need to input missing groups for(i in names(grps)) { grps[naGrps[, i], i] <- levels(grps[,i])[1L] } naGrps <- t(apply(naGrps, 1, cumsum)) # propagating NAs } ord <- do.call(order, grps) ## making group levels unique for(i in 2:ncol(grps)) { grps[, i] <- as.factor(paste(as.character(grps[, i-1]), as.character(grps[, i ]), sep = "/")) } } ## if (match(0, level, nomatch = 0)) { ## naGrps <- cbind(FALSE, naGrps) ## } ## naGrps <- as.matrix(naGrps)[ord, , drop = FALSE] naGrps <- cbind(FALSE, naGrps)[ord, , drop = FALSE] grps <- grps[ord, , drop = FALSE] dataMix <- dataMix[ord, ,drop = FALSE] revOrder <- match(origOrder, row.names(dataMix)) # putting in orig. order } ## restore contrasts for the below model.matrix() calls (which may use ## different factor variables, so we avoid passing the whole contr list) for(i in intersect(names(dataMix), names(contr))) { attr(dataMix[[i]], "contrasts") <- contr[[i]] } N <- nrow(dataMix) ## ## evaluating the naPattern expression, if any ## naPat <- if(is.null(naPattern)) rep(TRUE, N) else as.logical(eval(asOneSidedFormula(naPattern)[[2]], dataMix)) ## ## Getting the plist for the new data frame ## plist <- object$plist fixed <- eval(object$call$fixed) if (!is.list(fixed)) fixed <- list(fixed) fixed <- do.call(c, lapply(fixed, function(fix.i) { if (is.name(fix.i[[2]])) list(fix.i) else ## multiple parameters on left hand side eval(parse(text = paste0("list(", paste(paste(all.vars(fix.i[[2]]), deparse (fix.i[[3]]), sep = "~"), collapse = ","), ")"))) })) fnames <- unlist(lapply(fixed, function(el) deparse(el[[2]]))) names(fixed) <- fnames fix <- fixef(object) ## fn <- names(fix) for(nm in fnames) { if (!is.logical(plist[[nm]]$fixed)) { oSform <- asOneSidedFormula(fixed[[nm]][[3]]) plist[[nm]]$fixed <- model.matrix(oSform, model.frame(oSform, dataMix)) } } if (maxQ > 0) { grpsRev <- lapply(rev(grps), as.character) ranForm <- formula(reSt)[whichQ] namGrp <- names(ranForm) rnames <- lapply(ranForm, function(el) unlist(lapply(el, function(el1) deparse(el1[[2]])))) for(i in seq_along(ranForm)) { names(ranForm[[i]]) <- rnames[[i]] } ran <- ranef(object) ran <- if(is.data.frame(ran)) list(ran) else rev(ran) ## rn <- lapply(ran[whichQ], names) ran <- lapply(ran, t) ranVec <- unlist(ran) for(nm in names(plist)) { for(i in namGrp) { if (!is.logical(plist[[nm]]$random[[i]])) { wch <- which(!is.na(match(rnames[[i]], nm))) plist[[nm]]$random[[i]] <- if (length(wch) == 1) { # only one formula for nm oSform <- asOneSidedFormula(ranForm[[i]][[nm]][[3]]) model.matrix(oSform, model.frame(oSform, dataMix)) } else { # multiple formulae lapply(ranForm[[i]][wch], function(el) { if (el[[3]] == "1") { TRUE } else { oSform <- asOneSidedFormula(el[[3]]) model.matrix(oSform, model.frame(oSform, dataMix)) } }) } } } } } else { namGrp <- "" grpsRev <- ranVec <- ran <- NULL } val <- vector("list", nlev) modForm <- getCovariateFormula(object)[[2]] omap <- object$map for(i in 1:nlev) { val[[i]] <- eval(modForm, data.frame(dataMix, getParsNlme(plist, omap$fmap, omap$rmapRel, omap$bmap, grpsRev, fix, ranVec, ran, level[i], N)))[naPat] } names(val) <- c("fixed", rev(namGrp))[level + 1] val <- as.data.frame(val) if (maxQ > 0) { val <- val[revOrder, , drop = FALSE] if (any(naGrps)) { val[naGrps] <- NA } } ## putting back in original order if (maxQ > 1) { # making groups unique for(i in 2:maxQ) oGrps[, i] <- as.factor(paste(as.character(oGrps[,i-1]), as.character(oGrps[,i ]), sep = "/")) } if (length(level) == 1) { val <- val[,1] ## ?? not in predict.lme() if (level > 0) { # otherwise 'oGrps' are typically undefined grps <- as.character(oGrps[, level]) if (asList) { val <- split(val, ordered(grps, levels = unique(grps))) } else { names(val) <- grps } } lab <- "Predicted values" if (!is.null(aux <- attr(object, "units")$y)) { lab <- paste(lab, aux) } attr(val, "label") <- lab val } else { data.frame(oGrps, predict = val) } } ## based on R's update.default update.nlme <- function (object, model., ..., evaluate = TRUE) { if (is.null(call <- object$call)) stop("need an object with call component") extras <- match.call(expand.dots = FALSE)$... if (!missing(model.)) call$model <- update.formula(formula(object), model.) if(length(extras) > 0) { existing <- !is.na(match(names(extras), names(call))) ## do these individually to allow NULL to remove entries. for (a in names(extras)[existing]) call[[a]] <- extras[[a]] if(any(!existing)) { call <- c(as.list(call), extras[!existing]) call <- as.call(call) } } if(evaluate) eval(call, parent.frame()) else call } ## update.nlme <- ## function(object, model, data, fixed, random, groups, start, correlation, ## weights, subset, method, na.action, naPattern, control, ## verbose, ...) ## { ## thisCall <- as.list(match.call())[-(1:2)] ## if (!is.null(thisCall$start) && is.numeric(start)) { ## thisCall$start <- list(fixed = start) ## } ## if (!is.null(nextCall <- object$origCall) && ## (is.null(thisCall$fixed) && !is.null(thisCall$random))) { ## nextCall <- as.list(nextCall)[-1] ## } else { ## nextCall <- as.list(object$call)[-1] ## if (is.null(thisCall$fixed)) { # no changes in fixef model ## if (is.null(thisCall$start)) { ## thisCall$start <- list(fixed = fixef(object)) ## } else { ## if (is.null(thisCall$start$fixed)) { ## thisCall$start$fixed <- fixef(object) ## } ## } ## } ## if (!is.null(thisCall$start$random)) { # making start random NULL ## thisCall$start$random <- NULL ## } ## if (is.null(thisCall$random) && is.null(thisCall$subset)) { ## ## no changes in ranef model and no subsetting ## thisCall$random <- object$modelStruct$reStruct ## } ## } ## if (!is.null(thisCall$model)) { ## thisCall$model <- update(formula(object), model) ## } ## if (is.na(match("correlation", names(thisCall))) && ## !is.null(thCor <- object$modelStruct$corStruct)) { ## thisCall$correlation <- thCor ## } ## if (is.na(match("weights", names(thisCall))) && ## !is.null(thWgt <- object$modelStruct$varStruct)) { ## thisCall$weights <- thWgt ## } ## nextCall[names(thisCall)] <- thisCall ## do.call(nlme, nextCall) ## } ###*### nlmeStruct - a model structure for nlme fits nlmeStruct <- ## constructor for nlmeStruct objects function(reStruct, corStruct = NULL, varStruct = NULL)#, resp = NULL, ## model = NULL, local = NULL, N = NULL, naPat = NULL) { val <- list(reStruct = reStruct, corStruct = corStruct, varStruct = varStruct) structure(val[!vapply(val, is.null, NA)], # removing NULL components settings = attr(val$reStruct, "settings"), ## attr(val, "resp") <- resp ## attr(val, "model") <- model ## attr(val, "local") <- local ## attr(val, "N") <- N ## attr(val, "naPat") <- naPat class = c("nlmeStruct", "lmeStruct", "modelStruct")) } ##*## nlmeStruct methods for standard generics fitted.nlmeStruct <- function(object, level = Q, conLin = attr(object, "conLin"), ...) { Q <- attr(object, "conLin")$dims[["Q"]] attr(object, "resp") - resid(object, level, conLin) } residuals.nlmeStruct <- function(object, level = Q, conLin = attr(object, "conLin"), ...) { Q <- conLin$dims[["Q"]] stopifnot(length(level) >= 1) loc <- attr(object, "local") oLev <- get("level", envir = loc) on.exit(assign("level", oLev, envir = loc)) dn <- c("fixed", rev(names(object$reStruct)))[level + 1] val <- array(0, c(attr(object, "NReal"), length(level)), list(dimnames(conLin$Xy)[[1]], dn)) for(i in seq_along(level)) { assign("level", level[i], envir = loc, immediate = TRUE) val[, i] <- c(eval(attr(object, "model")[[2]], envir = loc)) } val } nlmeControl <- ## Set control values for iterations within nlme function(maxIter = 50, pnlsMaxIter = 7, msMaxIter = 50, minScale = 0.001, tolerance = 1e-5, niterEM = 25, pnlsTol = 0.001, msTol = 0.000001, returnObject = FALSE, msVerbose = FALSE, msWarnNoConv = TRUE, gradHess = TRUE, apVar = TRUE, .relStep = .Machine$double.eps^(1/3), minAbsParApVar = 0.05, opt = c("nlminb", "nlm"), natural = TRUE, sigma = NULL, ...) { if(is.null(sigma)) sigma <- 0 else if(!is.finite(sigma) || length(sigma) != 1 || sigma < 0) stop("Within-group std. dev. must be a positive numeric value") list(maxIter = maxIter, pnlsMaxIter = pnlsMaxIter, msMaxIter = msMaxIter, minScale = minScale, tolerance = tolerance, niterEM = niterEM, pnlsTol = pnlsTol, msTol = msTol, returnObject = returnObject, msVerbose = msVerbose, msWarnNoConv = msWarnNoConv, gradHess = gradHess, apVar = apVar, .relStep = .relStep, minAbsParApVar = minAbsParApVar, opt = match.arg(opt), natural = natural, sigma=sigma, ...) } nonlinModel <- function(modelExpression, env, paramNames = get(".parameters", envir = env)) { modelExpression <- modelExpression[[2]] thisEnv <- environment() offset <- 0 ind <- vector("list", length(paramNames)) names(ind) <- paramNames for( i in paramNames ) { v.i <- get(i, envir = env) ind[[ i ]] <- offset + seq_along(v.i) offset <- offset + length(v.i) } modelValue <- eval(modelExpression, env) rm(i, offset, paramNames) function (newPars) { if(!missing(newPars)) { for(i in names(ind)) assign( i, unname(newPars[ ind[[i]] ]), envir = env) assign("modelValue", eval(modelExpression, env), envir = thisEnv) } modelValue } } ## Local Variables: ## ess-indent-offset: 2 ## End: