--- title: "First-week intervals: fit, profile, and boundary" description: "Fit a small random-intercept model, inventory profile targets, profile an RE-SD, and read profile.boundary before trusting the interval." output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{First-week intervals: fit, profile, and boundary} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` This page is for an applied ecology or evolution user in the first week with `drmTMB`. It walks one short path: fit → inventory targets → profile a random-effect SD → read `profile.boundary` → decide when not to trust the interval. It restates the default uncertainty story in `?confint.drmTMB`; it does **not** claim nominal coverage on every route. For the longer post-fit toolbox (`check_drm()`, Wald shortcuts, bootstrap, `newdata` profiles), read [Checking and using fitted models](https://itchyshin.github.io/drmTMB/articles/model-workflow.html). For which cells have coverage evidence, read [Can I fit and report this model?](capability-and-limits.html). ## Default recipe (read once) 1. Fixed effects and other routine Wald-ready targets: start with `confint(fit)`. 2. Random-effect SDs and other direct variance components: prefer `confint(..., method = "profile")` after `profile_targets()` shows the row is profile-ready. 3. Always read `conf.status` and `profile.boundary` on the returned table. 4. A computable interval is not coverage certification. ## Fit a small random-intercept model The example is intentionally tiny and non-phylogenetic so it stays CRAN-safe and fast: ```{r} library(drmTMB) set.seed(13) n_site <- 18 n_per_site <- 6 site <- factor(rep(seq_len(n_site), each = n_per_site)) site_effect <- rnorm(n_site, sd = 0.45) fish_site <- data.frame( site = site, temperature = runif(n_site * n_per_site, -1.5, 1.5), habitat = factor( sample(c("reef", "kelp"), n_site * n_per_site, replace = TRUE) ) ) site_mu <- site_effect[as.integer(fish_site$site)] fish_site$growth <- rnorm( nrow(fish_site), mean = 1 + 0.7 * fish_site$temperature + 0.35 * (fish_site$habitat == "kelp") + site_mu, sd = 0.35 ) fit_site <- drmTMB( bf(growth ~ temperature + habitat + (1 | site), sigma ~ 1), family = gaussian(), data = fish_site ) ``` Run `check_drm(fit_site)` before interpreting estimates. A clean diagnostic table catches optimizer and Hessian problems; it does not certify coverage. ## Inventory targets, then profile the RE-SD ```{r} profile_targets(fit_site) ``` Wald is the fast first pass for fixed effects and other Wald-ready rows: ```{r} confint(fit_site, parm = "fixed_effects") ``` For the site random-intercept SD, prefer the profile route and copy the exact target name from `profile_targets()`: ```{r} ci_site <- confint( fit_site, parm = "sd:mu:(1 | site)", method = "profile" ) ci_site ``` ## Read `profile.boundary` before you trust the interval Successful profile rows use `conf.status = "profile"`. When the profile lands near a variance-component boundary, `profile.boundary` is `TRUE` and `confint(method = "profile")` warns with class `drmTMB_profile_boundary_warning`. ```{r} ci_site[, c("parm", "lower", "upper", "conf.status", "profile.boundary")] ``` **When not to trust a returned interval** - `profile.boundary == TRUE` on a usable interval: treat the interval as indicative of scale, not as a calibrated `level` interval. The D-117 10-group random-effect SD gate measured conditional coverage as low as 0.1021 against a nominal 0.95 when that flag was set (and 0 of 89 in one cell where boundary hits are rare); the same behaviour appears in `lme4` on the same seeds (see `?confint.drmTMB` Boundary intervals). Prefer more groups when the design allows it. - Expect the SD itself to run low. At this design the point estimate was biased 8.3%-15.8% below truth -- a property of maximum likelihood at few groups, not of this package. Unconditionally the interval still covered 0.9248 against a nominal 0.95 over 400,000 attempts, so the flagged rows are the bad case rather than the usual one. - `REML = TRUE` helps here, measurably, without fixing everything: on the same design and seeds it moved profile coverage from 0.9248 to 0.9463, roughly halved the SD's downward bias, and made the misses nearly balanced. The boundary caveat above still applies under REML, and this is measured on this one design only -- the default estimator is unchanged. - `conf.status` is `profile_failed`, `clamp_limited`, `wald_at_boundary`, `newdata_required`, or `derived_interval_unavailable`: there is no usable calibrated interval on that row — follow the action implied by the status. - The capability guide does not name the exact cell as inference-ready: report the point estimate only, or keep the interval experimental. ## What to read next | Next question | Go here | | --- | --- | | Full post-fit toolbox and `conf.status` table | [Checking and using fitted models](https://itchyshin.github.io/drmTMB/articles/model-workflow.html) | | Which family × effect rows have coverage evidence | [Can I fit and report this model?](capability-and-limits.html) | | Large tip counts and `se_group_sd` | [Working with large data](large-data.html) | | Reference detail for Wald, profile, and boundary warnings | `?confint.drmTMB` |