Summary: I am trying to model some count data. I initially attempted to fit a poisson GLM, but diagnostics appear to indicate overdispersion. I have tried several different recommended remedies but these do not appear to improve the fit of the model. How can I model these overdispersed data effectively?
Data: My response variable y
is count data varying from 0 to 11. I have two continuous predictor variables x1
and x2
that I have scaled to mean 0 and sd 1. Previous exploration has indicated x1
follows a non-linear relationship with y
and is probably best fit with a quadratic term.
Model: I would like to fit the model y ~ x1 + I(x1^2) + x2
in order to test the associations of the two predictor variables with y
.
Approach: Initially I fitted a poisson-family GLM. A residual qq plot indicated non-normal residuals and suggested right-skew. I therefore tested for overdispersion, which seemed to be present. I tried three alternative approaches to deal with the overdispersion:
- Negative binomial GLM
- Observation-level random effects
- Quasi-poisson GLM
In each case the residuals still appear to be right-skewed, if anything more extreme than the original model.
Question: have I diagnosed overdispersion correctly? Is there another problem with my model which is leading to the poor fit, and how can I remedy this?
Code:
# Load packages
library(MASS)
library(dplyr)
library(lme4)
# Load data
d <- tibble(y = c(0, 1, 4, 0, 0, 2, 2, 1, 1, 3, 0, 0, 1, 1, 5, 6, 4, 5, 1, 0, 2, 4, 3, 1, 1, 0, 0, 1, 5, 3, 3, 1, 0, 4, 3, 0, 3, 1, 0, 0, 3, 2, 0, 0, 1, 9, 3, 0, 4, 0, 1, 0, 1, 5, 2, 1, 3, 5, 4, 0, 0, 1, 0, 2, 0, 3, 0, 0, 1, 2, 1, 0, 0, 0, 0, 7, 0, 3, 2, 3, 1, 0, 6, 2, 0, 1, 4, 0, 5, 7, 3, 6, 0, 3, 11, 4, 0, 5, 2, 1),
x1 = c(-0.219, 1.278, -0.788, -0.788, 0.456, 0.372, 0.393, 0.92, -0.788, 0.983, -0.788, -0.556, -0.788, 0.034, -0.43, 2.164, 0.709, 0.751, -0.43, -0.198, 1.51, 5.032, 0.013, -0.43, 0.013, -0.788, -0.177, -0.43, 0.097, 0.688, 0.034, -0.577, -0.451, -0.198, -0.282, -0.43, 5.285, 0.92, -0.219, -0.788, 0.372, 0.667, 0.034, 1.342, 1.342, -0.156, 0.435, -0.198, -0.177, -0.788, 4.947, -0.556, 0.878, -0.198, 0.372, -0.409, 1.342, 1.278, 1.004, 0.604, -0.219, 0.097, 0.329, -0.577, -0.409, 1.953, 0.329, 0.414, 0.097, 0.161, 0.393, -0.788, -0.535, -0.767, -0.198, -0.43, -0.198, 0.646, 1.806, -0.788, 4.336, 0.962, 0.414, -0.788, -0.788, -0.577, 1.637, 0.983, -0.788, -0.198, -0.788, -0.198, -0.788, 0.393, 1.342, 1.806, -0.788, -0.788, 0.097, 0.161),
x2 = c(1.182, -1.088, -1.088, -1.088, 0.047, 1.182, -0.331, 0.533, -1.088, -0.52, 1.182, 1.182, 1.182, 1.182, 1.182, 0.955, 1.182, 1.182, 1.182, -0.331, 0.804, 1.182, -1.088, -1.088, -1.088, 1.182, -1.088, -1.088, 0.425, 1.182, 1.182, -1.088, 1.182, -0.331, 1.182, 1.182, 1.182, -1.088, 1.182, -1.088, -0.634, 0.274, -0.52, -1.088, 1.182, 1.182, 1.182, 1.182, 0.425, -1.088, 0.047, 1.182, -1.088, -1.088, -0.634, 1.182, 1.182, -1.088, 0.804, -0.331, 1.182, 1.182, -0.52, -1.088, 1.182, 1.182, 1.182, -1.088, -1.088, -0.52, -1.088, -1.088, 0.047, 1.182, -1.088, 1.182, -1.088, -0.18, 1.182, -1.088, 1.182, -0.709, 1.182, -1.088, -1.088, 1.182, -0.439, 0.425, 1.182, -0.331, 1.182, 1.182, -1.088, 1.182, 1.182, -0.331, -1.088, 1.182, 0.614, -0.52))
# Try fitting a poisson GLM
GLM1 <- glm(y ~ x1 + I(x1^2) + x2,
family = "poisson",
data = d)
# Check distribution of residuals
GLM1.res <- resid(GLM1)
qqnorm(GLM1.res)
qqline(GLM1.res)
# Assess overdispersion
mean(d$y)
var(d$y)
od.fac <- sum(GLM1.res^2) / df.residual(GLM1)
od.fac
# Try fitting negative binomial GLM
GLM2 <- glm.nb(y ~ x1 + I(x1^2) + x2,
data = d)
GLM2.res <- resid(GLM2)
qqnorm(GLM2.res)
qqline(GLM2.res)
# Try fitting observation-level random effects
d <- mutate(d, ind = 1:n())
GLM3 <- glmer(y ~ x1 + I(x1^2) + x2 + (1|ind),
family = "poisson",
data = d2)
GLM3.res <- resid(GLM3)
qqnorm(GLM3.res)
qqline(GLM3.res)
# Try fitting quasi-poisson
GLM4 <- glm(y ~ x1 + I(x1^2) + x2 + (1|ind),
family = "quasipoisson",
data = d)
GLM4.res <- resid(GLM4)
qqnorm(GLM4.res)
qqline(GLM4.res)