Exploring marginal predictions/bias correction for GLMMs. The basic problem is Jensen’s inequality; if we have among-group variation around a mean on the linear predictor scale, using the inverse-link function to transform it back to the response scale will give a biased estimate of the mean
GLMMadaptive has marginal_coefs() to deal with this emmeans has a delta-method approximation to deal with this
library(lme4)
## Loading required package: Matrix
library(GLMMadaptive)
##
## Attaching package: 'GLMMadaptive'
## The following object is masked from 'package:lme4':
##
## negative.binomial
library(emmeans)
simulated example: binomial, low probability, large variance (should lead to large bias)
dd <- data.frame(grp=factor(rep(1:20,each=20)))
dd$y <- simulate(~1+(1|grp),
seed=101,
weights=rep(1,nrow(dd)),
family=binomial,
newdata=dd,
newparam=list(beta=-2,theta=2))[[1]]
Probability of original data is 0.205.
Fit lme4, GLMMadaptive models (nAGQ>1 is necessary; setting it to 11 matches GLMMadaptive default value)
m1 <- glmer(y~1+(1|grp), data=dd, family=binomial, nAGQ=11)
m2 <- mixed_model(y~1, random=~1|grp, data=dd, family=binomial)
Very similar results for fixed effect coef (intercept only) and among-group variance:
all.equal(fixef(m1),fixef(m2), tolerance=2e-3)
## [1] TRUE
all.equal(c(VarCorr(m1)[[1]]),c(m2$D), tolerance=0.01)
## [1] TRUE
Hack prediction slightly (there are no non-trivial predictors but we still need one row in the data frame):
plogis(predict(m1,newdata=data.frame(x=1),re.form=NA))
## 1
## 0.1086174
## equivalently
plogis(fixef(m1))
## (Intercept)
## 0.1086174
## these are biased by ~50% (0.108 vs 0.205)
Brute-force simulation of back-transforming Normal variation:
mean(plogis(rnorm(10000,mean=fixef(m1),sd=getME(m1,"theta")))) ## 0.1994 vs 0.205
## [1] 0.1994262
## or:
sv <- sapply(simulate(m1,1000),mean)
## simulating results gives approx correct means
summary(sv)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0650 0.1644 0.1950 0.1995 0.2350 0.4350
hist(sv,main="",breaks=100)
GLMMadaptive also does this by Monte Carlo sim
marginal_coefs(m2) ## on link scale
## (Intercept)
## -1.3884
plogis(marginal_coefs(m2)$betas) ## back-transform
## (Intercept)
## 0.1996687
summary(emmeans(m1, specs=~1), type = "response", bias.adj = TRUE)
## 1 prob SE df asymp.LCL asymp.UCL
## overall 0.147 0.0553 Inf 0.0661 0.283
##
## Confidence level used: 0.95
## Intervals are back-transformed from the logit scale
## Bias adjustment applied based on sigma = 1
Use Python so we can simplify (brute force D()
/deriv()
from R would probably work fine??)
from sympy import *
x = symbols('x')
d1 = simplify(diff(1/(1+exp(-x)), x))
d2 = simplify(diff(1/(1+exp(-x)), x, x))
d3 = simplify(diff(1/(1+exp(-x)), x, x, x))
d4 = simplify(diff(1/(1+exp(-x)), x, x, x, x))
print([d1, d2, d3, d4])
## [1/(4*cosh(x/2)**2), (1 - exp(x))*exp(x)/(exp(x) + 1)**3, ((exp(x) + 1)**3 - 6*(exp(x) + 1)**2 + 6*exp(x) + 6)*exp(x)/(exp(x) + 1)**5, (-(exp(x) + 1)**3 + 14*(exp(x) + 1)**2 - 36*exp(x) - 12)*exp(x)/(exp(x) + 1)**5]
d2 <- function(x) {
ex <- exp(x)
(1 - ex)*ex/(ex + 1)**3
}
d4 <- function(x) {
ex <- exp(x)
(-1*(ex + 1)^3 + 14*(ex + 1)^2 - 36*ex - 12)*ex/(ex + 1)^5
}
Check:
all.equal(d2(2.5), eval(D(D(expression(1/(1+exp(-x))),"x"),"x"), list(x=2.5)))
## [1] TRUE
## ugh
all.equal(d4(2.5), eval(D(D(D(D(expression(1/(1+exp(-x))),"x"),"x"),"x"),"x"),list(x=2.5)))
## [1] TRUE
b <- fixef(m1)
## second-order Taylor exp. (matches emmeans)
(delta_2 <- plogis(b) + sigma(m1)^2*d2(b)/2)
## (Intercept)
## 0.146511
## attempt to extend Taylor expansion (skew==0, so take 4th deriv * kurtosis??)
delta_2 + (3*sigma(m1)^4)*d4(b)/factorial(4)
## (Intercept)
## 0.1449778
## doesn't help, don't know what's wrong here ...
## calculation error? thinko?