1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
#############################################################################
## Copyright (c) 2010-2022 Rune Haubo Bojesen Christensen
##
## This file is part of the ordinal package for R (*ordinal*)
##
## *ordinal* is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 2 of the License, or
## (at your option) any later version.
##
## *ordinal* is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## A copy of the GNU General Public License is available at
## <https://www.r-project.org/Licenses/> and/or
## <http://www.gnu.org/licenses/>.
#############################################################################
## This file contains:
## The main clm function and some auxiliary functions to generate the
## model frame and handle the model environment.
checkArgs.clm <- function(mc) {
nm <- names(as.list(mc))
if(!"formula" %in% nm) stop("Model needs a formula", call.=FALSE)
if("offset" %in% nm)
stop("offset argument not allowed: ",
"specify 'offset' in formula or scale arguments instead")
invisible()
}
clm <-
function(formula, scale, nominal, data, weights, start, subset,
doFit = TRUE, na.action, contrasts, model = TRUE,
control = list(),
link = c("logit", "probit", "cloglog", "loglog", "cauchit",
"Aranda-Ordaz", "log-gamma"),
threshold = c("flexible", "symmetric", "symmetric2", "equidistant"), ...)
{
mc <- match.call(expand.dots = FALSE)
link <- match.arg(link)
threshold <- match.arg(threshold)
if(missing(contrasts)) contrasts <- NULL
if(missing(start)) start <- NULL
checkArgs.clm(mc=match.call())
## set control parameters: ## getControl.clm
control <- do.call(clm.control, c(control, list(...)))
## Extract and process formulas:
call.envir <- parent.frame(n=1)
formulas <- get_clmFormulas(mc, call.envir)
## Get full model.frame and terms.objects:
fullmf <- get_clm.mf(mc, formulas$fullForm, attr(formulas, "envir"),
call.envir)
if(control$method == "model.frame") return(fullmf)
terms.list <-
if(any(c("scale", "nominal") %in% names(formulas)))
get_clmTerms(mc, formulas, call.envir) else list(formula=terms(fullmf))
## Get y, X, weights, off etc.:
design <- get_clmDesign(fullmf, terms.list, contrasts)
lst <- namedList(doFit, control, link, threshold, start, formulas)
if(control$method == "design") return(c(design, lst))
## Get clm.struct:
design <- c(design, makeThresholds(design$y.levels, threshold))
design <- drop.cols(design, silent=TRUE, drop.scale=FALSE)
clm.struct <- c(design, lst)
## Fit model, check convergence, or return a model environment:
fit <- clm.fit.default(clm.struct)
if(doFit == FALSE) return(fit)
## Format output, prepare result:
keep <- c("terms", "contrasts", "xlevels", # formula
"S.terms", "S.contrasts", "S.xlevels", # scale
"nom.terms", "nom.contrasts", "nom.xlevels", # nominal
"na.action", "y", "y.levels",
"control", "link", "threshold", "start", "formulas")
res <- c(fit, clm.struct[match(keep, names(clm.struct), 0L)],
list(formula=lst$formulas$formula, call=match.call()))
## res$tJac <- format_tJac(res$tJac, res$y.levels, clm.struct$alpha.names)
res$info=get_clmInfoTab(res)
if(model) res$model <- fullmf
res <- res[sort(names(res))]
class(res) <- "clm"
res
}
clm.newRho <-
function(parent=parent.frame(), y, X, NOM=NULL, S=NULL, weights,
offset, S.offset=NULL, tJac, control=clm.control(), ...)
### Setting variables in rho: B1, B2, o1, o2, weights.
{
## Make B1, B2, o1, o2 based on y, X and tJac:
keep <- weights > 0
y[!keep] <- NA
y <- droplevels(y)
ntheta <- nlevels(y) - 1
y <- c(unclass(y))
y[is.na(y)] <- 0
n <- sum(keep)
B2 <- 1 * (col(matrix(0, nrow(X), ntheta + 1)) == y)
o1 <- c(1e5 * B2[keep, ntheta + 1]) - offset[keep]
o2 <- c(-1e5 * B2[keep, 1]) - offset[keep]
B1 <- B2[keep, -(ntheta + 1), drop = FALSE]
B2 <- B2[keep, -1, drop = FALSE]
## adjust B1 and B2 for structured thresholds:
B1 <- B1 %*% tJac
B2 <- B2 %*% tJac
## update B1 and B2 with nominal effects:
if(NCOL(NOM) > 1) { ## !is.null(NOM) && ncol(NOM) > 1) {
## if !is.null(NOM) and NOM is more than an intercept:
if(control$sign.nominal == "negative") NOM[, -1] <- -NOM[, -1]
LL1 <- lapply(1:ncol(NOM), function(x) B1 * NOM[keep, x])
B1 <- do.call(cbind, LL1)
LL2 <- lapply(1:ncol(NOM), function(x) B2 * NOM[keep, x])
B2 <- do.call(cbind, LL2)
}
## update B1 and B2 with location effects (X):
nbeta <- NCOL(X) - 1
if(nbeta > 0) {
if(control$sign.location == "negative") X <- -X
B1 <- cbind(B1, X[keep, -1, drop = FALSE])
B2 <- cbind(B2, X[keep, -1, drop = FALSE])
}
dimnames(B1) <- NULL
dimnames(B2) <- NULL
n.psi <- ncol(B1) ## no. linear model parameters
## there may be scale offset without scale predictors:
sigma <- Soff <-
if(is.null(S.offset)) rep(1, n) else exp(S.offset[keep])
## save scale model matrix:
k <- 0
if(!is.null(S)) {
S <- S[keep, -1, drop=FALSE]
dimnames(S) <- NULL
k <- ncol(S) ## no. scale parameters
}
has.scale <- ## TRUE if scale has to be considered.
(!is.null(S) || any(S.offset != 0))
## initialize fitted values and weights:
fitted <- numeric(length = n)
wts <- weights[keep]
lst <- namedList(B1, B2, o1, o2, n.psi, S, Soff, k, sigma, has.scale, fitted,
wts, clm.nll, clm.grad, clm.hess)
list2env(x=lst, parent=parent)
}
|