This vignette demonstrates complete analysis workflows using
tulpaRatio. Each example shows data preparation, model fitting,
diagnostics, and inference. See vignette("getting-started")
for basic usage and vignette("philosophy") for statistical
motivation.
Goal: Estimate standardized catch per unit effort (CPUE) while accounting for varying effort and site effects.
Challenge: Effort varies systematically across sites and seasons. Sites with more effort appear to have more precise CPUE, but this precision is illusory if effort correlates with catch conditions.
Strategy: Joint model with shared site effects.
library(tulpaRatio)
# Simulated fisheries data
set.seed(42)
n_sites <- 15
n_seasons <- 4
n_obs <- n_sites * n_seasons * 3
fisheries <- data.frame(
site = factor(rep(1:n_sites, each = n_seasons * 3)),
season = factor(rep(rep(c("spring", "summer", "fall", "winter"), each = 3), n_sites)),
depth = rnorm(n_obs, 50, 20),
catch = rpois(n_obs, lambda = 20),
effort_hours = rgamma(n_obs, shape = 4, rate = 0.5)
)
head(fisheries)Goal: Estimate the proportion of a target species relative to total catch across habitats.
Challenge: Both target count and total count are overdispersed. Using binomial models would underestimate uncertainty.
Strategy: Negative binomial for both counts with shared observer effects.
set.seed(123)
n_plots <- 30
n_visits <- 4
abundance <- data.frame(
plot = factor(rep(1:n_plots, each = n_visits)),
habitat = factor(rep(sample(c("forest", "grassland", "wetland"), n_plots, replace = TRUE), each = n_visits)),
observer = factor(sample(1:10, n_plots * n_visits, replace = TRUE))
)
# Plot and observer effects scale target and total counts alike; the
# target share is higher in grassland and lower in wetland
u <- rnorm(n_plots, sd = 0.4)[abundance$plot] + rnorm(10, sd = 0.4)[abundance$observer]
hab <- c(forest = 0, grassland = 0.4, wetland = -0.3)[as.character(abundance$habitat)]
abundance$target_count <- rnbinom(n_plots * n_visits, size = 3, mu = 15 * exp(u + hab))
abundance$total_count <- rnbinom(n_plots * n_visits, size = 5, mu = 50 * exp(u))
# Ensure target <= total
abundance$target_count <- pmin(abundance$target_count, abundance$total_count)Goal: Estimate detection probability from camera trap data with spatial structure.
Challenge: Detection varies by site characteristics and has spatial autocorrelation.
Strategy: Binomial model with spatial random effects.
set.seed(456)
n_cameras <- 40
# Camera locations on a grid
coords <- expand.grid(x = 1:8, y = 1:5)
coords <- coords[1:n_cameras, ]
# Create adjacency matrix (queen contiguity)
adj <- matrix(0, n_cameras, n_cameras)
for (i in 1:n_cameras) {
for (j in 1:n_cameras) {
if (i != j) {
dist <- sqrt((coords$x[i] - coords$x[j])^2 + (coords$y[i] - coords$y[j])^2)
if (dist <= sqrt(2) + 0.01) adj[i, j] <- 1
}
}
}
camera_data <- data.frame(
camera = factor(1:n_cameras),
vegetation = rnorm(n_cameras),
nights_active = sample(20:30, n_cameras, replace = TRUE)
)
# Detection varies smoothly across the grid
field <- 0.6 * sin(coords$x / 2) - 0.4 * cos(coords$y / 2)
camera_data$detections <- rbinom(
n_cameras,
size = camera_data$nights_active,
prob = plogis(-0.8 + 0.3 * camera_data$vegetation + field)
)Goal: Estimate annual trends in population indices while accounting for temporal autocorrelation.
Strategy: Random walk prior on year effects.
A temporal effect shared by numerator and denominator cancels in
their ratio, so the walk on the index uses shared = FALSE;
the warning this prints notes that such a walk can also absorb
unmeasured year-to-year drivers of the ratio.
| Workflow | Family | Key Feature |
|---|---|---|
| Fisheries CPUE | poisson_gamma |
Shared site effects |
| Relative Abundance | negbin_negbin |
Overdispersed counts |
| Detection Probability | binomial |
Spatial CAR |
| Temporal Trends | poisson_gamma |
Random walk |
vignette("getting-started") - Basic usagevignette("philosophy") - Statistical motivationvignette("spatial-temporal") - Spatial and temporal
details