### Fit a general linear mixed effects model
###
### Copyright 1997-2003 Jose C. Pinheiro,
### Douglas M. Bates <bates@stat.wisc.edu>
### Copyright 2006-2022 The R Core team
#
# 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/
#
createConLin <-
function(fixed, data = sys.frame(sys.parent()),
random = pdSymm(eval(as.call(fixed[-2]))), ...)
{
if(!inherits(fixed, "formula") || length(fixed) != 3)
stop("\nfixed-effects model must be a formula of the form \"resp ~ pred\"")
REML <- FALSE
reSt <- reStruct(random, REML = REML, data = NULL)
groups <- getGroupsFormula(reSt)
if(is.null(groups)) {
if(inherits(data, "groupedData")) {
groups <- getGroupsFormula(data)
groupsL <- rev(getGroupsFormula(data,
asList = TRUE))
Q <- length(groupsL)
if(length(reSt) != Q) { # may need to repeat reSt
if(length(reSt) != 1) {
stop("incompatible lengths for 'random' and grouping factors")
}
auxForm <-
eval(parse(text = paste("~", deparse(formula(random)[[2]]), "|",
deparse(groups[[2]]))))
reSt <- reStruct(auxForm, REML = REML, data = NULL)
}
else {
names(reSt) <- names(groupsL)
}
}
else {
stop("'data' must inherit from \"groupedData\" class if 'random' does not define groups")
}
}
## create an lme structure containing the random effects model
lmeSt <- lmeStruct(reStruct = reSt)
## extract a data frame with enough information to evaluate
## fixed, groups, reStruct, corStruct, and varStruct
dataMix <-
model.frame(formula = asOneFormula(formula(lmeSt), fixed, groups),
data = data, drop.unused.levels = TRUE)
origOrder <- row.names(dataMix) # preserve the original order
## sort the model.frame by groups and get the matrices and parameters
## used in the estimation procedures
grps <- getGroups(dataMix, eval(parse(text = paste("~1",
deparse(groups[[2]]), sep = "|"))))
## ordering data by groups
if(inherits(grps, "factor")) { # single level
##"order" treats a single named argument peculiarly so must split this off
ord <- order(grps)
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 = "/"))
}
}
grps <- grps[ord, , drop = FALSE]
dataMix <- dataMix[ord, , drop = FALSE]
## revOrder <- match(origOrder, row.names(dataMix)) # putting in orig. order
## obtaining basic model matrices
N <- nrow(grps)
Z <- model.matrix(reSt, dataMix)
ncols <- attr(Z, "ncols")
Names(lmeSt$reStruct) <- attr(Z, "nams")
## keeping the contrasts for later use in predict
contr <- attr(Z, "contr")
X <- model.frame(fixed, dataMix)
auxContr <- lapply(X, function(el)
if(inherits(el, "factor")) contrasts(el))
contr <- c(contr, auxContr[is.na(match(names(auxContr), names(contr)))])
contr <- contr[!vapply(contr, is.null, NA)]
X <- model.matrix(fixed, X)
y <- eval(fixed[[2]], dataMix)
ncols <- c(ncols, dim(X)[2], 1)
## Q <- ncol(grps)
## creating the condensed linear model :
list(Xy = array(c(Z, X, y), c(N, sum(ncols)),
list(row.names(dataMix),
c(colnames(Z), colnames(X), deparse(fixed[[2]])))),
dims = MEdims(grps, ncols), logLik = 0,
sigma = 0) # <- no "fixed Sigma" yet
}
simulate.lme <-
function(object, nsim = 1, seed = as.integer(runif(1, 0, .Machine$integer.max)),
m2, method = c("REML", "ML"), niterEM = c(40, 200), useGen, ...)
{
## object is a list of arguments to lme, or an lme object from which the
## call is extracted, to define the null model
## m2 is an option list of arguments to lme to define the feared model
if (inherits(nsim, "lm") || inherits(nsim, "lme"))
stop("order of arguments in 'simulate.lme' has changed to conform with generic in R-2.2.0",
domain = NA)
### FIXME? if(!ALT) behave like a regular simulate() method --> return 'base2' (see below)
getResults1 <-
function(conLin, nIter, pdClass, REML, ssq, p, pp1) {
unlist(.C(mixed_combined,
as.double(conLin$Xy),
as.integer(unlist(conLin$dims)),
double(ssq),
as.integer(nIter),
as.integer(pdClass),
as.integer(REML),
logLik = double(1),
R0 = double(pp1),
lRSS = double(1),
info = integer(1), # msg from optif9: <0 = error, 0 = OK
sigma = as.double(conLin$sigma))[c("info", "logLik")])
}
getResults2 <-
function(conLin, reSt, REML, control) {
lmeSt <- lmeStruct(reStruct = reStruct(reSt, REML = REML))
attr(lmeSt, "conLin") <- conLin
lmeSt <- Initialize(lmeSt, data = NULL, groups = NULL, control = control)
attr(lmeSt, "conLin") <- MEdecomp(attr(lmeSt, "conLin"))
aMs <- nlminb(c(coef(lmeSt)),
function(lmePars) -logLik(lmeSt, lmePars),
control = list(iter.max = control$msMaxIter,
eval.max = control$msMaxEval,
trace = control$msVerbose))
c(info = aMs$convergence, logLik = -aMs$objective)
}
if(!exists(".Random.seed", envir = .GlobalEnv))
runif(1) # initialize the RNG if necessary
RNGstate <- get(".Random.seed", envir = .GlobalEnv)
on.exit(assign(".Random.seed", RNGstate, envir = .GlobalEnv))
set.seed(seed)
if (inherits(object, "lme")) { # given as an lme object
fit1 <- object
object <- as.list(object$call[-1])
} else {
object <- as.list(match.call(lme, substitute(object))[ -1 ])
fit1 <- do.call(lme, object, envir = parent.frame())
}
if (length(fit1$modelStruct) > 1)
stop("models with \"corStruct\" and/or \"varFunc\" objects not allowed")
reSt1 <- fit1$modelStruct$reStruct
condL1 <- do.call(createConLin, object, envir = parent.frame())
pdClass1 <- vapply(reSt1, data.class, "")
pdClass1 <- match(pdClass1, c("pdSymm", "pdDiag", "pdIdent",
"pdCompSymm", "pdLogChol"), 0) - 1
control1 <- lmeControl()
if (!is.null(object$control)) {
control1[names(object$control)] <- object$control
}
control1$niterEM <- niterEM[1]
sig <- fit1$sigma
DeltaInv <- pdMatrix(reSt1, factor = TRUE)
for(i in names(DeltaInv)) {
DeltaInv[[i]] <- sig * DeltaInv[[i]]
}
if (missing(useGen)) {
useGen <- any(pdClass1 == -1)
}
nullD <- condL1$dims
N <- nullD$N
Q <- nullD$Q
p1 <- nullD$ncol[Q + 1]
pp11 <- p1 * (p1 + 1)
ycol1 <- sum(nullD$ncol)
qvec <- nullD$qvec[1:Q]
ssq1 <- sum(qvec^2)
csq1 <- cumsum(c(1, qvec[ - Q]))
csq2 <- cumsum(qvec)
ngrp <- nullD$ngrps
## base for creating response
base <-
condL1$Xy[, ycol1 - (nullD$ncol[Q + 1]:1), drop = FALSE] %*% fixef(fit1)
ind <- lapply(1:Q, function(i) rep(1:ngrp[i], nullD$ZXlen[[i]]))
if (ML <- !is.na(match("ML", method)))
nML <- array(0, c(nsim, 2), list(1:nsim, c("info", "logLik")))
if (REML <- !is.na(match("REML", method)))
nREML <- array(0, c(nsim, 2), list(1:nsim, c("info", "logLik")))
if ((ALT <- !missing(m2))) {
if (inherits(m2, "lme")) { # given as an lme object
fit2 <- m2
m2 <- as.list(m2$call[-1])
} else {
m2 <- as.list(match.call(lme, substitute(m2))[ -1 ])
if (is.null(m2$random)) {
m2$random <- asOneSidedFormula(object$fixed[-2])
}
aux <- object
aux[names(m2)] <- m2
m2 <- aux
fit2 <- do.call(lme, m2, envir = parent.frame())
}
if (length(fit2$modelStruct) > 1) {
stop("models with \"corStruct\" and/or \"varFunc\" objects not allowed")
}
condL2 <- do.call(createConLin, m2, envir = parent.frame())
reSt2 <- fit2$modelStruct$reStruct
control2 <- lmeControl()
if (!is.null(m2$control)) {
control2[names(m2$control)] <- m2$control
}
control2$niterEM <- niterEM[2]
pdClass2 <- vapply(fit2$modelStruct$reStruct, data.class, "")
pdClass2 <- match(pdClass2, c("pdSymm", "pdDiag", "pdIdent",
"pdCompSymm", "pdLogChol"), 0) - 1
useGen <- useGen || any(pdClass2 == -1)
altD <- condL2$dims
ssq2 <- sum((altD$qvec[1:altD$Q])^2)
p2 <- altD$ncol[altD$Q + 1]
pp12 <- p2 * (p2 + 1)
ycol2 <- sum(altD$ncol)
if (ML)
aML <- nML
if (REML)
aREML <- nREML
}
for(i in 1:nsim) {
base2 <- base + rnorm(N, sd = sig) ## = X beta + eps
## now add 'Z b' as Q different terms \sum_{j=1}^Q Z_j b_j :
for(j in 1:Q) {
base2 <- base2 +
((array(rnorm(ngrp[j] * qvec[j]), c(ngrp[j], qvec[j]),
list(1:ngrp[j], NULL)) %*% DeltaInv[[j]])[ind[[j]], , drop = FALSE] *
condL1$Xy[,csq1[j]:csq2[j], drop = FALSE]) %*% rep(1, qvec[j])
}
condL1$Xy[, ycol1] <- base2
if (REML) {
nREML[i,] <-
if (useGen)
getResults2(condL1, reSt1, TRUE, control1)
else
getResults1(condL1, niterEM[1], pdClass1, TRUE, ssq1, p1, pp11)
}
if (ML) {
nML[i,] <-
if (useGen)
getResults2(condL1, reSt1, FALSE, control1)
else
getResults1(condL1, niterEM[1], pdClass1, FALSE, ssq1, p1, pp11)
}
if (ALT) {
condL2$Xy[, ycol2] <- base2
if (REML) {
aREML[i,] <-
if (useGen)
getResults2(condL2, reSt2, TRUE, control2)
else
getResults1(condL2, niterEM[2], pdClass2, TRUE, ssq2, p2, pp12)
}
if (ML) {
aML[i,] <-
if (useGen)
getResults2(condL2, reSt2, FALSE, control2)
else
getResults1(condL2, niterEM[2], pdClass2, FALSE, ssq2, p2, pp12)
}
}
} ## for i = 1,..,nsim
v.null <- v.alt <- list()
if (ML) {
nML[, "logLik"] <- nML[, "logLik"] + N * (log(N) - (1 + log(2*pi)))/2
v.null$ML <- nML
if (ALT) {
aML[, "logLik"] <- aML[, "logLik"] + N * (log(N) - (1 + log(2*pi)))/2
v.alt$ML <- aML
}
}
if (REML) {
nREML[, "logLik"] <- nREML[, "logLik"] +
(N - p1) * (log(N - p1) - (1 + log(2*pi)))/2
v.null$REML <- nREML
if (ALT) {
aREML[, "logLik"] <- aREML[, "logLik"] +
(N - p2) * (log(N - p2) - (1 + log(2*pi)))/2
v.alt$REML <- aREML
}
}
df <- p1 + length(coef(reSt1)) + 1
if (ALT)
df <- abs(df - (p2 + length(coef(reSt2)) + 1))
## return :
structure(if(ALT && (ML || REML))
list(null = v.null, alt = v.alt)
else list(null = v.null),
class = "simulate.lme",
call = match.call(),
seed = seed,
df = df,
useGen = useGen)
}
print.simulate.lme <-
function(x, ...)
{
ox <- x
if (is.null(attr(x, "useGen"))) { # from simulate.lme
attr(x$null, "dims") <- NULL
if (!is.null(x$alt)) {
attr(x$alt, "dims") <- NULL
}
} else {
attr(x, "useGen") <- attr(x, "df") <- NULL
}
attr(x, "seed") <- attr(x, "call") <- NULL
NextMethod()
invisible(ox)
}
plot.simulate.lme <-
function(x, form = y ~ x | df * method, df = attr(x, "df"), weights,
xlab = "Empirical p-value",
ylab = "Nominal p-value", xlim = c(0.037, 0.963),
ylim = c(0.037, 0.963), aspect = 1,
strip = function(...) strip.default(..., style = 1), ...)
{
if (is.null(df))
stop("no degrees of freedom specified")
ML <- !is.null(x$null$ML)
if(ML) {
if (is.null(x$alt$ML))
stop("plot method only implemented for comparing models")
okML <- x$null$ML[, "info"] == 0 & x$alt$ML[, "info"] == 0
}
REML <- !is.null(x$null$REML)
if(REML) {
if (is.null(x$alt$REML))
stop("plot method only implemented for comparing models")
okREML <- x$null$REML[, "info"] == 0 & x$alt$REML[, "info"] == 0
}
if ((ldf <- length(df)) > 1) {
df <- sort(unique(df))
if (missing(weights)) {
weights <- rep.int(1/ldf, ldf)
} else {
if (!identical(weights,FALSE) && length(weights) != ldf)
stop("degrees of freedom and weights must have the same length")
}
} else {
weights <- FALSE
}
useWgts <- (length(weights) != 1)
if (any(df < 0)) {
stop("negative degrees of freedom not allowed")
} else {
if ((ldf == 1) && (df == 0)) {
stop("more than one degree of freedom is needed when one them is zero.")
}
}
if (ML) {
MLstat <-
rev(sort(2 * pmax(0, x$alt$ML[okML, "logLik"] - x$null$ML[okML,"logLik"])))
MLy <- lapply(df,
function(df, x) {
if (df > 0) pchisq(x, df, lower.tail=FALSE) else 1*(x == 0)
}, x = MLstat)
dfC <- paste("df",df,sep="=")
if (useWgts) { # has weights
if (ldf == 2) { # will interpolate
MLy <-
c(MLy[[1]], weights[1] * MLy[[1]] + weights[2] * MLy[[2]], MLy[[2]])
MLdf <- rep(c(dfC[1], paste("Mix(",df[1],",",df[2],")",sep=""),
dfC[2]), rep(length(MLstat), ldf + 1))
} else {
aux <- weights[1] * MLy[[1]]
auxNam <- paste("Mix(",df[1],sep="")
for(i in 2:ldf) {
aux <- aux + weights[i] * MLy[[i]]
auxNam <- paste(auxNam, ",", df[i],sep="")
}
auxNam <- paste(auxNam, ")",sep="")
MLy <- c(unlist(MLy), aux)
MLdf <- rep(c(dfC, auxNam), rep(length(MLstat), ldf + 1))
}
MLx <- rep((seq_along(MLstat) - 0.5)/length(MLstat), ldf + 1)
} else {
MLy <- unlist(MLy)
MLdf <- rep(dfC, rep(length(MLstat), ldf))
MLx <- rep((seq_along(MLstat) - 0.5)/length(MLstat), ldf)
}
auxInd <- MLdf != "df=0"
meth <- rep("ML", length(MLy))
Mdf <- MLdf
} else {
MLy <- MLdf <- MLx <- auxInd <- meth <- Mdf <- NULL
}
if (REML) {
REMLstat <- rev(sort(2 * pmax(0, x$alt$REML[okREML, "logLik"] -
x$null$REML[okREML, "logLik"])))
REMLy <- lapply(df,
function(df, x) {
if (df > 0) {
pchisq(x, df, lower.tail = FALSE)
} else {
1*(x == 0)
}
}, x = REMLstat)
dfC <- paste("df",df,sep="=")
if (useWgts) { # has weights
if (ldf == 2) { # will interpolate
REMLy <-
c(REMLy[[1]], weights[1] * REMLy[[1]] + weights[2] * REMLy[[2]],
REMLy[[2]])
REMLdf <- rep(c(dfC[1], paste("Mix(",df[1],",",df[2],")",sep=""),
dfC[2]), rep(length(REMLstat), ldf + 1))
} else {
aux <- weights[1] * REMLy[[1]]
auxNam <- paste("Mix(",df[1],sep="")
for(i in 2:ldf) {
aux <- aux + weights[i] * REMLy[[i]]
auxNam <- paste(auxNam, ",", df[i],sep="")
}
auxNam <- paste(auxNam, ")",sep="")
REMLy <- c(unlist(REMLy), aux)
REMLdf <- rep(c(dfC, auxNam), rep(length(REMLstat), ldf + 1))
}
REMLx <- rep((seq_along(REMLstat) - 0.5)/length(REMLstat), ldf + 1)
} else {
REMLy <- unlist(REMLy)
REMLdf <- rep(dfC, rep(length(REMLstat), ldf))
REMLx <- rep((seq_along(REMLstat) - 0.5)/length(REMLstat), ldf)
}
auxInd <- c(auxInd, REMLdf != "df=0")
meth <- c(meth, rep("REML", length(REMLy)))
Mdf <- c(Mdf, REMLdf)
} else {
REMLy <- REMLdf <- REMLx <- NULL
}
meth <- meth[auxInd]
Mdf <- Mdf[auxInd]
Mdf <- ordered(Mdf, levels = unique(Mdf))
frm <- data.frame(x = c(MLx, REMLx)[auxInd], y = c(MLy, REMLy)[auxInd],
df = Mdf, method = meth)
## names(frm$x) <- rep(1, nrow(frm))
## if (df[1] == 0) {
## names(frm$x)[substring(frm$df,1,3) == "Mix"] <- 1 - weights[1]
## if (missing(ylim)) {
## ylim <- c(0.0384, 1)
## }
## }
xyplot(form, data = frm, type = c('g', 'l'),
## panel = function(x, y, ...) {
## panel.grid()
## panel.xyplot(x, y, type = "l", ...)
## if ((dfType <- as.double(names(x)[1])) == 1) {
## panel.abline( 0, as.double(names(x)[1]), lty = 2 )
## } else {
## panel.xyplot(c(0,dfType,dfType,1), c(0,dfType,1,1),
## type="l", lty = 2, col = 1)
## }
## },
strip = strip, xlab = xlab, ylab = ylab, aspect = aspect,
xlim = xlim, ylim = ylim, ...)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.