Skip to content
Statistical RethinkingStatistical Rethinking

11.2 Zero-inflated outcomes

Zero-inflated outcomes arise when the data contain more zeros than a simple count model predicts. Mixture models combine a process that generates zeros with a process that generates counts.

11.2.1 The zero-inflated mixture

Zero-inflated data come from two processes:

  1. With probability π\pi, the observation is always zero (structural zero).
  2. With probability 1−π1 - \pi, the observation comes from a count distribution (e.g., Poisson).

p(yi)={π+(1−π) pcount(0)yi=0(1−π) pcount(yi)yi>0p(y_i) = \begin{cases} \pi + (1-\pi) \, p_{\text{count}}(0) & y_i = 0 \\ (1-\pi) \, p_{\text{count}}(y_i) & y_i > 0 \end{cases}

# Zero-inflated Poisson: simulate and fit
set.seed(42)
n <- 300
x <- runif(n, 0, 10)

# Process 1: structural zeros (probability depends on x)

logit_pi <- 0.5 - 0.2 * x
pi <- 1 / (1 + exp(-logit_pi))
is_structural <- rbinom(n, 1, pi)

# Process 2: Poisson counts

lambda <- exp(0.3 + 0.15 * x)
y_pois <- rpois(n, lambda)

# Combine

y <- ifelse(is_structural, 0, y_pois)

cat("Zero-inflated data:\n")
cat(" Total zeros:", sum(y == 0), "/", n, "\n")
cat(" Proportion zeros:", round(mean(y == 0), 3), "\n")
cat(" Mean of non-zeros:", round(mean(y[y > 0]), 2), "\n")

# Fit zero-inflated Poisson using pscl package (if available)

# Here we demonstrate the concept with a two-part model

# Part 1: logistic for y > 0

m_zero <- glm(I(y > 0) ~ x, family = binomial)

# Part 2: Poisson for y given y > 0

m_count <- glm(y[y > 0] ~ x[y > 0], family = poisson)

cat("\nTwo-part (hurdle) approximation:\n")
cat(" Zero process: intercept =", round(coef(m_zero)[1], 3),
" slope =", round(coef(m_zero)[2], 3), "\n")
cat(" Count process: intercept =", round(coef(m_count)[1], 3),
" slope =", round(coef(m_count)[2], 3), "\n")

# Plot

par(mfrow = c(1, 2))
hist(y, breaks = max(y) + 1, col = "steelblue",
main = "Zero-inflated counts", xlab = "y")
plot(x, y, pch = 16, col = adjustcolor("steelblue", 0.5),
main = "y vs x", xlab = "x", ylab = "y")
par(mfrow = c(1, 1))
Click Run to execute this code in your browser with WebR

11.2.2 ZIP vs hurdle

Both ZIP and hurdle models handle excess zeros, but they differ:

  • ZIP: Zeros can come from either the structural process or the Poisson. The Poisson can also produce zeros.
  • Hurdle: All zeros come from the hurdle. The count part is a truncated Poisson (no zeros).
# Compare ZIP and hurdle concepts
set.seed(42)
n <- 500
x <- runif(n, 0, 10)

# ZIP: zeros from structural + Poisson

pi_zip <- 0.3
is_struct <- rbinom(n, 1, pi_zip)
lambda <- exp(0.5 + 0.1 * x)
y_zip <- ifelse(is_struct, 0, rpois(n, lambda))

# Hurdle: zeros only from hurdle, counts are truncated Poisson

p_nonzero <- 1 / (1 + exp(-(0 + 0.2 * x)))
is_nonzero <- rbinom(n, 1, p_nonzero)
y_hurdle <- ifelse(is_nonzero, rpois(n, lambda) + 1, 0) # +1 to avoid 0

cat("ZIP data:\n")
cat(" Zeros:", sum(y_zip == 0), " Mean (y>0):", round(mean(y_zip[y_zip > 0]), 2), "\n")

cat("\nHurdle data:\n")
cat(" Zeros:", sum(y_hurdle == 0), " Mean (y>0):", round(mean(y_hurdle[y_hurdle > 0]), 2), "\n")

cat("\nKey difference:\n")
cat(" ZIP: Poisson can produce 0 (so some zeros are 'count zeros')\n")
cat(" Hurdle: count part starts at 1 (all zeros are 'hurdle zeros')\n")
Click Run to execute this code in your browser with WebR
Rethinking

Zeros are not all the same. In a ZIP model, a zero can mean "this observation cannot produce a count" (structural zero) or "this observation could produce a count but happened to get zero" (Poisson zero). This distinction matters for interpretation: structural zeros tell us about suitability or participation, while Poisson zeros tell us about chance. In a hurdle model, all zeros are the same — they all mean "the hurdle was not crossed." The choice between ZIP and hurdle depends on the scientific question: are there two kinds of zeros, or just one?

Summary of Section 11.2

  • Zero-inflated data have more zeros than a count model predicts.
  • ZIP: mixture of structural zeros and Poisson — zeros from two sources.
  • Hurdle: all zeros from the hurdle; count part is truncated.
  • Both have two linear predictors: one for the zero process, one for the count.
  • Choose based on whether zeros come from one or two processes.