Performs adjustments of a user-supplied independence loglikelihood for the presence of cluster dependence, following Chandler and Bate (2007). The user provides a function that returns a vector of observation-specific loglikelihood contributions and a vector that indicates cluster membership. The loglikelihood of a sub-model can be adjusted by fixing a set of parameters at particular values.
adjust_loglik(
loglik = NULL,
...,
cluster = NULL,
p = NULL,
init = NULL,
par_names = NULL,
fixed_pars = NULL,
fixed_at = 0,
name = NULL,
larger = NULL,
alg_deriv = NULL,
alg_hess = NULL,
mle = NULL,
H = NULL,
V = NULL
)A named function. Returns a vector of the
loglikelihood contributions of individual observations. The first
argument must be the vector of model parameter(s). If any of the model
parameters are out-of-bounds then loglik should return either
-Inf or a vector with at least one element equal to -Inf.
The number of parameters in the full model must be specified
using (at least) one of the arguments p, init or
par_names.
Further arguments to be passed either to loglik
(and to alg_deriv and alg_hess if these are supplied) or
to optim. The latter may include gr,
method, lower, upper or control.
In the call to optim, hessian = TRUE
will be used regardless of any value supplied.
The function loglik must not have arguments with names
that match any of these arguments to optim.
A vector or factor indicating from which cluster the
respective loglikelihood contributions from loglik originate.
Must have the same length as the vector returned by loglik.
If cluster is not supplied then it is set inside
adjust_loglik under the assumption that each observation forms
its own cluster.
A numeric scalar. The dimension of the full parameter
vector, i.e. the number of parameters in the full model. Must be
consistent with the lengths of init and par_names,
if these are also supplied.
A numeric vector of initial values. Must have length equal
to the number of parameters in the full model. If init
is supplied then p is set to length(init), provided that
this is consistent with the the value given by p or implied
by length(par_names).
If fixed_pars is not NULL then init[-fixed_pars]
is used in the search for the MLE.
If init is not supplied then rep(0.1, p) is used.
A character vector. Names of the p parameters
in the full model. Must be consistent with the lengths of
init and p, if these are also supplied.
A vector specifying which parameters are to be restricted
to be equal to the value(s) in fixed_at. Can be either a numeric
vector, specifying indices of the components of the full parameter
vector, or a character vector of parameter names, which must be a subset
of those supplied in par_names or stored in the object
larger.
A numeric vector of length 1 or length(fixed_pars).
If length(fixed_at) = 1 then the components fixed_pars
of the parameter vector are all fixed at fixed_at.
If length(fixed_at) = length(fixed_pars) then the component
fixed_pars[i] is fixed at fixed_at[i] for each i.
A character scalar. A name for the model that gives rise
to loglik. If this is not supplied then the name in
larger is used, if this has been supplied, and the name of
the function loglik otherwise.
Only relevant if fixed_pars is not NULL.
If larger is supplied but fixed_pars is not then an error
will result. if larger is supplied then information about the
model in larger, e.g. about p and par_names will
override any attempt to set these arguments in the call to
adjust_loglik.
An object of class "chandwich" returned by adjust_loglik,
corresponding to a model in which the smaller model implied by
fixed_pars is nested. If larger is supplied then
all the arguments to adjust_loglik apart from
fixed_pars and fixed_at are extracted from larger.
If init is not supplied in the current call to
adjust_loglik then init is set to
attr(larger, "MLE"), with the elements in fixed_pars
set to fixed_at.
A function with the vector of model parameter(s) as its
first argument. Returns a length(cluster) by p numeric
matrix. Column i contains the derivatives of each of the loglikelihood
contributions in loglik with respect to model parameter i.
A function with the vector of model parameter(s) as its
first argument. Returns a p by p numeric matrix equal to
the Hessian of loglik, i.e. the matrix of second derivatives of
the function loglik.
Supplying both V and alg_deriv or both H and
alg_hess will produce an error.
A numeric vector. Can only be used if fixed_pars = NULL.
Provides the maximum likelihood estimate of the model parameters,
that is, the value of the parameter vector
at which the independence loglikelihood loglik is maximized.
Must have length equal to the number of parameters in the
full model. If mle is supplied then p is set
to length(mle), provided that this is consistent with the the
value given by p or implied by length(par_names).
If mle is supplied then it overrides init.
p by p numeric matrices. Only used if mle is supplied.
Provide estimates of the Hessian of the
independence loglikelihood (H) and the variance of the vector
of cluster-specific contributions to the score vector (first
derivatives with respect to the parameters) of the independence
loglikelihood, each evaluated at the MLE mle. See the
Introducing chandwich vignette and/or Chandler and Bate (2007).
Supplying both V and alg_deriv or both H and
alg_hess will produce an error.
A function of class "chandwich" to evaluate an adjusted
loglikelihood, or the independence loglikelihood, at one or more sets
of model parameters, with arguments
A numeric vector or matrix giving values of the p_current
(see below) parameters in the model to which the returned adjusted
loglikelihood applies.
If p_current = 1 this may be a numeric vector or a matrix
with 1 column.
If p_current > 1 this may be a numeric vector of length p
(one set of model parameters) or a numeric matrix with p
columns (nrow(x) sets of model parameters), one set in each row
of x.
A character scalar. The type of adjustment to use.
One of "vertical", "cholesky", "spectral" or
"none".
The latter results in the evaluation of the (unadjusted) independence loglikelihood. The function has (additional) attributes
The number of parameters in the full model and current models, respectively.
A numeric vector giving the indices of the free
parameters in the current model, with names inferred from
par_names if this was supplied.
Numeric vectors, with names inferred from
par_names if this was supplied. Maximum likelihood estimates
of free parameters under the current model (mle) and all
parameters in the full model, including any parameters with fixed
values (res_MLE).
The unadjusted and adjusted estimated standard errors, of the free parameters, respectively.
The unadjusted and adjusted estimated variance-covariance matrix of the free parameters, respectively.
The Hessians of the independence and adjusted loglikelihood, respectively.
The matrix C in equation (14) of Chandler and Bate (2007), calculated using Cholesky decomposition and spectral decomposition, respectively.
The names of the parameters in the full and current models, respectively, if these were supplied in this call or a previous call.
The common maximised value of the independence and adjusted loglikelihoods.
The arguments loglik and cluster
supplied in this call, or a previous call.
A list containing the further arguments passed to
loglik via ... in this call, or a previous call.
a vector containing the contributions of individual observations to the independence log-likelihood evaluated at the MLE.
The argument name, or the name of the function
loglik if name isn't supplied.
The number of observations.
The call to adjust_loglik.
If fixed_pars is not NULL then there are further attributes
The argument fixed_pars, with names inferred from
par_names if this was supplied.
The argument fixed_at, with names inferred from
par_names if this was supplied.
If alg_deriv and/or alg_hess were supplied then these are
returned as further attributes.
To view an individual attribute called att_name use
attr(x, "att_name") or attributes(x)$att_name.
Three adjustments to the independence loglikelihood described in
Chandler and Bate (2007) are available. The vertical adjustment is
described in Section 6 and two horizontal adjustments are described
in Sections 3.2 to 3.4. See the descriptions of type and, for the
horizontal adjustments, the descriptions of C_cholesky and
C_spectral, in Value.
The adjustments involve first and second derivatives of the loglikelihood
with respect to the model parameters. These are estimated using
jacobian and optimHess
unless alg_deriv and/or alg_hess are supplied.
Chandler, R. E. and Bate, S. (2007). Inference for clustered data using the independence loglikelihood. Biometrika, 94(1), 167-183. doi:10.1093/biomet/asm015
summary.chandwich for maximum likelihood estimates
and unadjusted and adjusted standard errors.
plot.chandwich for plots of one-dimensional adjusted
loglikelihoods.
confint.chandwich, anova.chandwich,
coef.chandwich, vcov.chandwich
and logLik.chandwich for other chandwich methods.
conf_intervals for confidence intervals for
individual parameters.
conf_region for a confidence region for
a pair of parameters.
compare_models to compare nested models using an
(adjusted) likelihood ratio test.
# ------------------------- Binomial model, rats data ----------------------
# Contributions to the independence loglikelihood
binom_loglik <- function(prob, data) {
if (prob < 0 || prob > 1) {
return(-Inf)
}
return(dbinom(data[, "y"], data[, "n"], prob, log = TRUE))
}
rat_res <- adjust_loglik(loglik = binom_loglik, data = rats, par_names = "p")
# Plot the loglikelihoods
plot(rat_res, type = 1:4, legend_pos = "bottom", lwd = 2, col = 1:4)
# MLE, SEs and adjusted SEs
summary(rat_res)
#> MLE SE adj. SE
#> p 0.1535 0.008645 0.01305
# -------------------------- GEV model, owtemps data -----------------------
# ------------ following Section 5.2 of Chandler and Bate (2007) -----------
# Contributions to the independence loglikelihood
gev_loglik <- function(pars, data) {
o_pars <- pars[c(1, 3, 5)] + pars[c(2, 4, 6)]
w_pars <- pars[c(1, 3, 5)] - pars[c(2, 4, 6)]
if (isTRUE(o_pars[2] <= 0 | w_pars[2] <= 0)) return(-Inf)
o_data <- data[, "Oxford"]
w_data <- data[, "Worthing"]
check <- 1 + o_pars[3] * (o_data - o_pars[1]) / o_pars[2]
if (isTRUE(any(check <= 0))) return(-Inf)
check <- 1 + w_pars[3] * (w_data - w_pars[1]) / w_pars[2]
if (isTRUE(any(check <= 0))) return(-Inf)
o_loglik <- log_gev(o_data, o_pars[1], o_pars[2], o_pars[3])
w_loglik <- log_gev(w_data, w_pars[1], w_pars[2], w_pars[3])
return(o_loglik + w_loglik)
}
# Initial estimates (method of moments for the Gumbel case)
sigma <- as.numeric(sqrt(6 * diag(var(owtemps))) / pi)
mu <- as.numeric(colMeans(owtemps) - 0.57722 * sigma)
init <- c(mean(mu), -diff(mu) / 2, mean(sigma), -diff(sigma) / 2, 0, 0)
# Loglikelihood adjustment for the full model
par_names <- c("mu[0]", "mu[1]", "sigma[0]", "sigma[1]", "xi[0]", "xi[1]")
large <- adjust_loglik(gev_loglik, data = owtemps, init = init,
par_names = par_names)
# Rows 1, 3 and 4 of Table 2 of Chandler and Bate (2007)
t(summary(large))
#> mu[0] mu[1] sigma[0] sigma[1] xi[0] xi[1]
#> MLE 81.1700 2.6680 3.7290 0.5312 -0.19890 -0.08835
#> SE 0.3282 0.3282 0.2293 0.2293 0.04937 0.04937
#> adj. SE 0.4036 0.2128 0.2426 0.1911 0.03943 0.03625
# Loglikelihood adjustment of some smaller models: xi[1] = 0 etc
# Starting from a larger model
medium <- adjust_loglik(larger = large, fixed_pars = "xi[1]")
small <- adjust_loglik(larger = large, fixed_pars = c("sigma[1]", "xi[1]"))
small <- adjust_loglik(larger = medium, fixed_pars = c("sigma[1]", "xi[1]"))
# Starting from scratch
medium <- adjust_loglik(gev_loglik, data = owtemps, init = init,
par_names = par_names, fixed_pars = "xi[1]")
small <- adjust_loglik(gev_loglik, data = owtemps, init = init,
par_names = par_names, fixed_pars = c("sigma[1]", "xi[1]"))
# --------- Misspecified Poisson model for negative binomial data ----------
# ... following Section 5.1 of the "Object-Oriented Computation of Sandwich
# Estimators" vignette of the sandwich package
# https://cran.r-project.org/web/packages/sandwich/vignettes/sandwich-OOP.pdf
# Simulate data
set.seed(123)
x <- rnorm(250)
y <- rnbinom(250, mu = exp(1 + x), size = 1)
# Fit misspecified Poisson model
fm_pois <- glm(y ~ x + I(x^2), family = poisson)
summary(fm_pois)$coefficients
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) 1.06326821 0.04135723 25.709367 9.184267e-146
#> x 0.99607219 0.05353446 18.606186 2.862861e-77
#> I(x^2) -0.04912373 0.02314608 -2.122335 3.380961e-02
# Contributions to the independence loglikelihood
pois_glm_loglik <- function(pars, y, x) {
log_mu <- pars[1] + pars[2] * x + pars[3] * x ^ 2
return(dpois(y, lambda = exp(log_mu), log = TRUE))
}
pars <- c("alpha", "beta", "gamma")
pois_quad <- adjust_loglik(pois_glm_loglik, y = y, x = x, par_names = pars)
summary(pois_quad)
#> MLE SE adj. SE
#> alpha 1.06300 0.04136 0.08378
#> beta 0.99610 0.05354 0.10520
#> gamma -0.04913 0.02315 0.03628
# Providing algebraic derivatives and Hessian
pois_alg_deriv <- function(pars, y, x) {
mu <- exp(pars[1] + pars[2] * x + pars[3] * x ^ 2)
return(cbind(y - mu, x * (y - mu), x ^2 * (y - mu)))
}
pois_alg_hess <- function(pars, y, x) {
mu <- exp(pars[1] + pars[2] * x + pars[3] * x ^ 2)
alg_hess <- matrix(0, 3, 3)
alg_hess[1, ] <- -c(sum(mu), sum(x * mu), sum(x ^ 2 * mu))
alg_hess[2, ] <- -c(sum(x * mu), sum(x ^ 2 * mu), sum(x ^ 3 * mu))
alg_hess[3, ] <- -c(sum(x ^ 2 * mu), sum(x ^ 3 * mu), sum(x ^ 4 * mu))
return(alg_hess)
}
pois_quad <- adjust_loglik(pois_glm_loglik, y = y, x = x, p = 3,
alg_deriv = pois_alg_deriv, alg_hess = pois_alg_hess)
summary(pois_quad)
#> MLE SE adj. SE
#> 1 1.06300 0.04136 0.08378
#> 2 0.99610 0.05354 0.10520
#> 3 -0.04913 0.02315 0.03628
got_sandwich <- requireNamespace("sandwich", quietly = TRUE)
if (got_sandwich) {
# Providing MLE, H and V
# H and V calculated using bread() and meat() from sandwich package
n_obs <- stats::nobs(fm_pois)
pois_quad <- adjust_loglik(pois_glm_loglik, y = y, x = x, p = 3,
mle = fm_pois$coefficients,
H = -solve(sandwich::bread(fm_pois) / n_obs),
V = sandwich::meat(fm_pois) * n_obs)
}