R/gvf.R

Defines functions getBest.gvf.fits getBest.gvf.fit getBest.default getBest vcov.gvf.fits vcov.gvf.fit fitted.gvf.fits fitted.gvf.fit residuals.gvf.fits residuals.gvf.fit effects.gvf.fits effects.gvf.fit coef.gvf.fits coef.gvf.fit anova.gvf.fits anova.gvf.fit rstudent.gvf.fits rstudent.gvf.fit rstandard.gvf.fits rstandard.gvf.fit predict.gvf.fits predict.gvf.fit predictCV.gvf.fits predictCV.gvf.fit predictCV.default drop.gvf.points.gvf.fit drop.gvf.points.default drop.gvf.points getR2.gvf.fits getR2.gvf.fit getR2.default BIC.gvf.fits AIC.gvf.fits plot.gvf.fits plot.gvf.fit print.summary.gvf.fits print.summary.gvf.fit summary.gvf.fits summary.gvf.fit print.gvf.fits print.gvf.fit `[[.gvf.fits` `[.gvf.fits` fit.gvf.gvf.input fit.gvf.default fit.gvf plot.gvf.input is.dummy is.ones vars.exist kind.match Estimator.kind gvf.input

Documented in AIC.gvf.fits anova.gvf.fit anova.gvf.fits BIC.gvf.fits coef.gvf.fit coef.gvf.fits drop.gvf.points drop.gvf.points.default drop.gvf.points.gvf.fit effects.gvf.fit effects.gvf.fits fit.gvf fit.gvf.default fit.gvf.gvf.input fitted.gvf.fit fitted.gvf.fits getBest getBest.default getBest.gvf.fit getBest.gvf.fits getR2.default getR2.gvf.fit getR2.gvf.fits gvf.input plot.gvf.fit plot.gvf.fits plot.gvf.input predictCV.default predictCV.gvf.fit predictCV.gvf.fits predict.gvf.fit predict.gvf.fits print.gvf.fit print.gvf.fits residuals.gvf.fit residuals.gvf.fits rstandard.gvf.fit rstandard.gvf.fits rstudent.gvf.fit rstudent.gvf.fits summary.gvf.fit summary.gvf.fits vcov.gvf.fit vcov.gvf.fits

gvf.input <- function(design, ..., stats = list(...)) {
####################################################################
# Given a set of survey statistic objects (or a list storing them) #
# and a design object from which the statics are supposed to have  #
# been derived, builds a dataframe storing all the data needed to  #
# fit a Generalized Variance Function (GVF) model.                 #
####################################################################

  # First verify if the function has been called inside another function:
  # this is needed to correctly manage metadata when e.g. the caller is a
  # GUI stratum
  directly <- !( length(sys.calls()) > 1 )
  design.expr <- if (directly) substitute(design)

  # Test design class
  if (!inherits(design, "analytic")) 
     stop("Object 'design' must inherit from class analytic")

  # Has 'stats' been passed?
  is.stats <- TRUE
  if (missing(stats)) {
      is.stats <- FALSE
    }

  # Check passed value of argument 'stats'
  if (data.class(stats)!="list") {
     warning("stats is not a list of survey statistics: will try to match the dot-dot-dot argument instead.")
     stats <- list(...)
     is.stats <- FALSE
    }
  else {
     # Strip off names of 'stats' list components
     names(stats) <- NULL
    }

  if (length(stats) < 1)
     stop("No survey estimates provided: cannot model sampling errors!")

  # Test that input objects are all survey statistics
  svystat.classes <- c("svystatTM", "svystatR", "svystatS", "svystatSR", "svystatL", "svystatQ", "svystatB", "svySigma", "svySigma2", "svyby")
  ok.stats <- sapply(stats, function(stat) any(sapply(svystat.classes, function(class) inherits(stat, class))))

  if (!all(ok.stats)) {
      where.txt <- if (is.stats) "in passed stats list" else "in dot-dot-dot argument"
      stop("All input objects must be survey statistics!\n (wrong objects positions ", where.txt,": ",
           paste(which(!ok.stats), collapse = ", "),")")
    }

  # Test that input statistics are all from the same design
  root.des <- sapply(stats, function(stat) {
                                     des <- attr(stat, "design")
                                     # if below returns NA when summary statistics
                                     # lacks a design attribute (e.g. when gvf.input
                                     # is invoked via example())
                                     if (!is.null(des)) as.character(des) else NA
                                    })
  # drop NAs (if any)
  root.des.noNA <- root.des[!is.na(root.des)]
  root.des.u <- unique(root.des.noNA)
  if (length(root.des.u) > 1) {
     root.des1 <- root.des[1]
     root.des2 <- root.des[2]
     stop("Input estimates come from different design objects!\n (e.g.: object ",
          match(root.des1, root.des)," from ", root.des1, ", ", match(root.des2, root.des)," from ", root.des2,")")
    }

  # Test that the common root design of all stats is actually what has been passed to 'design'
  # Note that first clause below skips cases where *all* stats lack the design attribute
  if ((length(root.des.u)==1) && !identical(design, get(root.des.u))){
      stop("Input estimates have not been computed from input design object!")
    }

  # Test that input statistics are elegible for a meaningful model
  stats.kind <- sapply(stats, Estimator.kind, design=design)
  stats.kind.u <- unique(stats.kind)
    # 1. Avoid mixed cases (i.e. svystatTM with both factor and numeric interest variables)
    mix <- stats.kind %in% c("Mix of Totals and Absolute Frequencies", "Mix of Means and Relative Frequencies")
    if (any(mix)) {
        stop("Some input estimates involve variables of different types, e.g. factor and numeric!\n (object positions: ",paste(which(mix), collapse = ", "),")")
    }
    # 2. Test that input statistics are all the same kind
    if (length(stats.kind.u) > 1) {
        kind1 <- stats.kind.u[1]
        kind2 <- stats.kind.u[2]
        # *Allow* (with a warning) two components mix "Complex Estimator" kind + "other kind"
        # The rationale is that svystatL can actually be used to compute exctly  "other kind"
        # of estimates! (i.e. no kind mismatch at all, actually)
        if ( (length(stats.kind.u) == 2) && any(stats.kind.u == "Complex Estimator")) {
            warning("Input estimators are not the same kind!\n (e.g.: object ",
                    match(kind1, stats.kind)," is a ", kind1, ", object ", match(kind2, stats.kind)," is a ",
                    kind2,")")
        } else {
            stop("Input estimators are not the same kind!\n (e.g.: object ",
                 match(kind1, stats.kind)," is a ", kind1, ", object ", match(kind2, stats.kind)," is a ", kind2,")")
        }
    }
    # 3. Warn if input statistics involve different variables
      # NOTE: CURRENTLY Works only for svystatTM objects (i.e. Totals, Means, Abs. and Rel. Freq.)
      #       as the rest generate NULL for ALL.y.vars below
      # NOTE: Check is meaningful for quantitative variables only: thus exludes frequencies
    ALL.y.vars <- unlist(lapply(stats, function(x) attr(x, "y.vars")))
    if (!is.null(ALL.y.vars)) {
         ALL.y.vars.u <- unique(ALL.y.vars)
         if ( (length(ALL.y.vars.u) > 1) && !(stats.kind.u %in% c("Absolute Frequency", "Relative Frequency")) ) {
             warning("Input estimates involve different interest variables: ", paste(ALL.y.vars.u, collapse = ", "))
         }
    }

    # Start building the estimates and errors dataframe
    Y    <- unlist(lapply(stats,coef))
    SE   <- unlist(lapply(stats,SE))
    CV   <- unlist(lapply(stats,cv))
    VAR  <- unlist(lapply(stats,VAR))
    name <- names(Y)
    data.to.fit <- data.frame(name = name, Y = Y, SE = SE, CV = CV, VAR = VAR)

    # Check for DEFFs
    getDEFF <- function(x) {
                 # NOTE: No deff() method available for quantiles estimators,
                 #       hence must avoid warnings that would be raised by
                 #       deff.default() 23/02/2016
                 if (data.class(x) == "svystatQ") {
                     DEFF <- NULL
                    }
                 else {
                     DEFF <- try(deff(x), silent=TRUE)
                    }
                 if (inherits(DEFF, "try-error") || is.null(DEFF)) {
                     DEFF.NA <- coef(x)
                     DEFF.NA[] <- NA
                     DEFF <- DEFF.NA
                    }
                 DEFF
                }
    DEFF <- unlist(lapply(stats, getDEFF))
    if (any(!is.na(DEFF))) {
         data.to.fit$DEFF <- DEFF
         has.Deff <- TRUE
        }
    else {
         has.Deff <- FALSE
        }

    # Drop duplicated rows that could arise from unintentionally
    # passing the same input stat more than once
    nobs.ini <- nrow(data.to.fit)
    data.to.fit <- unique(data.to.fit)
    nobs.fin <- nrow(data.to.fit)
    if (nobs.fin < nobs.ini) {
        warning("Duplicated estimates have been dropped: ", nobs.ini - nobs.fin)
    }

    # Renumber rownames
    rownames(data.to.fit) <- NULL

    # Attach useful attributes to the output dataframe
    attr(data.to.fit, "y.vars") <- if (!is.null(ALL.y.vars)) ALL.y.vars.u
    attr(data.to.fit, "stats.kind") <- stats.kind.u
    attr(data.to.fit, "has.Deff") <- has.Deff
    attr(data.to.fit, "design") <- design.expr
    class(data.to.fit) <- c("gvf.input", class(data.to.fit))

  # Return value
  data.to.fit
}


  Estimator.kind <- function(stat, design) {
  #####################################################
  # Given a survey statistic object and a design from #
  # which the former is supposed to have been derived #
  # identify the 'precise kind' of the estimator.     #
  # Currently, possible return values (i.e. kinds)    #
  # are the following:                                #
  # (1)  'Total'                                      #
  # (2)  'Absolute Frequency'                         #
  # (3)  'Mix of Totals and Absolute Frequencies'     #
  # (4)  'Mean'                                       #
  # (5)  'Relative Frequency'                         #
  # (6)  'Mix of Means and Relative Frequencies'      #
  # (7)  'Ratio'                                      #
  # (8)  'Share'                                      #
  # (9)  'Share Ratio'                                #
  # (10) 'Regression Coefficient'                     #
  # (11) 'Quantile'                                   #
  # (12) 'Complex Estimator'                          #
  # (13) 'Population Variance'                        #
  # (14) 'Population Standard Deviation'              #
  #                                                   #
  # NOTE: This is the unexported version of function  #
  #       estimator.kind, which has more error        #
  #       handling statements.                        #
  #####################################################

    # First level assessment: what survey statistics function?
    kind <- switch(data.class(stat),
                   svystatTM = "TM", svystatTM.by = "TM",
                   svystatR  = "Ratio", svystatR.by = "Ratio",
                   svystatS  = "Share", svystatS.by = "Share",
                   svystatSR = "Share Ratio", svystatSR.by = "Share Ratio",
                   svystatB  = "Regression Coefficient", svystatB.by  = "Regression Coefficient",
                   svystatQ  = "Quantile", svystatQ.by = "Quantile",
                   svystatL  = "Complex Estimator", svystatL.by = "Complex Estimator",
                   svySigma2 = "Population Variance", svySigma2.by = "Population Variance",
                   svySigma  = "Population Standard Deviation", svySigma.by = "Population Standard Deviation")
    if (kind=="TM") {
        estfun <- if (!inherits(stat, "svyby")) {
                      attr(attr(stat, "origin"),"svyby")$statistic
                    }
                  else {
                      attr(stat,"svyby")$statistic
                    }

    # Second level assessment: what estimator? (i.e. split Total from Mean)
    if (estfun=="svytotal") kind <- "Total" else kind <- "Mean"

    # Third level assessment: numeric vs. factor variables, pure vs. mixed estimates
    y.vars <- attr(stat, "y.vars")

    # Test whether the variables exist in design
    vars.exist(y.vars, design)

    # Take into account variable types
    y.num  <- sapply(y.vars, function(var) is.numeric(design$variables[[var]]))

    # Treat 'ones' variables (i.e. columns of 1s) and
    # 'dummy' variables (i.e. columns of 0/1s) as if they were factors:
    # indeed they imply estimates of frequencies...
    y.od <- sapply(y.vars, function(var) is.ones(var, design) | is.dummy(var, design))

    # ...then identify REAL numeric variables
    y.Num <- y.num & !y.od

    if (kind=="Total") {
         if (all(!y.Num)) kind <- "Absolute Frequency"
         if (any(y.Num) && any(!y.Num)) kind <- "Mix of Totals and Absolute Frequencies"
        }

    if (kind=="Mean") {
         if (all(!y.Num)) kind <- "Relative Frequency"
         if (any(y.Num) && any(!y.Num)) kind <- "Mix of Means and Relative Frequencies"
        }
    }

    kind
  }


  kind.match <- function(kind, GVF.db.kind) {
  ###################################################################
  # Let 'kind' be the "stats.kind" attribute of a gvf.input object  #
  # and 'GVF.db.kind' the "Estimator.kind" of a registered model,   #
  # the function returns TRUE if the inputs are coherent, otherwise #
  # returns a diagnostic message.                                   #
  # NOTE: if 'GVF.db.kind' is NA (which is indeed a legal velue,    #
  #       see ?GVF.db), returns TRUE.                               #
  ###################################################################
    if (is.na(GVF.db.kind)) {
         return(TRUE)
        }
    if (kind == GVF.db.kind) {
         return(TRUE)
        }
    else {
         if ( (kind %in% c("Absolute Frequency", "Relative Frequency")) && (GVF.db.kind == "Frequency")) {
             return(TRUE)
            }
         else {
             warn.char <- paste("NOTE: fitted statistics have kind '", kind,
                                "'\n      whereas registered model has kind '", GVF.db.kind, "'!", sep = "")
             return(warn.char)
            }
        }
  }


  vars.exist <- function(vars, design) {
  #####################################
  # Check if variables named 'vars'   #
  # do actually exist inside 'design' #
  #####################################
    data <- design$variables
    vars <- unique(vars)
    absent.vars <- vars[!(vars %in% names(data))]
    if (length(absent.vars) > 0)
        stop("Variables not found: ", paste(absent.vars, collapse = ", "))
  }


  is.ones <- function(var, design, tol = 1E-6) {
  #######################################
  # Check if the variable named 'var'   #
  # is a column of 1s (within tolerance #
  # tol (which defaults to 1E-6).       #
  #######################################
    # vars.exist(var, design) #unnecessary: if var doesn't exist, returns FALSE!
    val <- design$variables[[var]]
    out <- FALSE
    if (is.numeric(val)) {
         test <- all.equal(sum(abs(range(val)-1)), 0, tol = tol)
         if (isTRUE(test)) {
             out <- TRUE
            }
        }
    out
  }


  is.dummy <- function(var, design, tol = 1E-6) {
  ########################################
  # Check if the variable named 'var' is #
  # a column of 1/0s (within tolerance   #
  # tol (which defaults to 1E-6).        #
  ########################################
    # vars.exist(var, design) #unnecessary: if var doesn't exist, returns FALSE!
    val <- design$variables[[var]]
    out <- FALSE
    if (is.numeric(val)) {
         test <- all.equal(sum(abs(range(val)-c(0,1))), 0, tol = tol)
         if (isTRUE(test)) {
             out <- TRUE
            }
        }
    out
  }


#`[.gvf.input` <- function(x, i, j)
################################################
# Subsetting method for 'gvf.input' objects.   #
# NOTE: The original attributes are preserved. #
################################################
#{
#    y <- `[.data.frame`(x, i, j, drop=FALSE)
#    attr(y, "y.vars") <- attr(x, "y.vars")
#    attr(y, "stats.kind") <- attr(x, "stats.kind")
#    attr(y, "has.Deff") <- attr(x, "has.Deff")
#    attr(y, "design") <- attr(y, "design")
#    class(y) <- class(x)
#    y
#}


plot.gvf.input <- function(x, ...){
##############################################
# Plot method on gvf.input objects: yields a #
# scatterplot matrix + polynomial smoother.  #
##############################################
  xx <- as.data.frame(x[,-1])
  plot(xx, panel=panel.smooth, ...)
# OLD CODE
# graphics:::plot.data.frame(x[,-1], panel=panel.smooth)
# This would avoid using ':::' but unfortunately can't manage to avoid plotting
# the first column...
# NextMethod("plot", object = x[,-1], panel=panel.smooth)
}


fit.gvf <- function(gvf.input, model = NULL, weights = NULL)
#################################################
# Generic function. Fit one or many GVF models. #
#################################################
{
UseMethod("fit.gvf")
}

fit.gvf.default <- function(gvf.input, model = NULL, weights = NULL)
####################################
# Default method: raises an error. #
####################################
{
stop("Object 'gvf.input' must inherit from class gvf.input or gvf.input.gr!")
}

fit.gvf.gvf.input <- function(gvf.input, model = NULL, weights = NULL) {
################################################################################
# This function fits one or more GVF models to a set of survey statistics.     #
# The primary purpose of fitting multiple models is for comparison: the user   #
# is expected to eventually choose his preferred model, in order to obtain     #
# sampling errors predictions.                                                 #
#                                                                              #
# Parameter 'model' can be either:                                             #
# 1) NULL (the default) meaning *all* registered models in GVF.db              #
# or:                                                                          #
# 2) any sub-vector of GVF.db$db$Model.id (i.e. an arbitrary selection of      #
#    registered models)                                                        #
# or:                                                                          #
# 3) an arbitrary (single) formula (i.e. any custom, user-defined model).      #
#                                                                              #
# NOTE: The class of the output is 'gvf.fit' if just a single model has been   #
#       fit, otherwise it is 'gvf.fits'. Objects of class 'gvf.fits'           #
#       essentially behave as lists of objects of class 'gvf.fit'.             #
################################################################################

# First verify if the function has been called inside another function:
# this is needed to correctly manage metadata when e.g. the caller is a
# GUI stratum
directly <- !( length(sys.calls()) > 2L )
gvf.input.expr <- if (directly) substitute(gvf.input)

# Test gvf.input class
if (!inherits(gvf.input, "gvf.input")) 
     stop("Object 'gvf.input' must inherit from class gvf.input")

# Check model type:
if (is.null(model)) {
     # i.e. *all* registered models
     model <- GVF.db$db$Model.id
    }
if (!inherits(model, "formula") && !is.numeric(model)) {
     stop("GVF models can be specified only as formulae or by passing the numeric ids of registered models! (see ?GVF.db)")
    }

#!!!!!! MANDATORY, otherwise cannot pass weights when calling lm
this.env <- environment()

  # 1) numeric case (i.e. registered models)
if (is.numeric(model)) {
     model.id <- sort(intersect(model, GVF.db$db$Model.id))
     if (length(model.id) < 1) {
         stop("Specified model doesn't match any available registered GVF! (see ?GVF.db)")
        }
     model.char <- GVF.db$db[GVF.db$db$Model.id %in% model.id, "GVF.model"]
     estkind.char <- GVF.db$db[GVF.db$db$Model.id %in% model.id, "Estimator.kind"]
     model.form <- lapply(model.char, FUN = as.formula, env = this.env)
     if (length(model.form) == 1) {
         model.form <- model.form[[1]]
        }
     # must check estimator kinds coherence? YES
     estkind.check <- TRUE
    }

  # 2) formula case (i.e. a custom model)
if (inherits(model, "formula")) {
     ## Set model.id to NULL (i.e. a custom model is being fit)
     model.id <- NULL

     # Run all checks on GVF.model at once
     GVF.moldel.check(model)

     model.form <- model
     #!!!!!! MANDATORY, otherwise cannot pass weights when calling lm
     environment(model.form) <- this.env
     # must check estimator kinds coherence? NO
     estkind.check <- FALSE
    }

# Check weights:
if (is.null(weights)) {
     w.char <- "NULL"
     weights <- NULL
    }
else {
     if (!inherits(weights,"formula"))
         stop("If supplied, weights must be passed as a formula")
     if (length(weights) >= 3)
         stop("weights formula must be one-sided!")
     w.char <- form.to.char(weights)
     w.form <- as.formula(paste(w.char, "- 1"))
     mf <- model.frame(w.form, gvf.input, na.action = na.pass)
     weights <- model.matrix(w.form, mf)
    }

# Fit the provided gvf model(s)
if (is.list(model.form)) {
     elm <- vector(mode = "list", length = length(model.form))
     i.lm <- 0
     for (form in model.form) {
          i.lm <- i.lm + 1
          elm[[i.lm]] <- lm(formula = form, data = gvf.input, weights = weights,
                            na.action = na.exclude, model = TRUE,
                            x = TRUE, y = TRUE)
          # Re-introduce NAs (if any) in response, as they have been dropped from $y slot
          if (!is.null(nas <- elm[[i.lm]]$na.action)) {
              y <- elm[[i.lm]]$y
              resp <- rep(NA, length(y) + length(nas))
              resp[-nas] <- y
              resp.names <- rep(NA, length(y) + length(nas))
              resp.names[-nas] <- names(y)
              resp.names[nas]  <- names(nas)
              names(resp) <- resp.names
              elm[[i.lm]]$resp <- resp
            }
          else {
              elm[[i.lm]]$resp <- elm[[i.lm]]$y
            }
          # Check estimator kind consistency
          chk <- kind.match(attr(gvf.input, "stats.kind"), estkind.char[i.lm])
          # Set attributes
          attr(elm[[i.lm]], "model") <- twosideform.to.char(form)
          attr(elm[[i.lm]], "model.id") <- model.id[i.lm]
          attr(elm[[i.lm]], "kind.mismatch") <- if (!isTRUE(chk)) chk
          attr(elm[[i.lm]], "weights") <- w.char
          attr(elm[[i.lm]], "gvf.input.expr") <- gvf.input.expr
          class(elm[[i.lm]]) <- c("gvf.fit", class(elm[[i.lm]]))
        }
    }
else {
      elm <- lm(formula = model.form, data = gvf.input, weights = weights,
                na.action = na.exclude, model = TRUE,
                x = TRUE, y = TRUE)
      # Re-introduce NAs (if any) in response, as they have been dropped from $y slot
      if (!is.null(nas <- elm$na.action)) {
          y <- elm$y
          resp <- rep(NA, length(y) + length(nas))
          resp[-nas] <- y
          resp.names <- rep(NA, length(y) + length(nas))
          resp.names[-nas] <- names(y)
          resp.names[nas]  <- names(nas)
          names(resp) <- resp.names
          elm$resp <- resp
        }
      else {
          elm$resp <- elm$y
        }
      # If needed, check estimator kind consistency
      if (estkind.check) {
          chk <- kind.match(attr(gvf.input, "stats.kind"), estkind.char)
        }
      # Set attributes
      attr(elm, "model") <- twosideform.to.char(model.form)
      attr(elm, "model.id") <- model.id
      attr(elm, "kind.mismatch") <- if (estkind.check && !isTRUE(chk)) chk
      attr(elm, "weights") <- w.char
      attr(elm, "gvf.input.expr") <- gvf.input.expr
      class(elm) <- c("gvf.fit", class(elm))
    }

# Set other attributes
attr(elm, "gvf.input") <- gvf.input

if (inherits(elm, "list")) {
     class(elm) <- c("gvf.fits", class(elm))
    }
# NOTE: if output inherits from list, then it is a collection of 'gvf.fit'
# objects, i.e. a 'gvf.fits' object
elm
}


`[.gvf.fits` <- function(x, ...)
#############################################
# Subsetting method for 'gvf.fits' objects. #
# NOTE: The original 'gvf.input' attributes #
#       are preserved.                      #
#############################################
{
    y <- NextMethod("[")
    attr(y, "gvf.input") <- attr(x, "gvf.input")
    attr(y, "gvf.input.expr") <- attr(x, "gvf.input.expr")
    class(y) <- class(x)
    y
}


`[[.gvf.fits` <- function(x, ...)
##################################################
# Extract method for 'gvf.fits' objects. Recall  #
# that components of x are actually 'gvf.fit'    #
# objects.                                       #
# NOTE: The original 'gvf.input' attributes is   #
#       preserved.                               #
##################################################
{
    y <- NextMethod("[[")
    attr(y, "gvf.input") <- attr(x, "gvf.input")
    y
}


print.gvf.fit <- function(x, digits = max(3L, getOption("digits") - 3L), ...)
#######################################
# Print method for 'gvf.fit' objects. #
#######################################
{
    # Vertical separator
    vsep <- paste(rep("-", 0.5 * getOption("width")), collapse = "")

    cat(vsep, "\n")
    cat("GVF model:  ", attr(x, "model"),   "\n")
    if (!is.null(attr(x, "model.id"))) {
         cat("- model.id: ", attr(x, "model.id"), "\n")
        }
    if (!is.null(attr(x, "gvf.input.expr"))) {
         cat("- data:     ", deparse(attr(x, "gvf.input.expr")), "\n")
        }
    cat("- weights:  ", attr(x, "weights"), "\n")
    cat("\n")

    if (length(coef(x))) {
         cat("Coefficients:\n")
         print.default(format(coef(x), digits = digits), print.gap = 2L, 
                       quote = FALSE)
        }
    else {
         cat("No coefficients\n")
        }

    if (!is.null(attr(x, "kind.mismatch"))) {
         cat("\n")
         cat(attr(x, "kind.mismatch"), "\n")
        }

    cat("\n\n")
    invisible(x)
}


print.gvf.fits <- function(x, digits = max(3L, getOption("digits") - 3L), ...)
########################################
# Print method for 'gvf.fits' objects. #
########################################
{
    for (e in x) {
        # print.gvf.fit(e, digits = digits, ...)
         print(e, digits = digits, ...) # PROVA!
        }
    invisible(x)
}


summary.gvf.fit <- function(object, correlation = FALSE,
                            symbolic.cor = FALSE, ...)
#########################################
# Summary method for 'gvf.fit' objects. #
#########################################
{
    s <- summary.lm(object, correlation = correlation,
                    symbolic.cor = symbolic.cor, ...)
    attr(s, "model")          <- attr(object, "model")
    attr(s, "model.id")       <- attr(object, "model.id")
    attr(s, "kind.mismatch")  <- attr(object, "kind.mismatch")
    attr(s, "weights")        <- attr(object, "weights")
    attr(s, "gvf.input.expr") <- attr(object, "gvf.input.expr")
    class(s) <- c("summary.gvf.fit", class(s))
    s
}


summary.gvf.fits <- function(object, correlation = FALSE,
                             symbolic.cor = FALSE, ...)
##########################################
# Summary method for 'gvf.fits' objects. #
##########################################
{
    ss <- vector(mode = "list", length = length(object))
    i.ss <- 0
    for (e in object) {
         i.ss <- i.ss + 1
        # ss[[i.ss]] <- summary.gvf.fit(e, correlation = correlation,
        #                               symbolic.cor = symbolic.cor, ...)
         ss[[i.ss]] <- summary(e, correlation = correlation,
                               symbolic.cor = symbolic.cor, ...) # PROVA!
        }
    class(ss) <- c("summary.gvf.fits", class(ss))
    ss
}


print.summary.gvf.fit <- function(x, digits = max(3L, getOption("digits") - 3L),
                                  symbolic.cor = x$symbolic.cor, 
                                  signif.stars = getOption("show.signif.stars"),
                                  ...)
##################################################
# Print method for summary on 'gvf.fit' objects. #
##################################################
{
    # Vertical separator
    vsep <- paste(rep("-", 0.6 * getOption("width")), collapse = "")

    cat(vsep, "\n")
    cat("GVF model:  ", attr(x, "model"),   "\n")
    if (!is.null(attr(x, "model.id"))) {
         cat("- model.id: ", attr(x, "model.id"), "\n")
        }
    if (!is.null(attr(x, "gvf.input.expr"))) {
         cat("- data:     ", deparse(attr(x, "gvf.input.expr")), "\n")
        }
    cat("- weights:  ", attr(x, "weights"), "\n")
    cat("\n")

    resid <- x$residuals
    df <- x$df
    rdf <- df[2L]
    cat(if (!is.null(x$weights) && diff(range(x$weights))) 
        "Weighted ", "Residuals:\n", sep = "")
    if (rdf > 5L) {
        nam <- c("Min", "1Q", "Median", "3Q", "Max")
        rq <- if (length(dim(resid)) == 2L) 
            structure(apply(t(resid), 1L, quantile), dimnames = list(nam, 
                dimnames(resid)[[2L]]))
        else {
            zz <- zapsmall(quantile(resid), digits + 1L)
            structure(zz, names = nam)
        }
        print(rq, digits = digits, ...)
    }
    else if (rdf > 0L) {
        print(resid, digits = digits, ...)
    }
    else {
        cat("ALL", df[1L], "residuals are 0: no residual degrees of freedom!")
        cat("\n")
    }
    if (length(x$aliased) == 0L) {
        cat("\nNo Coefficients\n")
    }
    else {
        if (nsingular <- df[3L] - df[1L]) 
            cat("\nCoefficients: (", nsingular, " not defined because of singularities)\n", 
                sep = "")
        else cat("\nCoefficients:\n")
        coefs <- x$coefficients
        if (!is.null(aliased <- x$aliased) && any(aliased)) {
            cn <- names(aliased)
            coefs <- matrix(NA, length(aliased), 4, dimnames = list(cn, 
                colnames(coefs)))
            coefs[!aliased, ] <- x$coefficients
        }
        printCoefmat(coefs, digits = digits, signif.stars = signif.stars, 
            na.print = "NA", ...)
    }
    cat("\nResidual standard error:", format(signif(x$sigma, 
        digits)), "on", rdf, "degrees of freedom")
    cat("\n")
    if (nzchar(mess <- naprint(x$na.action))) 
        cat("  (", mess, ")\n", sep = "")
    if (!is.null(x$fstatistic)) {
        cat("Multiple R-squared: ", formatC(x$r.squared, digits = digits))
        cat(",\tAdjusted R-squared: ", formatC(x$adj.r.squared, 
            digits = digits), "\nF-statistic:", formatC(x$fstatistic[1L], 
            digits = digits), "on", x$fstatistic[2L], "and", 
            x$fstatistic[3L], "DF,  p-value:", format.pval(pf(x$fstatistic[1L], 
                x$fstatistic[2L], x$fstatistic[3L], lower.tail = FALSE), 
                digits = digits))
        cat("\n")
    }
    correl <- x$correlation
    if (!is.null(correl)) {
        p <- NCOL(correl)
        if (p > 1L) {
            cat("\nCorrelation of Coefficients:\n")
            if (is.logical(symbolic.cor) && symbolic.cor) {
                print(symnum(correl, abbr.colnames = NULL))
            }
            else {
                correl <- format(round(correl, 2), nsmall = 2, 
                  digits = digits)
                correl[!lower.tri(correl)] <- ""
                print(correl[-1, -p, drop = FALSE], quote = FALSE)
            }
        }
    }

    if (!is.null(attr(x, "kind.mismatch"))) {
         cat("\n")
         cat(attr(x, "kind.mismatch"), "\n")
        }

    cat("\n\n")
    invisible(x)
}


print.summary.gvf.fits <- function(x, digits = max(3L, getOption("digits") - 3L),
                                   symbolic.cor = x$symbolic.cor, 
                                   signif.stars = getOption("show.signif.stars"),
                                   ...)
###################################################
# Print method for summary on 'gvf.fits' objects. #
###################################################
{
    for (e in x) {
        # print.summary.gvf.fit(e, digits = digits, symbolic.cor = symbolic.cor,
        #                       signif.stars = signif.stars, ...)
         print(e, digits = digits, symbolic.cor = symbolic.cor,
               signif.stars = signif.stars, ...) # PROVA!
        }
    invisible(x)
}


plot.gvf.fit <- function(x, which.more = 1:3, id.n = 3,
                         labels.id = names(residuals(x)), cex.id = 0.75,
                         label.pos = c(4, 2), cex.caption = 1, Main = NULL, ...)
#####################################################
# Plot method for 'gvf.fit' objects.                #
# NOTE: which.more = NULL means that only the       #
#       'Observed vs Fitted' plot will be provided. #
#####################################################
{
    if (!is.null(which.more)) {
         if (!is.numeric(which.more) || any(which.more < 1) || any(which.more > 6) || (length(which.more) > 3)) {
             stop("If specified, 'which.more' must be in 1:6 and of length <= 3!")
            }
         # How many plots?
         np <- 1 + length(unique(which.more))
         # Set plot region according to the number of plots requested
         old.par <- if (np == 4) par(mfrow = c(2, 2)) else par(mfrow = c(1, np))
        }

    ### Plot1: Observed vs Fitted - START ###
    r <- residuals(x)
    yh <- predict(x)
    yobs <- x$resp
    w <- weights(x)
    if (!is.null(w)) {
         wind <- w != 0
         r <- r[wind]
         yh <- yh[wind]
         yobs <- yobs[wind]
         w <- w[wind]
         labels.id <- labels.id[wind]
        }
    n <- length(r)
    if (is.null(id.n)) {
         id.n <- 0
        }
    else {
         id.n <- as.integer(id.n)
         if (id.n < 0L || id.n > n) 
            stop(gettextf("'id.n' must be in {1,..,%d}", n), domain = NA)
        }
    if (id.n > 0L) {
         if (is.null(labels.id)) 
             labels.id <- paste(1L:n)
         iid <- 1L:id.n
         show.r <- sort.list(abs(r), decreasing = TRUE)[iid]
         text.id <- function(x, y, ind, adj.x = TRUE) {
                             labpos <- if (adj.x) {
                                         label.pos[1 + as.numeric(x > mean(range(x)))]
                                        }
                                       else 3
                             text(x, y, labels.id[ind], cex = cex.id,
                                  xpd = TRUE, pos = labpos, offset = 0.25)
                            }
        }
    ylim <- range(yobs, na.rm = TRUE)
    if (id.n > 0) {
         ylim <- extendrange(r = ylim, f = 0.08)
        }
    dev.hold()
    plot(yh, yobs, xlab = "Fitted values", ylab = "Observed values",
         ylim = ylim, main = Main, ...)
    mtext("Observed vs Fitted", 3, 0.25, cex = cex.caption)
    if (id.n > 0) {
         y.id <- yobs[show.r]
         y.id[y.id < 0] <- y.id[y.id < 0] - strheight(" ")/3
         text.id(yh[show.r], y.id, show.r)
        }
    abline(0:1, col = "red")
    dev.flush()
    ### Plot1: Observed vs Fitted - END ###

    if (!is.null(which.more)) {
         stats_plot.lm(x, which = which.more, id.n = id.n, labels.id = labels.id,
                       cex.id = cex.id, label.pos = label.pos,
                       cex.caption = cex.caption, main = Main, ...)
         par(old.par)
        }
}


plot.gvf.fits <- function(x, which.more = NULL, id.n = 3,
                          labels.id = names(residuals(x)), cex.id = 0.75,
                          label.pos = c(4, 2), cex.caption = 1, Main = NULL, ...)
#########################################################
# Plot method for 'gvf.fits' objects.                   #
# NOTE: The default value of which.more differ from the #
#       one adopted for class gvf.fit and provides just #
#       the 'Observed vs Fitted' plot.                  #
#########################################################
{
    # How many models?
    nm <- length(x)
    # PROVA! Must finalize this function...
    if (nm > 1) {
         oask <- devAskNewPage(TRUE)
         on.exit(devAskNewPage(oask))
        }
    for (m in x) {
         Model.lab <- paste("Model: ", attr(m, "model"), sep = "")
         main <- if (is.null(Main)) Model.lab else paste(Main, " - ", Model.lab, sep = "")

         plot(m, which.more = which.more, id.n = id.n, labels.id = labels.id,
              cex.id = cex.id, label.pos = label.pos, cex.caption = cex.caption,
              Main = main, ...)
        }
}


###############
# AIC methods #
###############
# No need of AIC.gvf.fit
AIC.gvf.fits <- function(object, ...)
{
    AA <- vector(mode = "numeric", length = length(object))
    i.AA <- 0
    for (e in object) {
         i.AA <- i.AA + 1
         AA[i.AA] <- AIC(e, ...)
        }
    AA
}


###############
# BIC methods #
###############
# No need of BIC.gvf.fit
BIC.gvf.fits <- function(object, ...)
{
    BB <- vector(mode = "numeric", length = length(object))
    i.BB <- 0
    for (e in object) {
         i.BB <- i.BB + 1
         BB[i.BB] <- BIC(e, ...)
        }
    BB
}


getR2 <- function (object, adjusted = FALSE, ...){
##############################################
# Generic function. Get R2 (adjusted or not) #
# of fitted GVF models.                      #
##############################################
UseMethod("getR2")
}

getR2.default <- function(object, adjusted = FALSE, ...)
####################################
# Default method: raises an error. #
####################################
{
stop("Input object is not a fitted GVF model!")
}

getR2.gvf.fit <- function(object,
                          adjusted = FALSE, ...)
###################
# gvf.fit method. #
###################
{
s <- summary(object)
if (isTRUE(adjusted)) {
     r <- s$adj.r.squared
    }
else {
     r <- s$r.squared
    }
r
}

getR2.gvf.fits <- function(object,
                           adjusted = FALSE, ...)
####################
# gvf.fits method. #
####################
{
    rr <- vector(mode = "numeric", length = length(object))
    i.rr <- 0
    for (e in object) {
         i.rr <- i.rr + 1
         rr[i.rr] <- getR2(e, adjusted = adjusted, ...)
        }
    rr
}


drop.gvf.points <- function(x, method = c("pick", "cut"), which.plot = 1:2,
                            res.type = c("standard", "student"), res.cut = 3,
                            id.n = 3, labels.id = NULL,
                            cex.id = 0.75, label.pos = c(4, 2),
                            cex.caption = 1, col = NULL, drop.col = "red",
                            ...)
#############################################
# Generic function. Drops observations from #
# a fitted GVF model and refits the model.  #
#############################################
{
UseMethod("drop.gvf.points")
}

drop.gvf.points.default <- function(x, method = c("pick", "cut"), which.plot = 1:2,
                                    res.type = c("standard", "student"), res.cut = 3,
                                    id.n = 3, labels.id = NULL,
                                    cex.id = 0.75, label.pos = c(4, 2),
                                    cex.caption = 1, col = NULL, drop.col = "red",
                                    Main = NULL, ...)
####################################
# Default method: raises an error. #
####################################
{
stop("Input object must be a single fitted GVF model! (i.e. of class gvf.fit or gvf.fit.gr)")
}

drop.gvf.points.gvf.fit <- function(x, method = c("pick", "cut"), which.plot = 1:2,
                                    res.type = c("standard", "student"), res.cut = 3,
                                    id.n = 3, labels.id = NULL,
                                    cex.id = 0.75, label.pos = c(4, 2),
                                    cex.caption = 1, col = NULL, drop.col = "red",
                                    Main = NULL, ...)
##################################################################################
# This function drops observations from a fitted GVF model and refits the model. #
# Method for class gvf.fit.                                                      #
#                                                                                #
# If method = "pick", observations to be dropped are identified by clicking on   #
# points of a plot. Argumemt 'which.plot' determines the nature of the plot:     #
# value 1 is for 'Observed vs Fitted', value 2 is for 'Residuals vs Fitted'. In  #
# the latter case, argument 'res.type' specifies what kind of residuals have to  #
# be plotted. Argument 'id.n' specifies how many points have to be labelled      #
# initially, starting with the most extreme in terms of the selected residuals:  #
# this applies to both kinds of plots.                                           #
#                                                                                #
# If method = "cut", observations to be dropped are those with residuals whose   #
# absolute value exceeds the value of argument 'res.cut'. Again, argument        #
# 'res.type' specifies what kind of residuals have to be used. The points which  #
# have been cut are highlighted on a plot, whose nature is again specified by    #
# argument 'which.plot'. If which.plot = 1:2, dropped points are visualized on   #
# both the 'Observed vs Fitted' and the 'Residuals vs Fitted' graphs             #
# simultaneously.                                                                #
#                                                                                #
# All the other arguments have the same meaning and function as in plot.lm       #
##################################################################################
{
    # Check on x: must be a single fitted GVF model
    if (!inherits(x, "gvf.fit")) stop("Input object must be a single fitted GVF model!")

    method <- match.arg(method)
    if (method == "pick") {
         if (!missing(which.plot)) {
             if (!is.numeric(which.plot) || any(which.plot < 1) || any(which.plot > 2) || (length(which.plot) > 1)) {
                 stop("For method = 'pick', 'which.plot' must be either 1 or 2! (1 means 'Observed vs Fitted', 2 means 'Residuals vs Fitted')")
                }
            }
         else {
             which.plot <- which.plot[1]
            }
         # Trigger graphic device to prompt user:
         oask <- devAskNewPage(TRUE)
         on.exit(devAskNewPage(oask))
    }
    if (method == "cut") {
         if (!is.numeric(which.plot) || any(which.plot < 1) || any(which.plot > 2) || (length(which.plot) > 2)) {
             stop("For method = 'cut', 'which.plot' must be in 1:2! (1 means 'Observed vs Fitted', 2 means 'Residuals vs Fitted')")
            }
         which.plot <- unique(which.plot)
    }

    res.type <- match.arg(res.type)
    get.residuals <- switch(res.type,  "standard" = rstandard, "student" = rstudent)
    if (!is.numeric(res.cut) || (res.cut < 0)) stop("Argument 'res.cut' must be positive!")

    # How many and which plots?
    if (length(which.plot) == 2) {
         old.par <- par(mfrow = c(1, 2))
        }
    show <- rep(FALSE, 2)
    show[which.plot] <- TRUE

    # Get initial color for points
    if (is.null(col)) col <- par("col")

    if (show[1L]) {
    ### Plot1: Observed vs Fitted - START ###
#         r <- residuals(x) !OLD CODE USED rough RESIDUALS!
         r <- get.residuals(x)
         yh <- predict(x)
         yobs <- x$resp
         w <- weights(x)
         if (!is.null(w)) {
             wind <- w != 0
             r <- r[wind]
             yh <- yh[wind]
             yobs <- yobs[wind]
             w <- w[wind]
             labels.id <- labels.id[wind]
            }
         n <- length(r)

         if (method == "cut") {
             # Observations to cut
             id.n <- sum(abs(r) > res.cut, na.rm = TRUE)
            }
         if (is.null(id.n)) {
             id.n <- 0
            }
         else {
             id.n <- as.integer(id.n)
             if (id.n < 0L || id.n > n) 
                 stop(gettextf("'id.n' must be in {1,..,%d}", n), domain = NA)
            }
         if (id.n > 0L) {
             if (is.null(labels.id)) 
                 labels.id <- paste(1L:n)
                 iid <- 1L:id.n
                 show.r <- sort.list(abs(r), decreasing = TRUE)[iid]
                 text.id <- function(x, y, ind, adj.x = TRUE, ...) {
                                     labpos <- if (adj.x) {
                                                     label.pos[1 + as.numeric(x > mean(range(x)))]
                                                    }
                                               else 3
                                     text(x, y, labels.id[ind], cex = cex.id,
                                          xpd = TRUE, pos = labpos, offset = 0.25, ...)
                                    }
            }
         ylim <- range(yobs, na.rm = TRUE)
         if (id.n > 0) {
             ylim <- extendrange(r = ylim, f = 0.08)
            }
         dev.hold()
         # Set new color for cut points (if any)
         plot.col <- rep_len(col, n)
         if ( (method == "cut") && (id.n > 0) ) {
             plot.col[show.r] <- drop.col
            }
         plot(yh, yobs, xlab = "Fitted values", ylab = "Observed values",
              ylim = ylim, col = plot.col, main = Main, ...)
         mtext("Observed vs Fitted", 3, 0.25, cex = cex.caption)
         if (id.n > 0) {
             y.id <- yobs[show.r]
             y.id[y.id < 0] <- y.id[y.id < 0] - strheight(" ")/3
             text.col <- if (method == "cut") drop.col
             text.id(yh[show.r], y.id, show.r, col = text.col)
            }
         abline(0:1, col = "red")
         dev.flush()
    ### Plot1: Observed vs Fitted - END ###
    }

    if (show[2L]) {
    ### Plot2: Residuals vs Fitted - START ###
         res.string <- switch(res.type,  "standard" = "Standardized Residuals", "student" = "Studentized Residuals")
         r <- get.residuals(x)
         yh <- predict(x)
         w <- weights(x)
         if (!is.null(w)) {
             wind <- w != 0
             r <- r[wind]
             yh <- yh[wind]
             w <- w[wind]
             labels.id <- labels.id[wind]
            }
         n <- length(r)

         if (method == "cut") {
             # Observations to cut
             id.n <- sum(abs(r) > res.cut, na.rm = TRUE)
            }
         if (is.null(id.n)) {
             id.n <- 0
            }
         else {
             id.n <- as.integer(id.n)
             if (id.n < 0L || id.n > n) 
                 stop(gettextf("'id.n' must be in {1,..,%d}", n), domain = NA)
            }
         if (id.n > 0L) {
             if (is.null(labels.id)) 
                 labels.id <- paste(1L:n)
                 iid <- 1L:id.n
                 show.r <- sort.list(abs(r), decreasing = TRUE)[iid]
                 text.id <- function(x, y, ind, adj.x = TRUE, ...) {
                                     labpos <- if (adj.x) {
                                                     label.pos[1 + as.numeric(x > mean(range(x)))]
                                                    }
                                               else 3
                                     text(x, y, labels.id[ind], cex = cex.id,
                                          xpd = TRUE, pos = labpos, offset = 0.25, ...)
                                    }
            }
         ylim <- range(r, na.rm = TRUE)
         if (id.n > 0) {
             ylim <- extendrange(r = ylim, f = 0.08)
            }
         dev.hold()
         # Set new color for cut points (if any)
         plot.col <- rep_len(col, n)
         if ( (method == "cut") && (id.n > 0) ) {
             plot.col[show.r] <- drop.col
            }
         plot(yh, r, xlab = "Fitted values", ylab = res.string,
              ylim = ylim, col = plot.col, main = Main, ...)
         panel.smooth(yh, r, col = plot.col, ...)
         mtext(paste(res.string, " vs Fitted", sep = ""), 3, 0.25, cex = cex.caption)
         if (id.n > 0) {
             y.id <- r[show.r]
             y.id[y.id < 0] <- y.id[y.id < 0] - strheight(" ")/3
             text.col <- if (method == "cut") drop.col
             text.id(yh[show.r], y.id, show.r, col = text.col)
            }
         abline(h = 0, lty = 3, col = "gray")
         if (method == "cut") {
             abline(h =   res.cut, lty = 2, col = drop.col)
             abline(h = - res.cut, lty = 2, col = drop.col)
            }
         dev.flush()
    ### Plot2: Residuals vs Fitted - END ###
    }


    if (method == "pick") {
    # Identify points by mouse click...
         # What variable was there on the y axis?
         if (which.plot == 1) yy <- yobs
         if (which.plot == 2) yy <- r
         ii <- identify(yh, yy, labels = labels.id, 
                        cex = cex.id, col = drop.col)
        }
    if (method == "cut") {
    # Identify points by residuals threshold...
         if (id.n > 0L) { 
             ii <- show.r
            }
         else {
             ii <- NULL
            }
         # If needed, re-set graphical parameters
         if (length(which.plot) == 2) par(old.par)        
        }


    if ( (n.ii <- length(ii)) > 0) {
         # Now subset gvf.input by dropping identified points...
         gvf.input <- attr(x, "gvf.input")
         gvf.input <- gvf.input[-ii, ]

         # Lastly re-fit the obtained gvf.input with the same model...
         model <- attr(x, "model")
         model.form <- as.formula(model)
         w.char <- attr(x, "weights")
         if (w.char != "NULL"){
             weights <- as.formula(w.char)
            }
         else {
             weights <- NULL
            }

         # Was x a registered GVF model?
         old.model.id <- attr(x, "model.id")
         if (!is.null(old.model.id)) {
             # Is there still in GVF.db an entry with the same Model.id?
             if (any(GVF.db$db$Model.id == old.model.id)) {
                 now.model <- GVF.db$db$GVF.model[GVF.db$db$Model.id == old.model.id]
                 # Check if GVF.db entry with the old model id has been changed
                 # since the time the old model x was fitted
                 if (!identical(model, now.model)) {
                     warning("Input GVF fitted model does not match the corresponding entry in GVF models db!\n (perhaps you changed GVF.db after fitting?)")
                     # drop useless Model.id info
                     old.model.id <- NULL
                    }
                }
             else {
                 # No GVF.db entry with same Model.id: drop useless Model.id info
                 old.model.id <- NULL
                }
            }

         # Use GVF Model.id info when meaningful, else re-fit using the model formula
         if (!is.null(old.model.id)) {
             out <- fit.gvf(gvf.input = gvf.input, model = old.model.id, weights = weights)
            }
         else {
             out <- fit.gvf(gvf.input = gvf.input, model = model.form, weights = weights)
            }
         # Copy 'gvf.input.expr' attribute from x, as out would loose it being fit.gvf *not* invoked directly
         attr(out, "gvf.input.expr") <- attr(x, "gvf.input.expr")

           # Notify how many cases have been dropped...
           cat("\n# GVF model has been re-fitted after having dropped ", n.ii," observations", sep="")
           cat("\n#")
           # ...the corresponding *R2* variation
           cat("\n# - R^2             passed from ", getR2(x)," to ", getR2(out), sep="")
           # ...the corresponding *adjusted R2* variation
           cat("\n# - Adjusted R^2    passed from ", getR2(x, adjusted = TRUE)," to ", getR2(out, adjusted = TRUE),
               sep="")
           # ...the corresponding *AIC* variation
           cat("\n# - AIC             passed from ", AIC(x)," to ", AIC(out), sep="")
           # ...the corresponding adjusted *BIC* variation
           cat("\n# - BIC             passed from ", BIC(x)," to ", BIC(out), "\n\n", sep="")
         out
        }
     else {
         x
        }
}


predictCV <- function (object, new.Y = NULL,
                       scale = NULL, df = Inf,
                       interval = c("none", "confidence", "prediction"),
                       level = 0.95, na.action = na.pass,
                       pred.var = NULL, weights = 1)
#######################################
# Generic function. Predict CV values #
# on the basis of a fitted GVF model. #
#######################################
{
UseMethod("predictCV")
}

predictCV.default <- function(object, new.Y = NULL,
                              scale = NULL, df = Inf,
                              interval = c("none", "confidence", "prediction"),
                              level = 0.95, na.action = na.pass,
                              pred.var = NULL, weights = 1)
####################################
# Default method: raises an error. #
####################################
{
stop("Input object is not a fitted GVF model!")
}

predictCV.gvf.fit <- function(object, new.Y = NULL,
                              scale = NULL, df = Inf,
                              interval = c("none", "confidence", "prediction"),
                              level = 0.95, na.action = na.pass,
                              pred.var = NULL, weights = 1)
###################################
# gvf.fit method.                 #
###################################
{
# Checks on input object (which is a *single* fitted GVF model)
  # Get GVF model id
  model.id <- attr(object, "model.id")
  ## 1. must be a *registered* fitted GVF model
  if (is.null(model.id)){
      stop("Input fitted model not registered in GVF models db: cannot predict CVs!")
    }
  # Get appropriate resp.to.cv transformation
  resp.to.cv <- GVF.db$db$Resp.to.CV[GVF.db$db$Model.id == model.id]
  ## 2. must have a *non-missing* Resp.to.CV entry in GVF archive
  if (is.na(resp.to.cv)){
      stop("Input fitted model has missing Resp.to.CV in GVF models db: cannot predict CVs!")
    }

# By default new.Y is the fitted gvf.input object. This will return the CVs
# obtained by transforming the fitted response values
if (is.null(new.Y)){
     new.Y <- attr(object, "gvf.input")
    }

# Build resp.to.cv expression
expr <- parse(text = resp.to.cv)

# Compute predictions for the GVF model response:
if (!is.null(pred.var)){
     # Passed variance(s) for prediction intervals around future observations
     p <- predict.lm(object = object, newdata = new.Y,
                     scale = scale, df = df,
                     interval = interval, level = level,
                     type = "response", terms = NULL, na.action = na.action,
                     pred.var = pred.var, weights = weights)
    }
else {
     # This assumes the same default for 'pred.var' as in predict.lm()
     p <- predict.lm(object = object, newdata = new.Y,
                     scale = scale, df = df,
                     interval = interval, level = level,
                     type = "response", terms = NULL, na.action = na.action,
                     weights = weights)
    }

# Handle possible p types (which depend on interval)
if (is.matrix(p)){
     p <- as.data.frame(p)
    }
else {
     p <- data.frame(fit = p)
    }

# Start building actual CV predictions
p.CV <- p
colnames(p.CV) <- paste("CV", colnames(p), sep=".")
# Evaluate resp.to.cv expression (expr)
i.col <- 0
for (cols in p){
     i.col <- i.col + 1
     new.Y.cols <- data.frame(new.Y, resp = cols)
     p.CV[, i.col] <- eval(expr, envir = new.Y.cols)
}
# Bind CV predictions to the new.Y input data
p.CV <- data.frame(new.Y, p.CV)
# Return output object
return(p.CV)
}

predictCV.gvf.fits <- function(object, new.Y = NULL,
                               scale = NULL, df = Inf,
                               interval = c("none", "confidence", "prediction"),
                               level = 0.95, na.action = na.pass,
                               pred.var = NULL, weights = 1)
###################################
# gvf.fits method.                #
###################################
{
    pp <- vector(mode = "list", length = length(object))
    for (i.pp in 1:length(object)) {
         pp[[i.pp]] <- predictCV(object[[i.pp]], new.Y = new.Y,
                                 scale = scale, df = df,
                                 interval = interval,
                                 level = level, na.action = na.action,
                                 pred.var = pred.var, weights = weights)
        }
    pp
}


###################
# predict methods #
###################
predict.gvf.fit <- function(object, ...)
{
stats::predict.lm(object, ...)
}

predict.gvf.fits <- function(object, ...)
{
    pp <- vector(mode = "list", length = length(object))
    i.pp <- 0
    for (e in object) {
         i.pp <- i.pp + 1
         pp[[i.pp]] <- predict(e, ...)
        }
    pp
}


#####################
# rstandard methods #
#####################
rstandard.gvf.fit <- function(model, ...)
{
stats_rstandard.lm(model, ...)
}

rstandard.gvf.fits <- function(model, ...)
{
    pp <- vector(mode = "list", length = length(model))
    i.pp <- 0
    for (e in model) {
         i.pp <- i.pp + 1
         pp[[i.pp]] <- rstandard(e, ...)
        }
    pp
}


####################
# rstudent methods #
####################
rstudent.gvf.fit <- function(model, ...)
{
stats_rstudent.lm(model, ...)
}

rstudent.gvf.fits <- function(model, ...)
{
    pp <- vector(mode = "list", length = length(model))
    i.pp <- 0
    for (e in model) {
         i.pp <- i.pp + 1
         pp[[i.pp]] <- rstudent(e, ...)
        }
    pp
}


#################
# anova methods #
#################
anova.gvf.fit <- function(object, ...)
{
stats_anova.lm(object, ...)
}

anova.gvf.fits <- function(object, ...)
{
    aa <- vector(mode = "list", length = length(object))
    i.aa <- 0
    for (e in object) {
         i.aa <- i.aa + 1
         aa[[i.aa]] <- anova(e, ...)
        }
    aa
}


################
# coef methods #
################
coef.gvf.fit <- function(object, ...)
{
stats_coef.default(object, ...)
}

coef.gvf.fits <- function(object, ...)
{
    cc <- vector(mode = "list", length = length(object))
    i.cc <- 0
    for (e in object) {
         i.cc <- i.cc + 1
         cc[[i.cc]] <- coef(e, ...)
        }
    cc
}


###################
# effects methods #
###################
effects.gvf.fit <- function(object, ...)
{
stats_effects.lm(object, ...)
}

effects.gvf.fits <- function(object, ...)
{
    ee <- vector(mode = "list", length = length(object))
    i.ee <- 0
    for (e in object) {
         i.ee <- i.ee + 1
         ee[[i.ee]] <- effects(e, ...)
        }
    ee
}


#####################
# residuals methods #
#####################
residuals.gvf.fit <- function(object, ...)
{
residuals.lm(object, ...)
}

residuals.gvf.fits <- function(object, ...)
{
    rr <- vector(mode = "list", length = length(object))
    i.rr <- 0
    for (e in object) {
         i.rr <- i.rr + 1
         rr[[i.rr]] <- residuals(e, ...)
        }
    rr
}


##################
# fitted methods #
##################
fitted.gvf.fit <- function(object, ...)
{
stats_fitted.default(object, ...)
}

fitted.gvf.fits <- function(object, ...)
{
    ff <- vector(mode = "list", length = length(object))
    i.ff <- 0
    for (e in object) {
         i.ff <- i.ff + 1
         ff[[i.ff]] <- fitted(e, ...)
        }
    ff
}


################
# vcov methods #
################
vcov.gvf.fit <- function(object, ...)
{
stats_vcov.lm(object, ...)
}

vcov.gvf.fits <- function(object, ...)
{
    vv <- vector(mode = "list", length = length(object))
    i.vv <- 0
    for (e in object) {
         i.vv <- i.vv + 1
         vv[[i.vv]] <- vcov(e, ...)
        }
    vv
}


getBest <- function(object,
                    criterion = c("R2", "adj.R2", "AIC", "BIC"), ...)
###############################################
# Generic function. Get best GVF fitted model #
# on the basis of a given criterion.          #
###############################################
{
UseMethod("getBest")
}

getBest.default <- function(object,
                            criterion = c("R2", "adj.R2", "AIC", "BIC"), ...)
####################################
# Default method: raises an error. #
####################################
{
stop("Input object is not a fitted GVF model!")
}

getBest.gvf.fit <- function(object,
                            criterion = c("R2", "adj.R2", "AIC", "BIC"), ...)
#####################################
# gvf.fit method: no actual choice, #
# returns object.                   #
#####################################
{
criterion <- match.arg(criterion)
object
}

getBest.gvf.fits <- function(object,
                             criterion = c("R2", "adj.R2", "AIC", "BIC"), ...)
################################################
# gvf.fits method: returns the fitted GVF with #
# highest score in the given criterion.        #
################################################
{
criterion <- match.arg(criterion)
score <- switch(criterion, "R2"     = getR2(object, ...),
                           "adj.R2" = getR2(object, adjusted = TRUE, ...),
                           "AIC"    = -AIC(object, ...),
                           "BIC"    = -BIC(object, ...)
                )

object[[which.max(score)]]
}


############################
# Possible methods TO ADD! #
############################

# influence.measures(model)

# dffits(model, infl = , res = )

# dfbeta(model, ...)

# dfbetas(model, ...)

# covratio(model, infl = lm.influence(model, do.coef = FALSE), res = weighted.residuals(model))

# cooks.distance(model, ...)

# hatvalues(model, ...)
DiegoZardetto/ReGenesees documentation built on Dec. 16, 2024, 2:03 p.m.