This is the human-readable diagnostic to call right after
fit <- gllvmTMB(...). It combines the quick numerical screen
(sanity_multi()), the rotation identifiability advisory, and key
biological summaries (correlation diagonals, ICCs, communalities)
into a single report with explicit next-step hints for any WARN
signal. Use check_gllvmTMB() when you need the same fit-health
checks as a stable table for scripts or reports.
Usage
gllvmTMB_diagnose(
object,
gradient_thresh = 0.01,
se_thresh = 100,
big_corr_thresh = 0.5,
verbose = TRUE
)Arguments
- object
A fit returned by
gllvmTMB().- gradient_thresh, se_thresh
Forwarded to
sanity_multi().- big_corr_thresh
Threshold above which a
Sigma_Bcorrelation off-diagonal is flagged as worth highlighting. Default 0.5.- verbose
If
TRUE(default), prints the report. Always returns the structured list invisibly.
Value
Invisibly a list with components: sanity (the
sanity_multi() flags), rotation (rotation-advisory list),
Sigma_B, Sigma_W, ICC_site, communality_B, communality_W,
and hints (character vector of suggested actions).
Details
Scope: first-line convergence, Hessian, standard-error, restart, and rotation diagnostics. It reports risks and summaries but does not replace profile, bootstrap, or simulation calibration. Target-explicit validation will decide which interval warnings can be promoted to broader guarantees.
See also
check_gllvmTMB(), sanity_multi(),
suggest_lambda_constraint(),
extract_Sigma(), extract_communality(),
compare_dep_vs_two_psi() / compare_indep_vs_two_psi() for
identifiability cross-checks on the paired phylogenetic fit.
Examples
set.seed(1)
# Eight traits on 100 units, each unit measured twice. Odd-numbered
# traits share one latent axis, even-numbered traits share the other.
Lambda <- cbind(c(0.9, 0, 0.8, 0, 0.7, 0, 0.6, 0),
c(0, 0.9, 0, 0.8, 0, 0.7, 0, 0.6))
sim <- simulate_unit_trait(n_units = 100, n_obs_per_unit = 2,
n_traits = 8, Lambda_B = Lambda,
psi_B = 0.3, sigma2_eps = 0.3)
fit <- gllvmTMB(
value ~ 0 + trait + latent(0 + trait | unit, d = 2, unique = TRUE),
data = sim$data, trait = "trait", unit = "unit"
)
# The printed report has four parts: optimiser checks, rotation,
# biological summaries (variances, strong trait correlations and
# communalities) and suggested next steps.
dx <- gllvmTMB_diagnose(fit)
#>
#> ── 1. Optimiser & numerical sanity ──
#>
#> Optimiser convergence (== 0): PASS
#> Max |gradient| < 1.0e-02: PASS (max |gr| = 0.00115)
#> Hessian positive-definite: PASS
#> Max fixed-effect SE < 100: PASS (max SE = 0.112)
#> latent(unit, d=2) diag loadings non-zero: PASS (min |Lambda_B diag| = 0.763)
#> ── 2. Rotational identifiability ──
#>
#> ! `Lambda_B` (rank d = 2) is identified only up to rotation.
#>
#> ── 3. Biological summaries ──
#>
#>
#> Sigma_B (between-unit covariance) diagonal and correlations:
#> 0.952 1.071 1.087 0.812 0.818 0.794 0.666 0.535
#> 12 trait pair(s) with |corr| > 0.50 (showing 8 of 12):
#> trait_3 ~ trait_1 : +0.64
#> trait_5 ~ trait_1 : +0.55
#> trait_7 ~ trait_1 : +0.58
#> trait_4 ~ trait_2 : +0.70
#> trait_6 ~ trait_2 : +0.68
#> trait_8 ~ trait_2 : +0.77
#> trait_5 ~ trait_3 : +0.59
#> trait_7 ~ trait_3 : +0.61
#>
#> Global communalities:
#> 0.612 0.825 0.703 0.591 0.506 0.56 0.55 0.712
#> ── 4. Suggested next steps ──
#>
#> • Lambda is identified only up to rotation. For a unique loading matrix, see
#> `suggest_lambda_constraint()`. For interpretation, use `getLoadings(fit,
#> rotate = "varimax")`. The implied Sigma matrices are rotation-invariant and
#> need no constraint.
# The same results come back invisibly as a list, for use in scripts.
names(dx)
#> [1] "sanity" "rotation" "Sigma_B" "Sigma_W"
#> [5] "ICC_site" "communality_B" "communality_W" "hints"
dx$hints
#> [1] "Lambda is identified only up to rotation. For a unique loading matrix, see `suggest_lambda_constraint()`. For interpretation, use `getLoadings(fit, rotate = \"varimax\")`. The implied Sigma matrices are rotation-invariant and need no constraint."
round(dx$Sigma_B$R_B, 2)
#> trait_1 trait_2 trait_3 trait_4 trait_5 trait_6 trait_7 trait_8
#> trait_1 1.00 0.00 0.64 -0.04 0.55 0.04 0.58 -0.03
#> trait_2 0.00 1.00 0.16 0.70 0.07 0.68 0.04 0.77
#> trait_3 0.64 0.16 1.00 0.09 0.59 0.17 0.61 0.12
#> trait_4 -0.04 0.70 0.09 1.00 0.02 0.57 -0.01 0.65
#> trait_5 0.55 0.07 0.59 0.02 1.00 0.09 0.53 0.04
#> trait_6 0.04 0.68 0.17 0.57 0.09 1.00 0.07 0.63
#> trait_7 0.58 0.04 0.61 -0.01 0.53 0.07 1.00 0.01
#> trait_8 -0.03 0.77 0.12 0.65 0.04 0.63 0.01 1.00
dx$communality_B
#> trait_1 trait_2 trait_3 trait_4 trait_5 trait_6 trait_7 trait_8
#> 0.6117654 0.8250612 0.7025898 0.5906170 0.5058653 0.5602779 0.5503689 0.7116746
# verbose = FALSE skips the printed report; the numbers behind the
# optimiser checks are in $sanity.
quiet <- gllvmTMB_diagnose(fit, verbose = FALSE)
str(quiet$sanity)
#> List of 6
#> $ converged : logi TRUE
#> $ max_gradient : num 0.00115
#> $ sdreport_ok : logi TRUE
#> $ pd_hessian : logi TRUE
#> $ max_se : num 0.112
#> $ rr_B_min_loading: num 0.763
