Dispatches to pigauto's phylogenetic baseline machinery and returns imputed latent-scale means and standard errors for every species.
Arguments
- data
object of class
"pigauto_data".- tree
object of class
"phylo".- splits
list (output of
make_missing_splits) orNULL.- model
character. Evolutionary model:
"BM"(default) or"OU".- graph
optional list returned by
build_phylo_graph. When supplied,graph$D(cophenetic distances) is reused for label propagation andgraph$R_phy(phylogenetic correlation matrix) is reused for BM imputation, avoiding duplicate \(O(n^2)\) allocations. WhenNULL(default), both matrices are computed here.- multi_obs_aggregation
character. How to aggregate multiple observations per species before the Level-C joint baseline:
"hard"(default) thresholds binary proportions at 0.5 and uses argmax for categorical, matching Phase 10 behaviour."soft"preserves species-level proportions and dispatches the truncated-Gaussian soft E-step (estep_liability_binary_soft) so that intermediate class frequencies contribute fractional liability evidence. Only relevant for multi-obs data with binary or categorical traits when the Level-C joint baseline is active.- lambda_mode
character. Pagel-lambda mode for the CONTINUOUS-FAMILY baseline (continuous, count, ordinal, proportion, zi_count magnitude columns only – see “Per-type lambda dispatch” in Details).
"fixed_1"preserves the default Brownian correlation matrix;"estimate","cv", and"bayes"delegate lambda handling to the per-column BM path. Binary/categorical/zi_gate columns are unaffected bylambda_modeand keep the threshold-joint / OVR-categorical baseline. Covariate caveat: whendata$covariatesis supplied, the per-column path switches tobm_impute_col_with_cov(), which has no lambda argument and always fits at lambda = 1;lambda_mode != "fixed_1"is then silently ignored for BM-eligible columns and a warning is emitted.- em_iterations
integer. Number of Phase 6 EM iterations for the threshold-joint baseline (binary + ordinal + OVR categorical). Default
0Ldisables the EM loop and preserves v0.9.1 output byte-for-byte. When>= 1, the BM rate \(\Sigma\) learned by the in-house joint solver (R/joint_mvn_solver.R) at iteration \(k\) is fed back as the per-trait prior SD at iteration \(k+1\), up toem_iterationstimes or untilem_tolconvergence.em_iterations = 1Lis a degenerate single-pass run and produces the same baseline output as0L;>= 2Lis needed for actual iteration. Only affects the threshold-joint path (continuous-only traits pass through the existing joint MVN path unchanged).- em_tol
numeric. Relative-Frobenius convergence tolerance for the Phase 6 / 7 EM loop. Early-stops when \(||\Sigma_k - \Sigma_{k-1}||_F / ||\Sigma_{k-1}||_F < \)
em_tol. Default1e-3.- em_offdiag
logical. Phase 7 opt-in: when
TRUEANDem_iterations >= 2L, each liability cell's prior at iteration \(k+1\) is the conditional-MVN \((\mu, sd)\) given the posterior liability of other traits at iteration \(k\), using the full off- diagonal entries of \(\Sigma\). Binary + ordinal only (OVR categorical stays on Phase 6 diagonal). DefaultFALSEpreserves Phase 6 behaviour.- joint_solver
character. Which solver estimates the joint Sigma / posterior for the joint MVN, threshold-joint, and OVR categorical baselines.
"inhouse"(default) uses the single-pass in-house solver (R/joint_mvn_solver.R) and is byte-identical to prior releases."rphylopars"delegates toRphylopars::phylopars()'s converged REML fit, which measured 0.14-1.27 lower z-RMSE than the in-house solver on AVONET300 (seedocs/dev-log/2026-08-16-continuous-gap-diagnosis.md); on failure or non-finite output it falls back to"inhouse"with a warning. Only affects the joint MVN / threshold-joint / OVR categorical paths above; ignored when those paths don't fire. Note thatlambda_mode != "fixed_1"disables the continuous-only joint MVN path (it has no lambda argument) but no longer disables the threshold-joint / OVR-categorical paths – see “Per-type lambda dispatch” in Details.- joint_refine_iter
integer, default
0L. Enables cross-trait refinement of the joint baseline's cell imputations using the estimated Sigma (the in-house solver'smax_iterEM cell-refinement;R/joint_mvn_solver.R).0Lpreserves current behaviour byte-for-byte. The refinement is guarded: the Sigma step must shrink each iteration, or the loop rolls back to the last good iterate and sets$diverged.Where it helps, measured (
docs/dev-log/2026-08-17-refinement-results.md): at Pagel's lambda around 0.2,joint_refine_iter = 3Llowered simulated RMSE by ~13\ near 1 it changes essentially nothing: on AVONET300, whose four continuous traits sit at lambda 0.993-0.998, every effect was within one Monte Carlo standard error. Note the reason is that refinement has nothing to add at high signal – not that the guard intervenes; on that data the guard does not fire. Use it when phylogenetic signal is moderate or low; expect no benefit on strongly conserved traits.
Value
A list with:
- mu
Numeric matrix (n_species x p_latent), baseline means in latent scale.
- se
Numeric matrix (n_species x p_latent), standard errors.
Details
When splits is supplied the val and test cells are masked to
NA before fitting, so the baseline is evaluated under the same
conditions as fit_pigauto.
Continuous-family columns use Brownian-motion conditional MVN baselines on the phylogenetic correlation matrix, either independently or through the joint MVN path when the data and optional dependencies support it. Binary, ordinal, categorical, and zero-inflated gate columns use the appropriate label-propagation or threshold/liability baseline candidates, with per-column fallbacks when a joint path is not available.
Covariates and the joint baseline (P1-8): data$covariates
is only used by the per-column BM path (bm_impute_col_with_cov()).
The joint MVN and threshold-joint (Rphylopars) baselines do not accept a
covariate design matrix, so when a joint path is selected (BM-eligible
columns >= 2, or binary/ordinal cols present, with Rphylopars available)
any supplied covariates are ignored for the BASELINE and a warning is
emitted; covariates still reach the GNN correction via
fit_pigauto regardless of which baseline path fires.
Per-type lambda dispatch (arc/lambda-per-type): lambda_mode
only ever governs the baseline for CONTINUOUS-FAMILY columns (continuous,
count, ordinal, proportion, zi_count magnitude). Binary, ordinal,
categorical, and zero-inflated gate columns keep the threshold-joint /
OVR-categorical joint baseline (always fit at lambda = 1) regardless of
lambda_mode – there is no discrete-trait analogue of Pagel's
lambda, and previously forcing these columns onto label propagation any
time lambda_mode != "fixed_1" cost 19pp of Trophic.Level accuracy
on AVONET (0.789 -> 0.600; see
docs/dev-log/2026-08-16-external-comparison-results.md). When
lambda_mode != "fixed_1" and the threshold-joint baseline fires
for a dataset with binary/ordinal AND continuous-family columns, the
joint liability fit still uses the continuous-family columns internally
to inform the joint Sigma (and hence the binary/ordinal posteriors); only
its continuous-column baseline OUTPUT is discarded in favour of the
lambda-aware per-column BM fit.
Examples
# \donttest{
data(avonet300, tree300, package = "pigauto")
tree <- ape::keep.tip(tree300, tree300$tip.label[seq_len(30L)])
traits <- avonet300[match(tree$tip.label, avonet300$Species_Key),
c("Mass", "Wing.Length"), drop = FALSE]
rownames(traits) <- tree$tip.label
pd <- preprocess_traits(traits, tree)
splits <- make_missing_splits(pd$X_scaled, trait_map = pd$trait_map)
bl <- fit_baseline(pd, tree, splits)
# }
