R/anova.R

# =============================== anova.oola ================================ #

#' Comparison of nested models
#'
#' \code{anova} method for objects of class \code{"oola"}.
#' Compares two or more nested models using the adjusted likelihood ratio
#' test statistic (ALRTS) described in Section 3.5 of
#' \href{http://dx.doi.org/10.1093/biomet/asm015}{Chandler and Bate (2007)}.
#' The nesting must result from the simple constraint that a subset of the
#' parameters of the larger model is held fixed.
#'
#' @param object An object of class \code{"oola"}, returned by
#'   \code{\link{alogLik}} or \code{\link{adjust_object}}.
#' @param object2 An object of class \code{"chandwich"}, returned by
#'   \code{\link{alogLik}} or \code{\link{adjust_object}}.
#' @param ... Further objects of class \code{"oola"} and/or arguments
#'   to be passed to \code{\link[chandwich]{anova.chandwich}}.
#'
#'   The objects of class \code{"oola"} need not be provided in nested
#'   order: they will be ordered inside \code{anova.oola} based on the
#'   values of \code{attr(., "p_current")}.
#' @return An object of class \code{"anova"} inheriting from class
#'  \code{"data.frame"}, with four columns:
#'     \item{Model.Df}{The number of parameters in the model}
#'     \item{Df}{The decrease in the number of parameter compared the model
#'       in the previous row}
#'     \item{ALRTS}{The adjusted likelihood ratio test statistic}
#'     \item{Pr(>ALRTS)}{The p-value associated with the test that the
#'       model is a valid simplification of the model in the previous row.}
#'  The row names are the names of the model objects.
#' @seealso \code{\link[chandwich]{anova.chandwich}}.
#' @seealso \code{\link{adjust_object}}.
#' @references Chandler, R. E. and Bate, S. (2007). Inference for clustered
#'   data using the independence loglikelihood. \emph{Biometrika},
#'   \strong{94}(1), 167-183. \url{http://dx.doi.org/10.1093/biomet/asm015}
#' @examples
#' got_evd <- requireNamespace("evd", quietly = TRUE)
#'
#' if (got_evd) {
#'   y <- c(chandwich::owtemps[, "Oxford"], chandwich::owtemps[, "Worthing"])
#'   x <- rep(c(1, -1), each = length(y) / 2)
#'   owfit <- evd::fgev(y, nsloc = x)
#'   year <- rep(1:(length(y) / 2), 2)
#'   oola_small <- alogLik(owfit, cluster = year)
#'
#'   owfit <- evd::fgev(y)
#'   year <- rep(1:(length(y) / 2), 2)
#'   oola_tiny <- alogLik(owfit, cluster = year)
#'
#'   anova(oola_small, oola_tiny)
#' }
#' @export
anova.oola <- function (object, object2, ...) {
  if (missing(object)) {
    stop("model one must be supplied, using object")
  }
  if (missing(object2)) {
    stop("model two must be supplied, using object2")
  }
  # Extract the names of object and object2
  model1 <- deparse(substitute(object))
  model2 <- deparse(substitute(object2))
  # Extract arguments supplied in ... and determine which are named
  dotargs <- list(...)
  named <- if (is.null(names(dotargs)))
    rep_len(FALSE, length(dotargs))
  else (names(dotargs) != "")
  which_named <- which(named)
  which_not_named <- which(!named)
  # Named objects are intended for compare_models()
  for_compare_models <- dotargs[named]
  # Create list of model objects:  unnamed arguments may be model objects
  model_list <- c(list(object, object2), dotargs[!named])
  # Check for objects that do not have class "oola"
  is_chand <- vapply(model_list, function(x) inherits(x, "oola"), NA)
  if (any(!is_chand)) {
    stop("The following are not 'oola' objects: ",
         paste(names(model_list)[!is_chand], collapse = ", "))
  }
  extra_names <- as.list(substitute(list(...)))[-1][which_not_named]
  extra_names <- sapply(extra_names, function(x) deparse(x))
  model_names <- c(model1, model2, extra_names)
  # Check for duplicate names
  if (anyDuplicated(model_names)) {
    stop("A model name has been supplied more than once")
  }
  # Order the models in order of the number of parameters
  n_pars <- vapply(model_list, function(x) attr(x, "p_current"), 0)
  # Check for models with the same number of parameters
  if (anyDuplicated(n_pars)) {
    stop("At least two models have the same number of parameters")
  }
  m_order <- order(n_pars, decreasing = TRUE)
  model_list <- model_list[m_order]
  n_pars <- n_pars[m_order]
  n_models <- length(model_list)
  # Infer the values of fixed_pars and fixed_at for all models
  # The largest model is model_list[[1]]
  all_pars <- attr(model_list[[1]], "free_pars")
  names_all_pars <- names(attr(model_list[[1]], "free_pars"))
  # Nested models
#  class(model_list[[1]]) <- "chandwich"
  for (i in 2:n_models) {
    smaller_names <- names(attr(model_list[[i]], "free_pars"))
    fixed_pars <- which(!(names_all_pars %in% smaller_names))
    fixed_pars <- all_pars[fixed_pars]
    fixed_at <- rep(0, length(fixed_pars))
    names(fixed_at) <- names(fixed_pars)
    attr(model_list[[i]], "fixed_pars") <- fixed_pars
    attr(model_list[[i]], "fixed_at") <- fixed_at
#    class(model_list[[i]]) <- "chandwich"
  }
  # Do the testing
  alrts <- p_value <- numeric(n_models - 1)
  for (i in 2:n_models) {
    larger <- model_list[[i - 1]]
    smaller <- model_list[[i]]
    res <- do.call(chandwich::compare_models,
                   c(list(larger = larger, smaller = smaller),
                     for_compare_models))
    alrts[i - 1] <- res$alrts
    p_value[i - 1] <- res$p_value
  }
  df <- -diff(n_pars)
  my_table <- data.frame(n_pars, c(NA, df), c(NA, alrts), c(NA, p_value))
  dimnames(my_table) <- list(model_names,
                             c("Model.Df", "Df", "ALRTS", "Pr(>ALRTS)"))
  structure(my_table, heading = c("Analysis of (Adjusted) Deviance Table\n"),
            class = c("anova", "data.frame"))
}
paulnorthrop/oola documentation built on May 12, 2019, 10:52 a.m.