## ----include = FALSE----------------------------------------------------------
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
library(drmTMB)

## -----------------------------------------------------------------------------
set.seed(106)
n <- 360
soil_counts <- data.frame(
  habitat = factor(
    rep(c("degraded", "restored"), each = n / 2),
    levels = c("degraded", "restored")
  ),
  surface = factor(
    sample(c("litter", "bare"), n, replace = TRUE, prob = c(0.72, 0.28)),
    levels = c("litter", "bare")
  ),
  moisture = as.numeric(scale(runif(n, 0.15, 0.95))),
  trap_nights = sample(2:5, n, replace = TRUE)
)

restored <- as.numeric(soil_counts$habitat == "restored")
bare <- as.numeric(soil_counts$surface == "bare")

rate <- exp(log(2.4) + 0.35 * restored + 0.30 * soil_counts$moisture)
mu <- soil_counts$trap_nights * rate
sigma_nb2 <- exp(-0.75 + 0.40 * restored)
zi <- plogis(-3.2 + 1.6 * bare - 0.25 * restored)

structural_zero <- runif(n) < zi
soil_counts$springtails <- ifelse(
  structural_zero,
  0L,
  rnbinom(n, size = 1 / sigma_nb2^2, mu = mu)
)

head(soil_counts)

## -----------------------------------------------------------------------------
fit_nb2 <- drmTMB(
  bf(
    springtails ~ habitat + moisture + offset(log(trap_nights)),
    sigma ~ habitat
  ),
  family = nbinom2(),
  data = soil_counts
)

## -----------------------------------------------------------------------------
check_drm(fit_nb2)

## -----------------------------------------------------------------------------
coef(fit_nb2, "mu")
coef(fit_nb2, "sigma")

sigma_ratio <- exp(coef(fit_nb2, "sigma")["habitatrestored"])
c(
  sigma_ratio_restored_vs_degraded = sigma_ratio,
  variance_multiplier_at_same_mu = sigma_ratio^2
)

## -----------------------------------------------------------------------------
fit_zinb2 <- drmTMB(
  bf(
    springtails ~ habitat + moisture + offset(log(trap_nights)),
    sigma ~ habitat,
    zi ~ surface
  ),
  family = nbinom2(),
  data = soil_counts
)

check_drm(fit_zinb2)

## -----------------------------------------------------------------------------
coef(fit_zinb2, "zi")

zero_grid <- data.frame(
  habitat = factor(c("degraded", "degraded"), levels = levels(soil_counts$habitat)),
  surface = factor(c("litter", "bare"), levels = levels(soil_counts$surface)),
  moisture = 0,
  trap_nights = 3
)

data.frame(
  surface = zero_grid$surface,
  structural_zero_probability = predict(fit_zinb2, newdata = zero_grid, dpar = "zi")
)

## -----------------------------------------------------------------------------
new_traps <- data.frame(
  habitat = factor(c("degraded", "restored"), levels = levels(soil_counts$habitat)),
  surface = factor(c("litter", "litter"), levels = levels(soil_counts$surface)),
  moisture = c(0, 0),
  trap_nights = c(3, 3)
)

mu_hat <- predict(fit_zinb2, newdata = new_traps, dpar = "mu")
sigma_hat <- predict(fit_zinb2, newdata = new_traps, dpar = "sigma")
zi_hat <- predict(fit_zinb2, newdata = new_traps, dpar = "zi")

data.frame(
  habitat = new_traps$habitat,
  conditional_mean = mu_hat,
  sigma = sigma_hat,
  structural_zero_probability = zi_hat,
  unconditional_mean = (1 - zi_hat) * mu_hat,
  unconditional_variance =
    (1 - zi_hat) * (mu_hat + sigma_hat^2 * mu_hat^2) +
      zi_hat * (1 - zi_hat) * mu_hat^2
)

## ----count-model-parts-figure, eval=requireNamespace("ggplot2", quietly = TRUE), fig.width=7.4, fig.height=4.8, fig.cap="Response-scale model parts for the zero-inflated NB2 example. Points show fitted conditional counts, unconditional counts, NB2 extra-Poisson scale, and structural-zero probabilities on separate facets with their own x scales. No interval bars are drawn because this figure compares fitted components, not confidence intervals.", fig.alt="Faceted point plot for the zero-inflated NB2 soil-count example. Separate facets show conditional expected counts, unconditional expected counts, NB2 extra-Poisson sigma, and structural-zero probabilities for habitat and surface combinations."----
library(ggplot2)

count_plot_grid <- expand.grid(
  habitat = factor(levels(soil_counts$habitat), levels = levels(soil_counts$habitat)),
  surface = factor(levels(soil_counts$surface), levels = levels(soil_counts$surface))
)
count_plot_grid$moisture <- 0
count_plot_grid$trap_nights <- 3
count_plot_grid$conditional_mean <- predict(
  fit_zinb2,
  newdata = count_plot_grid,
  dpar = "mu"
)
count_plot_grid$sigma <- predict(fit_zinb2, newdata = count_plot_grid, dpar = "sigma")
count_plot_grid$structural_zero_probability <- predict(
  fit_zinb2,
  newdata = count_plot_grid,
  dpar = "zi"
)
count_plot_grid$unconditional_mean <-
  (1 - count_plot_grid$structural_zero_probability) *
  count_plot_grid$conditional_mean
count_plot_grid$row_label <- paste(count_plot_grid$habitat, count_plot_grid$surface)
count_plot_long <- rbind(
  data.frame(
    count_plot_grid[c("habitat", "row_label")],
    component = "Conditional mean",
    value = count_plot_grid$conditional_mean
  ),
  data.frame(
    count_plot_grid[c("habitat", "row_label")],
    component = "Unconditional mean",
    value = count_plot_grid$unconditional_mean
  ),
  data.frame(
    count_plot_grid[c("habitat", "row_label")],
    component = "NB2 sigma",
    value = count_plot_grid$sigma
  ),
  data.frame(
    count_plot_grid[c("habitat", "row_label")],
    component = "Structural-zero probability",
    value = count_plot_grid$structural_zero_probability
  )
)
count_plot_long$component <- factor(
  count_plot_long$component,
  levels = c(
    "Conditional mean",
    "Unconditional mean",
    "NB2 sigma",
    "Structural-zero probability"
  )
)
count_plot_long$row_label <- factor(
  count_plot_long$row_label,
  levels = rev(unique(count_plot_grid$row_label))
)

ggplot(count_plot_long, aes(value, row_label, colour = habitat)) +
  geom_point(size = 2.7) +
  facet_wrap(~component, scales = "free_x", ncol = 2) +
  scale_colour_manual(values = c("degraded" = "#D55E00", "restored" = "#009E73")) +
  labs(
    title = "Zero-inflated counts have several fitted pieces",
    subtitle = "Facets keep count means, NB2 sigma, and structural-zero probabilities separate",
    x = "Response-scale fitted value",
    y = NULL,
    colour = "Habitat"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank(),
    legend.position = "bottom",
    plot.title = element_text(face = "bold"),
    plot.subtitle = element_text(colour = "grey30"),
    strip.text = element_text(face = "bold")
  )

## -----------------------------------------------------------------------------
AIC(fit_nb2, fit_zinb2)

