Checking and using fitted models
Status — Stable (Gaussian post-fit + inference)
Mirrors drmTMB's Checking and using fitted models. In DRM.jl today: coefficient extraction, Wald standard errors, Wald and profile-likelihood confidence intervals, fitted values, residuals, predict (new data), simulate, and parametric bootstrap intervals (bootstrap_ci).
Once you have a fit, pull coefficients and quantify their uncertainty.
using DRM, Random
Random.seed!(5)
n = 1500
x = randn(n)
y = 0.4 .+ 0.7 .* x .+ exp.(-0.2 .+ 0.3 .* x) .* randn(n)
fit = drm(bf(@formula(y ~ x), @formula(sigma ~ x)), Gaussian(); data = (; y, x))
coef(fit, :mu) # mean coefficients
stderror(fit) # Wald standard errors (all coefficients)4-element Vector{Float64}:
0.022288731088698478
0.019641780488133715
0.018257479230049008
0.018428125720313257Wald confidence intervals for every coefficient (drmTMB's default interval method), one row per coefficient:
confint(fit; level = 0.95)4-element Vector{@NamedTuple{param::Symbol, coef::String, estimate::Float64, lower::Float64, upper::Float64}}:
(param = :mu, coef = "(Intercept)", estimate = 0.39349561099036484, lower = 0.34981050079541753, upper = 0.43718072118531215)
(param = :mu, coef = "x", estimate = 0.6948983766847261, lower = 0.6564011943357424, upper = 0.7333955590337098)
(param = :sigma, coef = "(Intercept)", estimate = -0.20618165608600256, lower = -0.24196565782538676, upper = -0.17039765434661835)
(param = :sigma, coef = "x", estimate = 0.2767183623681545, lower = 0.24059989965376422, upper = 0.3128368250825448)Each row carries its parameter (:mu / :sigma), coefficient name, point estimate, and interval bounds. Intervals are on each parameter's working scale — μ on the response scale, σ on log σ — so for a residual-SD ratio you exponentiate the σ bounds.
For a profile-likelihood interval (drmTMB's method = "profile"), pass method = :profile. It inverts the likelihood-ratio statistic — re-optimising the nuisance parameters at each fixed value — so it is asymmetric and exact under the LR statistic where Wald is only a quadratic approximation. Use Wald for speed; profile when a parameter's likelihood is skewed (a scale or variance term):
confint(fit; method = :profile)4-element Vector{@NamedTuple{param::Symbol, coef::String, estimate::Float64, lower::Float64, upper::Float64}}:
(param = :mu, coef = "(Intercept)", estimate = 0.39349561099036484, lower = 0.34976295310997185, upper = 0.43719443926054)
(param = :mu, coef = "x", estimate = 0.6948983766847261, lower = 0.6564190723861569, upper = 0.733474660727599)
(param = :sigma, coef = "(Intercept)", estimate = -0.20618165608600256, lower = -0.24154386644368464, upper = -0.16996567969655801)
(param = :sigma, coef = "x", estimate = 0.2767183623681545, lower = 0.24060654717320173, upper = 0.3128428527218225)What scale is the interval on?
A σ row gives an interval for a log σ coefficient. exp(lower) and exp(upper) give the interval for the multiplicative effect on the residual SD. See Which scale are you modelling?.
Fitted values and residuals
ŷ = fitted(fit) # fitted means (Xβ̂)
res = residuals(fit) # observed − fitted
(n = length(res), sum_resid = round(sum(res), digits = 6))(n = 1500, sum_resid = 12.76284)For a bivariate model, fitted / residuals return one vector per response, keyed Dict(:mu1 => …, :mu2 => …).
Predict the mean on new data (population level — random/structured effects integrated out):
predict(fit, (; x = [-1.0, 0.0, 1.0])) # μ̂ at three new x values3-element Vector{Float64}:
-0.30140276569436125
0.39349561099036484
1.088393987675091Simulating from a fitted model
simulate draws a parametric replicate — the building block of a parametric bootstrap:
y_rep = simulate(fit; rng = MersenneTwister(1)) # one replicate response vector
length(y_rep)1500Bootstrap confidence intervals
bootstrap_ci automates the parametric bootstrap — simulate B replicates, refit each, and take percentile intervals. It takes the same arguments as drm (plus B), since it refits internally:
bootstrap_ci(bf(@formula(y ~ x), @formula(sigma ~ x)), Gaussian();
data = dat, B = 500, level = 0.95)Returns the same (param, coef, estimate, lower, upper) rows as confint. Use Wald (confint) for speed; bootstrap when you want fewer distributional assumptions.
For longer jobs, use bootstrap_result to keep the audit trail:
bres = bootstrap_result(bf(@formula(y ~ x), @formula(sigma ~ x)), Gaussian();
data = dat, B = 500, threads = true,
failures = :skip)
(bres.attempted, bres.used, bres.failed)See also
Profile and bootstrap intervals,
predict, andsimulateare all shown above; for their evidence status see Evidence & limits.