make_zero_one_map: Fix factor-level intercepts for groups with all zeros or ones

View source: R/zero-one-map.R

make_zero_one_mapR Documentation

Fix factor-level intercepts for groups with all zeros or ones

Description

A grouping factor level (e.g. factor(year), but also possibly a region, stratum, region-by-year interaction, or any other factor level) with all-zero observations can drive the corresponding factor-coded coefficient to -Inf, causing convergence problems. In delta/hurdle and binomial or beta-binomial models, an all-positive or all-success level can similarly drive the encounter coefficient to +Inf. Tweedie will have the same issue with all zeros.This helper returns map and start lists suitable for passing through sdmTMBcontrol() to fix those problem coefficients at large positive/negative values. This mirrors an approach taken in VAST.

Usage

make_zero_one_map(formula, data, group, family, weights = NULL, value = 20)

Arguments

formula

The fixed-effect formula to be used in sdmTMB(). For delta models, either a single formula applied to both linear predictors, or a list() of two formulas. The formula should contain factor(group) (or as.factor(group), or a pre-converted factor column) so each level has its own column, typically response ~ 0 + factor(year).

data

The data frame passed to sdmTMB().

group

Character. Name of the grouping column in data whose levels should be inspected for all-zero/all-positive observations. Most commonly the time column.

family

The sdmTMB family object (e.g., tweedie(), delta_gamma()).

weights

Optional numeric vector of trial sizes for non-delta binomial or beta-binomial models fit with proportion data. This should match the weights argument passed to sdmTMB(). Ignored for other families.

value

Positive numeric. Magnitude at which to fix the intercept; plogis(20), plogis(-20), and exp(-20) are all within ~2e-9 of 0 or 1, which is plenty.

Details

Inspect the returned all_zero_levels and all_one_levels elements before fitting to make sure the detected problem levels match your expectation.

Although the motivating use case is years in fisheries index standardization (e.g., density ~ 0 + factor(year)), the same mechanism applies to any factor whose levels each get a column in the design matrix. For an interaction such as region by year, create a combined column (e.g., data$region_year <- interaction(data$region, data$year, drop = TRUE)) and use ~ 0 + region_year.

For Poisson-link delta families (e.g., delta_gamma(type = "poisson-link")), the first linear predictor controls both the encounter probability and the positive rate, so fixing it shifts the meaning of the second-linear-predictor coefficients in those levels. The helper still works for all-zero levels (the large negative value makes encounter probability ~0, and the level contributes ~0 to the index), but emits a message for all-positive levels. Although this affects interpretation of the coefficients, the overall combined prediction remains OK.

Value

A named list with elements:

map

A list with b_j (and b_j2 for delta families) factors suitable for sdmTMBcontrol(map = ...).

start

A list with b_j (and b_j2) starting-value vectors suitable for sdmTMBcontrol(start = ...).

all_zero_levels

Group levels with all-zero observations.

all_one_levels

Group levels with all-positive observations for delta families, or all-success observations for non-delta binomial/beta-binomial families. Empty for other families.

See Also

sdmTMBcontrol(), sdmTMB()

Examples

d <- pcod
d$density[d$year == 2003] <- 0 # force an all-zero year

m <- make_zero_one_map(
  formula = density ~ 0 + factor(year),
  data = d,
  group = "year",
  family = tweedie()
)
m$all_zero_levels
m$all_one_levels
m$start
m$map


mesh <- make_mesh(d, c("X", "Y"), cutoff = 15)
fit <- sdmTMB(
  density ~ 0 + factor(year),
  data = d,
  mesh = mesh,
  time = "year",
  family = tweedie(),
  spatial = "on",
  spatiotemporal = "off", # off for example speed
  control = sdmTMBcontrol(map = m$map, start = m$start) #<
)
fit
coef(fit)


sdmTMB documentation built on Oct. 17, 2026, 1:07 a.m.