SCM

SCM Repository

[matrix] View of /pkg/R/lmer.R
ViewVC logotype

View of /pkg/R/lmer.R

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1123 - (download) (annotate)
Tue Jan 10 17:04:29 2006 UTC (13 years, 10 months ago) by bates
File size: 46719 byte(s)
Release 0.99-6
# Methods for lmer and for the objects that it produces

## Some utilities

## Return the index into the packed lower triangle
Lind <- function(i,j) {
    if (i < j) stop(paste("Index i=", i,"must be >= index j=", j))
    ((i - 1) * i)/2 + j
}

## Return the pairs of expressions separated by vertical bars
findbars <- function(term)
{
    if (is.name(term) || is.numeric(term)) return(NULL)
    if (term[[1]] == as.name("(")) return(findbars(term[[2]]))
    if (!is.call(term)) stop("term must be of class call")
    if (term[[1]] == as.name('|')) return(term)
    if (length(term) == 2) return(findbars(term[[2]]))
    c(findbars(term[[2]]), findbars(term[[3]]))
}

## Return the formula omitting the pairs of expressions
## that are separated by vertical bars
nobars <- function(term)
{
    # FIXME: is the is.name in the condition redundant?
    #   A name won't satisfy the first condition.
    if (!('|' %in% all.names(term)) || is.name(term)) return(term)
    if (is.call(term) && term[[1]] == as.name('|')) return(NULL)
    if (length(term) == 2) {
        nb <- nobars(term[[2]])
        if (is.null(nb)) return(NULL)
        term[[2]] <- nb
        return(term)
    }
    nb2 <- nobars(term[[2]])
    nb3 <- nobars(term[[3]])
    if (is.null(nb2)) return(nb3)
    if (is.null(nb3)) return(nb2)
    term[[2]] <- nb2
    term[[3]] <- nb3
    term
}

## Substitute the '+' function for the '|' function
subbars <- function(term)
{
    if (is.name(term) || is.numeric(term)) return(term)
    if (length(term) == 2) {
        term[[2]] <- subbars(term[[2]])
        return(term)
    }
    stopifnot(length(term) == 3)
    if (is.call(term) && term[[1]] == as.name('|'))
        term[[1]] <- as.name('+')
    term[[2]] <- subbars(term[[2]])
    term[[3]] <- subbars(term[[3]])
    term
}

## Expand an expression with colons to the sum of the lhs
## and the current expression

colExpand <- function(term)
{
    if (is.name(term) || is.numeric(term)) return(term)
    if (length(term) == 2) {
        term[[2]] <- colExpand(term[[2]])
        return(term)
    }
    stopifnot(length(term) == 3)
    if (is.call(term) && term[[1]] == as.name(':')) {
        return(substitute(A+B, list(A = term, B = colExpand(term[[2]]))))
    }
    term[[2]] <- colExpand(term[[2]])
    term[[3]] <- colExpand(term[[3]])
    term
}

abbrvNms <- function(gnm, cnms)
{
    ans <- paste(abbreviate(gnm), abbreviate(cnms), sep = '.')
    if (length(cnms) > 1) {
        anms <- lapply(cnms, abbreviate, minlength = 3)
        nmmat <- outer(anms, anms, paste, sep = '.')
        ans <- c(ans, paste(abbreviate(gnm, minlength = 3),
                            nmmat[upper.tri(nmmat)], sep = '.'))
    }
    ans
}

## Control parameters for lmer
lmerControl <-
  function(maxIter = 200, # used in ../src/lmer.c only
           tolerance = sqrt(.Machine$double.eps),# ditto
           msMaxIter = 200,
           ## msTol = sqrt(.Machine$double.eps),
           ## FIXME:  should be able to pass tolerances to nlminb()
           msVerbose = getOption("verbose"),
           niterEM = 15,
           EMverbose = getOption("verbose"),
           PQLmaxIt = 30,# FIXME: unused; PQL currently uses 'maxIter' instead
           analyticGradient = TRUE,
           analyticHessian = FALSE # unused _FIXME_
           )
{
    list(maxIter = as.integer(maxIter),
         tolerance = as.double(tolerance),
         msMaxIter = as.integer(msMaxIter),
         ## msTol = as.double(msTol),
         msVerbose = as.integer(msVerbose),# "integer" on purpose
         niterEM = as.integer(niterEM),
         EMverbose = as.logical(EMverbose),
         PQLmaxIt = as.integer(PQLmaxIt),
         analyticGradient = as.logical(analyticGradient),
         analyticHessian = as.logical(analyticHessian))
}

setMethod("lmer", signature(formula = "formula"),
          function(formula, data, family,
                   method = c("REML", "ML", "PQL", "Laplace", "AGQ"),
                   control = list(), start,
                   subset, weights, na.action, offset,
                   model = TRUE, x = FALSE, y = FALSE , ...)
          ## x, y : not dealt with at all -- FIXME ? .NotYetImplemented(
      {
          ## match and check parameters
          if (length(formula) < 3) stop("formula must be a two-sided formula")
          cv <- do.call("lmerControl", control)
          ## evaluate glm.fit, a generalized linear fit of fixed effects only
          mf <- match.call()
          m <- match(c("family", "data", "subset", "weights",
                       "na.action", "offset"), names(mf), 0)
          mf <- mf[c(1, m)]
          frame.form <- subbars(formula) # substitute `+' for `|'
          fixed.form <- nobars(formula)  # remove any terms with `|'
          if (inherits(fixed.form, "name")) # RHS is empty - use a constant
              fixed.form <- substitute(foo ~ 1, list(foo = fixed.form))
          environment(fixed.form) <- environment(frame.form) <- environment(formula)
          mf$formula <- fixed.form
          mf$x <- mf$model <- mf$y <- TRUE
          mf[[1]] <- as.name("glm")
          glm.fit <- eval(mf, parent.frame())
          x <- glm.fit$x
          y <- as.double(glm.fit$y)
          family <- glm.fit$family
          ## check for a linear mixed model
          lmm <- family$family == "gaussian" && family$link == "identity"
          if (lmm) { # linear mixed model
              method <- match.arg(method)
              if (method %in% c("PQL", "Laplace", "AGQ")) {
                  warning(paste('Argument method = "', method,
                                '" is not meaningful for a linear mixed model.\n',
                                'Using method = "REML".\n', sep = ''))
                  method <- "REML"
              }
          } else { # generalized linear mixed model
              if (missing(method)) method <- "PQL"
              else {
                  method <- match.arg(method)
                  if (method == "ML") method <- "PQL"
                  if (method == "REML")
                      warning('Argument method = "REML" is not meaningful ',
                              'for a generalized linear mixed model.',
                              '\nUsing method = "PQL".\n')
              }
          }

          ## evaluate a model frame for fixed and random effects
          mf$formula <- frame.form
          mf$x <- mf$model <- mf$y <- mf$family <- NULL
          mf$drop.unused.levels <- TRUE
          mf[[1]] <- as.name("model.frame")
          frm <- eval(mf, parent.frame())

          ## grouping factors and model matrices for random effects
          bars <- findbars(formula[[3]])
	  random <-
	      lapply(bars, function(x)
		     list(model.matrix(eval(substitute(~ T, list(T = x[[2]]))),
				       frm),
			  eval(substitute(as.factor(fac)[,drop = TRUE],
					  list(fac = x[[3]])), frm)))
          names(random) <- unlist(lapply(bars, function(x) deparse(x[[3]])))

          ## order factor list by decreasing number of levels
          nlev <- sapply(random, function(x) length(levels(x[[2]])))
          if (any(diff(nlev) > 0)) {
              random <- random[rev(order(nlev))]
          }

          ## Create the model matrices and a mixed-effects representation (mer)
          mmats <- c(lapply(random, "[[", 1),
                     .fixed = list(cbind(glm.fit$x, .response = glm.fit$y)))
          mer <- .Call("lmer_create", lapply(random, "[[", 2),
                       mmats, method, PACKAGE = "Matrix")
          if (lmm) {                    ## linear mixed model
              if (missing(start)) .Call("lmer_initial", mer, PACKAGE="Matrix")
              else .Call("lmer_set_initial", mer, start, PACKAGE = "Matrix")
              .Call("lmer_ECMEsteps", mer, cv$niterEM, cv$EMverbose, PACKAGE = "Matrix")
              LMEoptimize(mer) <- cv
              fits <- .Call("lmer_fitted", mer, mmats, TRUE, PACKAGE = "Matrix")
              return(new("lmer",
                         mer,
                         assign = attr(x, "assign"),
                         call = match.call(),
                         family = family, fitted = fits,
                         fixed = fixef(mer),
                         frame = if (model) frm else data.frame(),
                         logLik = logLik(mer),
                         residuals = unname(model.response(frm) - fits),
                         terms = glm.fit$terms))
          }

          ## The rest of the function applies to generalized linear mixed models
          gVerb <- getOption("verbose")
          eta <- glm.fit$linear.predictors
          wts <- glm.fit$prior.weights
          wtssqr <- wts * wts
          offset <- glm.fit$offset
          if (is.null(offset)) offset <- numeric(length(eta))
          mu <- numeric(length(eta))

          dev.resids <- quote(family$dev.resids(y, mu, wtssqr))
          linkinv <- quote(family$linkinv(eta))
          mu.eta <- quote(family$mu.eta(eta))
          variance <- quote(family$variance(mu))
          LMEopt <- get("LMEoptimize<-")
          doLMEopt <- quote(LMEopt(x = mer, value = cv))

          GSpt <- .Call("glmer_init", environment(), PACKAGE = "Matrix")
          .Call("glmer_PQL", GSpt, PACKAGE = "Matrix")  # obtain PQL estimates

          fixInd <- seq(ncol(x))
          ## pars[fixInd] == beta, pars[-fixInd] == theta
          PQLpars <- c(fixef(mer),
                       .Call("lmer_coef", mer, 2, PACKAGE = "Matrix"))
          ## set flag to skip fixed-effects in subsequent calls
          mer@nc[length(mmats)] <- -mer@nc[length(mmats)]
          ## indicator of constrained parameters
          const <- c(rep(FALSE, length(fixInd)),
                     unlist(lapply(mer@nc[seq(along = random)],
                                   function(k) 1:((k*(k+1))/2) <= k)
                            ))
          devAGQ <- function(pars, n)
              .Call("glmer_devAGQ", pars, GSpt, n, PACKAGE = "Matrix")

          deviance <- devAGQ(PQLpars, 1)
### FIXME: For nf == 1 change this to an AGQ evaluation.  Needs
### AGQ for nc > 1 first.
          fxd <- PQLpars[fixInd]
          loglik <- logLik(mer)

          if (method %in% c("Laplace", "AGQ")) {
              nAGQ <- 1
              if (method == "AGQ") {    # determine nAGQ at PQL estimates
                  dev11 <- devAGQ(PQLpars, 11)
                  ## FIXME: Should this be an absolute or a relative tolerance?
                  devTol <- sqrt(.Machine$double.eps) * abs(dev11)
                  for (nAGQ in c(9, 7, 5, 3, 1))
                      if (abs(dev11 - devAGQ(PQLpars, nAGQ - 2)) > devTol) break
                  nAGQ <- nAGQ + 2
                  if (gVerb)
                      cat(paste("Using", nAGQ, "quadrature points per column\n"))
              }
              obj <- function(pars)
                  .Call("glmer_devAGQ", pars, GSpt, nAGQ, PACKAGE = "Matrix")
              if (exists("nlminb", mode = "function")) {
                  optimRes <-
                      nlminb(PQLpars, obj,
                             lower = ifelse(const, 5e-10, -Inf),
                             control = list(trace = getOption("verbose"),
                                            iter.max = cv$msMaxIter))
                  optpars <- optimRes$par
                  if (optimRes$convergence != 0)
                      warning("nlminb failed to converge")
                  deviance <- optimRes$objective
              } else {
                  optimRes <-
                      optim(PQLpars, obj, method = "L-BFGS-B",
                            lower = ifelse(const, 5e-10, -Inf),
                            control = list(trace = getOption("verbose"),
                                           maxit = cv$msMaxIter))
                  optpars <- optimRes$par
                  if (optimRes$convergence != 0)
                      warning("optim failed to converge")
                  deviance <- optimRes$value
              }
              if (gVerb) {
                  cat(paste("convergence message", optimRes$message, "\n"))
              }
              fxd[] <- optpars[fixInd]  ## preserve the names
              .Call("lmer_coefGets", mer, optpars[-fixInd], 2, PACKAGE = "Matrix")
          }

          .Call("glmer_finalize", GSpt, PACKAGE = "Matrix")
          loglik[] <- -deviance/2
          new("lmer", mer,
              frame = if (model) frm else data.frame(),
              terms = glm.fit$terms,
              assign = attr(glm.fit$x, "assign"),
              call = match.call(), family = family,
              logLik = loglik, fixed = fxd)
      })
## end{  "lmer . formula " }

setReplaceMethod("LMEoptimize", signature(x="mer", value="list"),
                 function(x, value)
             {
                 if (value$msMaxIter < 1) return(x)
                 nc <- x@nc
                 constr <- unlist(lapply(nc[1:(length(nc) - 2)],
                                         function(k) 1:((k*(k+1))/2) <= k))
                 fn <- function(pars)
                     deviance(.Call("lmer_coefGets", x, pars, 2, PACKAGE = "Matrix"))
                 gr <-
                     if (value$analyticGradient)
                         function(pars) {
                             if (!isTRUE(all.equal(pars,
                                                   .Call("lmer_coef", x,
                                                         2, PACKAGE = "Matrix"))))
                                 .Call("lmer_coefGets", x, pars, 2, PACKAGE = "Matrix")
                             .Call("lmer_gradient", x, 2, PACKAGE = "Matrix")
                         }
		 ## else NULL
		 optimRes <-
		     if (exists("nlminb", mode = "function"))
			 nlminb(.Call("lmer_coef", x, 2, PACKAGE = "Matrix"),
				fn, gr,
				lower = ifelse(constr, 5e-10, -Inf),
				control = list(iter.max = value$msMaxIter,
					       trace = as.integer(value$msVerbose)))
		     else
			 optim(.Call("lmer_coef", x, 2, PACKAGE = "Matrix"),
			       fn, gr, method = "L-BFGS-B",
			       lower = ifelse(constr, 5e-10, -Inf),
			       control = list(maxit = value$msMaxIter,
					      trace = as.integer(value$msVerbose)))
                 .Call("lmer_coefGets", x, optimRes$par, 2, PACKAGE = "Matrix")
                 if (optimRes$convergence != 0) {
                     warning(paste("optim or nlminb returned message",
                                   optimRes$message,"\n"))
                 }
                 return(x)
             })

setMethod("ranef", signature(object = "lmer"),
          function(object, accumulate = FALSE, ...) {
              val <- new("lmer.ranef",
                         lapply(.Call("lmer_ranef", object, PACKAGE = "Matrix"),
                                data.frame, check.names = FALSE),
                         varFac = object@bVar,
                         stdErr = .Call("lmer_sigma", object,
                         object@method == "REML", PACKAGE = "Matrix"))
              if (!accumulate || length(val@varFac) == 1) return(val)
              ## check for nested factors
              L <- object@L
              if (any(sapply(seq(a = val), function(i) length(L[[Lind(i,i)]]@i))))
                  error("Require nested grouping factors to accumulate random effects")
              val
          })

setMethod("fixef", signature(object = "mer"),
          function(object, ...)
              .Call("lmer_fixef", object, PACKAGE = "Matrix"))

setMethod("fixef", signature(object = "lmer"),
          function(object, ...) object@fixed)

setMethod("VarCorr", signature(x = "lmer"),
##FIXME - change this for reasonable defaults of useScale according to
          ##the family slot.
          function(x, REML = TRUE, useScale = TRUE, ...) {
              val <- .Call("lmer_variances", x, PACKAGE = "Matrix")
              for (i in seq(along = val)) {
                  dimnames(val[[i]]) = list(x@cnames[[i]], x@cnames[[i]])
                  val[[i]] = as(as(val[[i]], "pdmatrix"), "corrmatrix")
              }
              new("VarCorr",
                  scale = .Call("lmer_sigma", x, REML, PACKAGE = "Matrix"),
                  reSumry = val,
                  useScale = useScale)
          })

setMethod("gradient", signature(x = "lmer"),
          function(x, unconst, ...)
          .Call("lmer_gradient", x, unconst, PACKAGE = "Matrix"))

setMethod("summary", signature(object = "lmer"),
          function(object, ...)
          new("summary.lmer", object,
              showCorrelation = TRUE,
              useScale = !((object@family)$family %in% c("binomial", "poisson"))))

setMethod("show", signature(object = "lmer"),
          function(object)
          show(new("summary.lmer", object,
                   showCorrelation = FALSE,
                   useScale = !((object@family)$family %in% c("binomial", "poisson")))))

setMethod("show", "summary.lmer",
          function(object) {
              fcoef <- object@fixed
              useScale <- object@useScale
              corF <- as(as(vcov(object, useScale = useScale), "pdmatrix"),
                         "corrmatrix")
              #DF <- getFixDF(object)
              coefs <- cbind(fcoef, corF@stdDev) #, DF)
              nc <- object@nc
              dimnames(coefs) <-
                  list(names(fcoef), c("Estimate", "Std. Error")) #, "DF"))
              digits <- max(3, getOption("digits") - 2)
              REML <- object@method == "REML"
              llik <- object@logLik
              dev <- object@deviance

              rdig <- 5
              if (glz <- !(object@method %in% c("REML", "ML"))) {
                  cat(paste("Generalized linear mixed model fit using",
                            object@method, "\n"))
              } else {
                  cat("Linear mixed-effects model fit by ")
                  cat(if(REML) "REML\n" else "maximum likelihood\n")
              }
              if (!is.null(object@call$formula)) {
                  cat("Formula:", deparse(object@call$formula),"\n")
              }
              if (!is.null(object@call$data)) {
                  cat("   Data:", deparse(object@call$data), "\n")
              }
              if (!is.null(object@call$subset)) {
                  cat(" Subset:",
                      deparse(asOneSidedFormula(object@call$subset)[[2]]),"\n")
              }
              if (glz) {
                  cat(" Family: ", object@family$family, "(",
                      object@family$link, " link)\n", sep = "")
                  print(data.frame(AIC = AIC(llik), BIC = BIC(llik),
                               logLik = c(llik),
                               deviance = -2*llik,
                               row.names = ""))
              } else {
                  print(data.frame(AIC = AIC(llik), BIC = BIC(llik),
                               logLik = c(llik),
                               MLdeviance = dev["ML"],
                               REMLdeviance = dev["REML"],
                               row.names = ""))
              }
              cat("Random effects:\n")
              show(VarCorr(object, useScale = useScale))
              ngrps <- lapply(object@flist, function(x) length(levels(x)))
              cat(sprintf("# of obs: %d, groups: ", object@nc[length(object@nc)]))
              cat(paste(paste(names(ngrps), ngrps, sep = ", "), collapse = "; "))
              cat("\n")
              if (!useScale)
                  cat("\nEstimated scale (compare to 1) ",
                      .Call("lmer_sigma", object, FALSE, PACKAGE = "Matrix"),
                      "\n")
              if (nrow(coefs) > 0) {
                  if (0) {              #this section removed because of objections to its validity
                      if (useScale) {
                          stat <- coefs[,1]/coefs[,2]
                          pval <- 2*pt(abs(stat), coefs[,3], lower = FALSE)
                          nms <- colnames(coefs)
                          coefs <- cbind(coefs, stat, pval)
                          colnames(coefs) <- c(nms, "t value", "Pr(>|t|)")
                      } else {
                          coefs <- coefs[, 1:2, drop = FALSE]
                          stat <- coefs[,1]/coefs[,2]
                          pval <- 2*pnorm(abs(stat), lower = FALSE)
                          nms <- colnames(coefs)
                          coefs <- cbind(coefs, stat, pval)
                          colnames(coefs) <- c(nms, "z value", "Pr(>|z|)")
                      }
                  }
                  cat("\nFixed effects:\n")
                  printCoefmat(coefs)
                  #printCoefmat(coefs, tst.ind = 4, zap.ind = 3)
                  if (length(object@showCorrelation) > 0 && object@showCorrelation[1]) {
                      rn <- rownames(coefs)
                      dimnames(corF) <- list(
                                               abbreviate(rn, minlen=11),
                                               abbreviate(rn, minlen=6))
                      if (!is.null(corF)) {
                          p <- NCOL(corF)
                          if (p > 1) {
                              cat("\nCorrelation of Fixed Effects:\n")
                              corF <- format(round(corF, 3), nsmall = 3)
                              corF[!lower.tri(corF)] <- ""
                              print(corF[-1, -p, drop=FALSE], quote = FALSE)
                          }
                      }
                  }
              }
              invisible(object)
          })

## calculates degrees of freedom for fixed effects Wald tests
## This is a placeholder.  The answers are generally wrong.  It will
## be very tricky to decide what a 'right' answer should be with
## crossed random effects.

setMethod("getFixDF", signature(object="lmer"),
          function(object, ...)
      {
          nc <- object@nc[-seq(along = object@Omega)]
          p <- abs(nc[1]) - 1
          n <- nc[2]
          rep(n-p, p)
      })

setMethod("logLik", signature(object="mer"),
          function(object, REML = object@method == "REML", ...) {
              val <- -deviance(object, REML = REML)/2
              nc <- object@nc[-seq(a = object@Omega)]
              attr(val, "nall") <- attr(val, "nobs") <- nc[2]
              attr(val, "df") <- abs(nc[1]) +
                  length(.Call("lmer_coef", object, 0, PACKAGE = "Matrix"))
              attr(val, "REML") <- REML
              class(val) <- "logLik"
              val
          })

setMethod("logLik", signature(object="lmer"),
          function(object, ...) object@logLik)

setMethod("anova", signature(object = "lmer"),
          function(object, ...)
      {
          mCall <- match.call(expand.dots = TRUE)
          dots <- list(...)
          modp <- logical(0)
          if (length(dots))
              modp <- sapply(dots, inherits, "lmer") | sapply(dots, inherits, "lm")
          if (any(modp)) {              # multiple models - form table
              opts <- dots[!modp]
              mods <- c(list(object), dots[modp])
              names(mods) <- sapply(as.list(mCall)[c(FALSE, TRUE, modp)], as.character)
              mods <- mods[order(sapply(lapply(mods, logLik, REML = FALSE), attr, "df"))]
              calls <- lapply(mods, slot, "call")
              data <- lapply(calls, "[[", "data")
              if (any(data != data[[1]])) stop("all models must be fit to the same data object")
              header <- paste("Data:", data[[1]])
              subset <- lapply(calls, "[[", "subset")
              if (any(subset != subset[[1]])) stop("all models must use the same subset")
              if (!is.null(subset[[1]]))
                  header <-
                      c(header, paste("Subset", deparse(subset[[1]]), sep = ": "))
              llks <- lapply(mods, logLik, REML = FALSE)
              Df <- sapply(llks, attr, "df")
              llk <- unlist(llks)
              chisq <- 2 * pmax(0, c(NA, diff(llk)))
              dfChisq <- c(NA, diff(Df))
              val <- data.frame(Df = Df,
                                AIC = sapply(llks, AIC),
                                BIC = sapply(llks, BIC),
                                logLik = llk,
                                "Chisq" = chisq,
                                "Chi Df" = dfChisq,
                                "Pr(>Chisq)" = pchisq(chisq, dfChisq, lower = FALSE),
                                check.names = FALSE)
              class(val) <- c("anova", class(val))
              attr(val, "heading") <-
                  c(header, "Models:",
                    paste(names(mods),
                          unlist(lapply(lapply(calls, "[[", "formula"), deparse)),
                          sep = ": "))
              return(val)
          } else {
              foo <- object
              foo@status["factored"] <- FALSE
              .Call("lmer_factor", foo, PACKAGE="Matrix")
              dfr <- getFixDF(foo)
              rcol <- ncol(foo@RXX)
              ss <- foo@RXX[ , rcol]^2
              ssr <- ss[[rcol]]
              ss <- ss[seq(along = dfr)]
              names(ss) <- object@cnames[[".fixed"]][seq(along = dfr)]
              asgn <- foo@assign
              terms <- foo@terms
              nmeffects <- attr(terms, "term.labels")
              if ("(Intercept)" %in% names(ss))
                  nmeffects <- c("(Intercept)", nmeffects)
              ss <- unlist(lapply(split(ss, asgn), sum))
              df <- unlist(lapply(split(asgn,  asgn), length))
              #dfr <- unlist(lapply(split(dfr, asgn), function(x) x[1]))
              ms <- ss/df
              #f <- ms/(ssr/dfr)
              #P <- pf(f, df, dfr, lower.tail = FALSE)
              #table <- data.frame(df, ss, ms, dfr, f, P)
              table <- data.frame(df, ss, ms)
              dimnames(table) <-
                  list(nmeffects,
#                       c("Df", "Sum Sq", "Mean Sq", "Denom", "F value", "Pr(>F)"))
                       c("Df", "Sum Sq", "Mean Sq"))
              if ("(Intercept)" %in% nmeffects) table <- table[-1,]
              attr(table, "heading") <- "Analysis of Variance Table"
              class(table) <- c("anova", "data.frame")
              table
          }
      })

setMethod("update", signature(object = "lmer"),
          function(object, formula., ..., evaluate = TRUE)
      {
          call <- object@call
          if (is.null(call))
              stop("need an object with call component")
          extras <- match.call(expand.dots = FALSE)$...
          if (!missing(formula.))
              call$formula <- update.formula(formula(object), formula.)
          if (length(extras) > 0) {
              existing <- !is.na(match(names(extras), names(call)))
              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
      })


setMethod("confint", signature(object = "lmer"),
          function (object, parm, level = 0.95, ...)
          warning("confint method for lmer objects has been withdrawn")
##       {
##           cf <- fixef(object)
##           pnames <- names(cf)
##           if (missing(parm))
##               parm <- seq(along = pnames)
##           else if (is.character(parm))
##               parm <- match(parm, pnames, nomatch = 0)
##           a <- (1 - level)/2
##           a <- c(a, 1 - a)
##           pct <- paste(round(100 * a, 1), "%")
##           ci <- array(NA, dim = c(length(parm), 2),
##                       dimnames = list(pnames[parm], pct))
##           ses <- sqrt(diag(vcov(object)))[parm]
##           ci[] <- cf[parm] + ses * t(outer(a, getFixDF(object)[parm], qt))
##           ci
##       }
          )

setMethod("deviance", "mer",
          function(object, REML = NULL, ...) {
              .Call("lmer_factor", object, PACKAGE = "Matrix")
              if (is.null(REML))
                  REML <- object@method == "REML"
              object@deviance[[ifelse(REML, "REML", "ML")]]
          })


setMethod("deviance", "lmer",
          function(object, ...) -2 * c(object@logLik))


setMethod("chol", signature(x = "lmer"),
          function(x, pivot = FALSE, LINPACK = pivot) {
              x@status["factored"] <- FALSE # force a decomposition
              .Call("lmer_factor", x, PACKAGE = "Matrix")
          })

setMethod("solve", signature(a = "lmer", b = "missing"),
          function(a, b, ...)
          .Call("lmer_invert", a, PACKAGE = "Matrix")
          )

setMethod("formula", "lmer", function(x, ...) x@call$formula)

setMethod("vcov", signature(object = "lmer"),
          function(object, REML = object@method == "REML", useScale = TRUE,...) {
              sc <- .Call("lmer_sigma", object, REML, PACKAGE = "Matrix")
              rr <- object@RXX
              nms <- object@cnames[[".fixed"]]
              dimnames(rr) <- list(nms, nms)
              nr <- nrow(rr)
              rr <- rr[-nr, -nr, drop = FALSE]
              rr <- rr %*% t(rr)
              if (useScale) {
                  rr = sc^2 * rr
              }
              rr
          })

## Extract the L matrix
setAs("lmer", "dtTMatrix",
      function(from)
  {
      ## force a refactorization if the factors have been inverted
      if (from@status["inverted"]) from@status["factored"] <- FALSE
      .Call("lmer_factor", from, PACKAGE = "Matrix")
      L <- lapply(from@L, as, "dgTMatrix")
      nf <- length(from@D)
      Gp <- from@Gp
      nL <- Gp[nf + 1]
      Li <- integer(0)
      Lj <- integer(0)
      Lx <- double(0)
      for (i in 1:nf) {
          for (j in 1:i) {
              Lij <- L[[Lind(i, j)]]
              Li <- c(Li, Lij@i + Gp[i])
              Lj <- c(Lj, Lij@j + Gp[j])
              Lx <- c(Lx, Lij@x)
          }
      }
      new("dtTMatrix", Dim = as.integer(c(nL, nL)), i = Li, j = Lj, x = Lx,
          uplo = "L", diag = "U")
  })

## Extract the ZZX matrix
setAs("lmer", "dsTMatrix",
      function(from)
  {
      .Call("lmer_inflate", from, PACKAGE = "Matrix")
      ZZpO <- lapply(from@ZZpO, as, "dgTMatrix")
      ZZ <- lapply(from@ZtZ, as, "dgTMatrix")
      nf <- length(ZZpO)
      Gp <- from@Gp
      nZ <- Gp[nf + 1]
      Zi <- integer(0)
      Zj <- integer(0)
      Zx <- double(0)
      for (i in 1:nf) {
          ZZpOi <- ZZpO[[i]]
          Zi <- c(Zi, ZZpOi@i + Gp[i])
          Zj <- c(Zj, ZZpOi@j + Gp[i])
          Zx <- c(Zx, ZZpOi@x)
          if (i > 1) {
              for (j in 1:(i-1)) {
                  ZZij <- ZZ[[Lind(i, j)]]
                  ## off-diagonal blocks are transposed
                  Zi <- c(Zi, ZZij@j + Gp[j])
                  Zj <- c(Zj, ZZij@i + Gp[i])
                  Zx <- c(Zx, ZZij@x)
              }
          }
      }
      new("dsTMatrix", Dim = as.integer(c(nZ, nZ)), i = Zi, j = Zj, x = Zx,
          uplo = "U")
  })

setMethod("fitted", signature(object = "lmer"),
          function(object, ...)
          napredict(attr(object@frame, "na.action"), object@fitted))

setMethod("residuals", signature(object = "lmer"),
          function(object, ...)
          naresid(attr(object@frame, "na.action"), object@residuals))

setMethod("resid", signature(object = "lmer"),
          function(object, ...) do.call("residuals", c(list(object), list(...))))

setMethod("coef", signature(object = "lmer"),
          function(object, ...)
      {
          fef <- data.frame(rbind(object@fixed), check.names = FALSE)
          ref <- as(ranef(object), "list")
          names(ref) <- names(object@flist)
          val <- lapply(ref, function(x) fef[rep(1, nrow(x)),])
          for (i in seq(a = val)) {
              refi <- ref[[i]]
              row.names(val[[i]]) <- row.names(refi)
              if (!all(names(refi) %in% names(fef)))
                  stop("unable to align random and fixed effects")
              val[[i]][ , names(refi)] <- val[[i]][ , names(refi)] + refi
          }
          new("lmer.coef", val)
      })

setMethod("plot", signature(x = "lmer.coef"),
          function(x, y, ...)
      {
          ## require("lattice", quietly = TRUE) -- now via Imports
          varying <- unique(do.call("c",
                                    lapply(x, function(el)
                                           names(el)[sapply(el,
                                                            function(col)
                                                            any(col != col[1]))])))
          gf <- do.call("rbind", lapply(x, "[", j = varying))
          gf$.grp <- factor(rep(names(x), sapply(x, nrow)))
          switch(min(length(varying), 3),
                 qqmath(eval(substitute(~ x | .grp,
                                        list(x = as.name(varying[1])))), gf, ...),
                 xyplot(eval(substitute(y ~ x | .grp,
                                        list(y = as.name(varying[1]),
                                             x = as.name(varying[2])))), gf, ...),
                 splom(~ gf | .grp, ...))
      })

setMethod("plot", signature(x = "lmer.ranef"),
          function(x, y, ...)
      {
          ## require("lattice", quietly = TRUE) -- now via Imports
          lapply(x, function(x) {
              cn <- lapply(colnames(x), as.name)
              switch(min(ncol(x), 3),
                     qqmath(eval(substitute(~ x, list(x = cn[[1]]))), x, ...),
                     xyplot(eval(substitute(y ~ x,
                                            list(y = cn[[1]],
                                                 x = cn[[2]]))), x, ...),
                     splom(~ x, ...))
          })
      })

setMethod("with", signature(data = "lmer"),
          function(data, expr, ...) {
              dat <- eval(data@call$data)
              if (!is.null(na.act <- attr(data@frame, "na.action")))
                  dat <- dat[-na.act, ]
              lst <- c(list(. = data), data@flist, data@frame, dat)
              eval(substitute(expr), lst[unique(names(lst))])
          })

setMethod("terms", signature(x = "lmer"),
          function(x, ...) x@terms)

setMethod("show", signature(object="VarCorr"),
          function(object)
      {
          digits <- max(3, getOption("digits") - 2)
          useScale <- length(object@useScale) > 0 && object@useScale[1]
          sc <- ifelse(useScale, object@scale,  1.)
          reStdDev <- c(lapply(object@reSumry,
                               function(x, sc)
                               sc*x@stdDev,
                               sc = sc), list(Residual = sc))
          reLens <- unlist(c(lapply(reStdDev, length)))
          reMat <- array('', c(sum(reLens), 4),
          list(rep('', sum(reLens)),
               c("Groups", "Name", "Variance", "Std.Dev.")))
          reMat[1+cumsum(reLens)-reLens, 1] <- names(reLens)
          reMat[,2] <- c(unlist(lapply(reStdDev, names)), "")
          reMat[,3] <- format(unlist(reStdDev)^2, digits = digits)
          reMat[,4] <- format(unlist(reStdDev), digits = digits)
          if (any(reLens > 1)) {
              maxlen <- max(reLens)
              corr <-
                  do.call("rbind",
                          lapply(object@reSumry,
                                 function(x, maxlen) {
                                     cc <- format(round(x, 3), nsmall = 3)
                                     cc[!lower.tri(cc)] <- ""
                                     nr <- dim(cc)[1]
                                     if (nr >= maxlen) return(cc)
                                     cbind(cc, matrix("", nr, maxlen-nr))
                                 }, maxlen))
              colnames(corr) <- c("Corr", rep("", maxlen - 1))
              reMat <- cbind(reMat, rbind(corr, rep("", ncol(corr))))
          }
          if (!useScale) reMat <- reMat[-nrow(reMat),]
          print(reMat, quote = FALSE)
      })

setMethod("mcmcsamp", signature(object = "lmer"),
          function(object, n = 1, verbose = FALSE, saveb = FALSE,
                   trans = TRUE, ...)
      {
          if (object@family$family == "gaussian" &&
              object@family$link == "identity") {
              glmer <- FALSE
              ans <- .Call("lmer_MCMCsamp", object, saveb, n, trans,
                            PACKAGE = "Matrix")
          } else {
              glmer <- TRUE
              if (trans)
                  warning("trans option not currently allowed for generalized models")
              trans <- FALSE
              ## Check arguments
              if (length(object@Omega) > 1 || object@nc[1] > 1)
                  stop("mcmcsamp currently defined for glmm models with only one variance component")
              cv <- Matrix:::lmerControl()
              if (verbose) cv$msVerbose <- 1
              family <- object@family
              frm <- object@frame

              ## recreate model matrices
              fixed.form <- Matrix:::nobars(object@call$formula)
              if (inherits(fixed.form, "name")) # RHS is empty - use a constant
                  fixed.form <- substitute(foo ~ 1, list(foo = fixed.form))
              glm.fit <- glm(eval(fixed.form), family, frm, x = TRUE,
                             y = TRUE)
              x <- glm.fit$x
              y <- as.double(glm.fit$y)
              bars <- Matrix:::findbars(object@call$formula[[3]])
              random <-
                  lapply(bars,
                         function(x) list(model.matrix(eval(substitute(~term,
                                                                       list(term=x[[2]]))),
                                                       frm),
                                          eval(substitute(as.factor(fac)[,drop = TRUE],
                                                          list(fac = x[[3]])), frm)))
              names(random) <- unlist(lapply(bars, function(x) deparse(x[[3]])))
              if (any(names(random) != names(object@flist)))
                  random <- random[names(object@flist)]
              mmats <- c(lapply(random, "[[", 1),
                         .fixed = list(cbind(glm.fit$x, .response = glm.fit$y)))
              mer <- as(object, "mer")

              ## establish the GS object and the ans matrix
              eta <- glm.fit$linear.predictors # perhaps later change this to object@fitted?
              wts <- glm.fit$prior.weights
              wtssqr <- wts * wts
              offset <- glm.fit$offset
              if (is.null(offset)) offset <- numeric(length(eta))
              off <- numeric(length(eta))
              mu <- numeric(length(eta))
              dev.resids <- quote(family$dev.resids(y, mu, wtssqr))
              linkinv <- quote(family$linkinv(eta))
              mu.eta <- quote(family$mu.eta(eta))
              variance <- quote(family$variance(mu))
              LMEopt <- getAnywhere("LMEoptimize<-")
              doLMEopt <- quote(LMEopt(x = mer, value = cv))
              GSpt <- .Call("glmer_init", environment(), PACKAGE = "Matrix")
              fixed <- object@fixed
              varc <- .Call("lmer_coef", mer, 2, PACKAGE = "Matrix")
              b <- .Call("lmer_ranef", mer, PACKAGE = "Matrix")
              ans <- .Call("glmer_MCMCsamp", GSpt, b, fixed, varc, saveb, n,
                           PACKAGE = "Matrix")
              .Call("glmer_finalize", GSpt, PACKAGE = "Matrix");
          }
          gnms <- names(object@flist)
          cnms <- object@cnames
          ff <- fixef(object)
          colnms <- c(names(ff), if (glmer) character(0) else "sigma^2",
                    unlist(lapply(seq(along = gnms),
                                  function(i)
                                  abbrvNms(gnms[i],cnms[[i]]))))
          if (trans) {
              ## parameter type: 0 => fixed effect, 1 => variance,
              ##                 2 => covariance
              ptyp <- c(integer(length(ff)), if (glmer) integer(0) else 1:1,
                        unlist(lapply(seq(along = gnms),
                                      function(i)
                                  {
                                      k <- length(cnms[[i]])
                                      rep(1:2, c(k, (k*(k-1))/2))
                                  })))
              colnms[ptyp == 1] <-
                  paste("log(", colnms[ptyp == 1], ")", sep = "")
              colnms[ptyp == 2] <-
                  paste("atanh(", colnms[ptyp == 2], ")", sep = "")
          }
          colnames(ans) <- colnms
          ans
      })

rWishart <- function(n, df, invScal)
  .Call("Matrix_rWishart", n, df, invScal, PACKAGE = "Matrix")


setMethod("model.matrix", signature(object = "lmer"),
          function(object, ...)
      {
          frm <- object@frame
          fixed.form <- Matrix:::nobars(object@call$formula)
          if (inherits(fixed.form, "name")) # RHS is empty - use a constant
              fixed.form <- substitute(foo ~ 1, list(foo = fixed.form))
          glm.fit <- glm(eval(fixed.form), object@family, frm, x = TRUE, y = TRUE)
          fxd <- unname(drop(glm.fit$x %*% fixef(object)))

          ## Create the random effects model matrices
          bars <- Matrix:::findbars(object@call$formula[[3]])
          random <-
              lapply(bars,
                     function(x) list(model.matrix(eval(substitute(~term,
                                                                   list(term=x[[2]]))),
                                                   frm),
                                      eval(substitute(as.factor(fac)[,drop = TRUE],
                                                      list(fac = x[[3]])), frm)))
          names(random) <- unlist(lapply(bars, function(x) deparse(x[[3]])))
          ## re-order the random effects pairs if necessary
          if (any(names(random) != names(object@flist)))
              random <- random[names(object@flist)]
          c(lapply(random, "[[", 1),
            .fixed = list(cbind(glm.fit$x, .response = glm.fit$y)))
      })

setMethod("simulate", signature(object = "lmer"),
          function(object, nsim = 1, seed = NULL, ...)
      {
          if(!exists(".Random.seed", envir = .GlobalEnv))
              runif(1)               # initialize the RNG if necessary
          if(is.null(seed))
              RNGstate <- .Random.seed
          else {
              R.seed <- .Random.seed
              set.seed(seed)
              RNGstate <- structure(seed, kind = as.list(RNGkind()))
              on.exit(assign(".Random.seed", R.seed, envir = .GlobalEnv))
          }

          family <- object@family
          if (family$family != "gaussian" ||
              family$link != "identity")
              stop("simulation of generalized linear mixed models not yet implemented")

          ## pieces we will need later
          scale <- .Call("lmer_sigma", object, object@method == "REML",
                         PACKAGE = "Matrix")
          mmats <- model.matrix(object)
          ff <- fixef(object)

###_FIXME: If the factor levels have been permuted, has the
###        permutation been applied in the stored frame?  Otherwise we
###        need to check this.

          ## similate the linear predictors
          lpred <- .Call("lmer_simulate", as(object, "mer"), nsim,
                         unname(drop(mmats[[length(mmats)]][,seq(a = ff),drop = FALSE] %*% ff)),
                         mmats, TRUE, PACKAGE = "Matrix")
          ## add per-observation noise term
          lpred <- as.data.frame(lpred + rnorm(prod(dim(lpred)), sd = scale))

          ## save the seed
          attr(lpred, "seed") <- RNGstate
          lpred
      })

simulate2 <- function(object, n = 1, ...)
{
    family <- object@family
    if (family$family != "gaussian" ||
        family$link != "identity")
        stop("simulation of generalized linear mixed models not implemented yet")

    ## create the mean from the fixed effects
    frm <- object@frame
    fixed.form <- Matrix:::nobars(object@call$formula)
    if (inherits(fixed.form, "name")) # RHS is empty - use a constant
        fixed.form <- substitute(foo ~ 1, list(foo = fixed.form))
    glm.fit <- glm(eval(fixed.form), family, frm, x = TRUE, y = TRUE)
    lpred <- matrix(glm.fit$x %*% fixef(object), nr = nrow(frm), nc = n)

    ## Create the random effects model matrices
    bars <- Matrix:::findbars(object@call$formula[[3]])
    random <-
        lapply(bars,
               function(x) list(model.matrix(eval(substitute(~term,
                                                             list(term=x[[2]]))),
                                             frm),
                                eval(substitute(as.factor(fac)[,drop = TRUE],
                                                list(fac = x[[3]])), frm)))
    names(random) <- unlist(lapply(bars, function(x) deparse(x[[3]])))
    ## re-order the random effects pairs if necessary
    flist <- object@flist
    if (any(names(random) != names(flist)))
        random <- random[names(flist)]
    mmats <- lapply(random, "[[", 1)

    ## simulate the random effects
    scale <- .Call("lmer_sigma", object, object@method == "REML",
                   PACKAGE = "Matrix")
    Omega <- object@Omega
    re <- lapply(seq(along = Omega),
                 function(i) {
                     om <- Omega[[i]]
                     nr <- nrow(om)
                     nlev <- length(levels(flist[[i]]))
                     scale * array(solve(chol(new("dpoMatrix", Dim = dim(om),
                                                  uplo = "U", x = c(om))),
                                         matrix(rnorm(nr * n * nlev),
                                                nr = nr))@x, c(nr, n, nlev))
                 })
    ## apply the random effects
    for (j in seq(along = Omega)) {
        for (i in 1:nrow(lpred))
        lpred[i,] <- lpred[i,] + mmats[[j]][i,] %*% re[[j]][, , as.integer(flist[[j]])[i]]
    }
    ## add per-observation noise term
    lpred <- lpred + rnorm(prod(dim(lpred)), sd = scale)
    attr(lpred, "re") <- re
    lpred
}

refdist <- function(fm1, fm2, n, ...)
{
    cv <- lmerControl()
    obs <- deviance(fm2) - deviance(fm1)
    newy <- simulate(fm2, n)
    mm1 <- model.matrix(fm1)
    mm2 <- model.matrix(fm2)
    ref <- numeric(n)
    mer1 <- as(fm1, "mer")
    mer2 <- as(fm2, "mer")
    for (j in 1:n) {
        .Call("lmer_update_y", mer2, newy[[j]], mm2, PACKAGE = "Matrix")
        LMEoptimize(mer2) <- cv
        .Call("lmer_update_y", mer1, newy[[j]], mm1, PACKAGE = "Matrix")
        LMEoptimize(mer1) <- cv
        ref[j] <- deviance(mer2) - deviance(mer1)
    }
    attr(ref, "observed") <- obs
    ref
}


root@r-forge.r-project.org
ViewVC Help
Powered by ViewVC 1.0.0  
Thanks to:
Vienna University of Economics and Business Powered By FusionForge