
Impute missing traits using a fitted pigauto model
Source:R/predict_pigauto.R
predict.pigauto_fit.RdRuns a single forward pass through the fitted model and returns imputed
trait values back-transformed to the original scale. Supports all
trait types (continuous, binary, categorical, ordinal, count, proportion,
zero-inflated count, multi-proportion)
and MC dropout for stochastic prediction diagnostics (when
n_imputations > 1). The fitted model is a gated ensemble
of a phylogenetic baseline and a graph neural network correction;
prediction is the per-trait blend
(1 - r_cal) * baseline + r_cal * delta_GNN.
Arguments
- object
object of class
"pigauto_fit".- newdata
NULL(use the training data) or a"pigauto_data"object for new species.- return_se
logical. Compute standard errors? (default
TRUE).- n_imputations
integer. Number of stochastic imputation draws — BM posterior samples plus GNN dropout — (default
1L). Set to e.g. 10 or 20 to inspect stochastic prediction variation. These are not validated analysis-aware multiple imputations.- baseline_override
optional
list(mu, se)with the same shape asobject$baseline. When supplied, predictions use this baseline instead of the one saved in the fit. Used internally bymulti_impute_trees()to reuse a trained GNN across posterior trees. Most users can ignore this. DefaultNULL(use the fit's own baseline).- pool_method
character. How to pool the M decoded draws when
n_imputations > 1:"median"(default) takes the per-cell median for count / proportion / zi_count magnitude traits — robust to single dropout-noisy draws amplified by non-linear decoders (expm1/plogis)."mean"restores the pre-v0.9.2 arithmetic-mean pooling. Continuous / ordinal / binary / categorical pooling is unchanged (linear / probability-averaged). See issue #40.- clamp_outliers
logical. Phase G (v0.9.1.9011+). When
TRUE, post-back-transform predictions for log-transformed continuous, count, and zi_count magnitude traits are capped attm$obs_max * clamp_factor(tm$obs_maxis the observed maximum on the original scale, set at preprocess time). Targets tail-extrapolation modes amplified byexp()/expm1()back-transforms. DefaultFALSEpreserves v0.9.1 behaviour exactly.- clamp_factor
numeric scalar (>= 1). Multiplicative factor on the observed maximum used by
clamp_outliers. Default5. Ignored whenclamp_outliers = FALSE.- match_observed
character, one of
c("none", "pmm"). Phase G' (v0.9.1.9012+). When"pmm", uses Predictive Mean Matching (Little 1988; Buuren mice) on the at-risk types (log-transformed continuous, count, zi_count magnitude, proportion). For each missing cell, finds thepmm_Kobserved cells whose own predictions are closest to the missing cell's prediction, samples one, and returns its observed value. Imputed values are guaranteed to lie in the observed data range – no extrapolation is possible by construction.When to use: PMM is a niche feature in pigauto. The package already provides conformal prediction intervals (calibrated against held-out residuals) and stochastic conformal-width and Brownian/MC-dropout prediction draws. PMM is only worth enabling for: (a) methodological comparison against mice / equivalent packages, or (b) workflows that specifically require imputed values to come from the observed data pool. For tail safety on single-imputation point estimates, prefer
clamp_outliers = TRUE. PMM failed the downstream fixed-effect redesign pilot and is unsupported for inference.The Phase G' acceptance bench (
useful/MEMO_2026-05-01_phase_g_prime_results.md) confirmed PMM does not strictly improve point-estimate RMSE over the no-PMM default: it wins on extrapolating cells (e.g. AVONET Casuarius) but loses on cells where the GNN's prediction is already accurate (donor-mismatch noise).Discrete-class types (binary / categorical / ordinal / multi_proportion) and un-log continuous: no-op. Default
"none"preserves v0.9.1.9011 behaviour exactly.- pmm_K
integer scalar (>= 1). Donor pool size for PMM. Default
5L(mice convention). Ignored whenmatch_observed = "none".- ...
ignored.
Value
A list of class "pigauto_pred" with:
- imputed
data.frame of imputed values in original scale with proper R types (numeric, integer, factor, ordered).
- imputed_latent
Numeric matrix (n x p_latent) of predictions in latent scale.
- se
Numeric matrix (n x n_original_traits) of per-cell uncertainty. See the "What
$semeans per trait type" section below for what each trait type's column actually contains.NULLifreturn_se = FALSE.- probabilities
Named list. Binary traits: numeric probability vector. Categorical traits: n x K probability matrix. Other types: not present.
- imputed_datasets
List of M stochastic prediction data.frames when
n_imputations > 1;NULLotherwise. Not supported for downstream inference.- trait_map
Trait map from the fitted model.
- species_names
Character vector.
- trait_names
Character vector.
- n_imputations
Integer, number of imputations performed.
Details
When n_imputations > 1, each imputation m draws a BM
posterior sample t_BM_draw ~ N(BM_mu, BM_se) on the latent scale
for originally-missing cells (BM_se = 0 for observed cells so they
are never perturbed). The model runs in train mode (GNN dropout active)
using t_BM_draw as input. The final blend is
(1 - r_cal) * t_BM_draw + r_cal * GNN_delta(t_BM_draw): when the
calibrated gate is zero the imputation is a pure BM posterior draw; when
r_cal > 0 both BM draws and GNN dropout contribute variance. Point
estimates are the mean (continuous, count) or mode (binary, categorical,
ordinal) across passes. The M complete datasets are returned in
imputed_datasets for prediction diagnostics. These datasets are not
supported for downstream inference. Use multi_impute_analysis() for
the narrow analysis-aware backend after consulting its supported-model and
lifecycle documentation.
gnn = FALSE fits (traditional-stats mode): when the fit was
produced with gnn = FALSE, no GNN was ever trained, so this
function makes no torch:: calls at all – prediction is the
calibrated baseline blend r_cal_bm * baseline + r_cal_mean *
mean_baseline_per_col (r_cal_gnn is always zero) computed on
plain R matrices. Two baselines are available: in production mode (the
default, no .mask_observed_idx) predictions use baseline_full
– fit with splits = NULL, so every observed cell can inform every
other cell; in evaluation mode (.mask_observed_idx supplied, as
used internally for val/test scoring) predictions use the held-out
baseline instead, so scored cells never see their own value.
n_imputations > 1 draws are BM posterior samples
MU_m ~ N(baseline_mu, baseline_se) at originally-missing cells
(observed cells are always restored to their true value), with no GNN
dropout contribution.
Decoding per type:
- continuous
reverse z-score, then
exp()if log-transformed- binary
sigmoid(latent)to probability, round to 0/1- count
reverse z-score of log1p,
expm1(), round, clip >= 0- ordinal
reverse z-score, round to nearest valid integer level
- categorical
softmax()over K latent columns, argmax
Conformal intervals: conformal_lower / conformal_upper
use whatever conformal_method the fit was trained with
(fit_pigauto). For "mondrian", the half-width is not
constant across rows: each target cell's phylogenetic locality (mean
cophenetic distance to its 5 nearest training-observed species for that
trait) picks the near- or far-stratum score stored on the fit, so
predictions in undersampled clades get wider intervals. See
fit_pigauto's conformal_method argument for details.
zi_count conformal intervals are conditional on non-zero.
conformal_lower[, nm] / conformal_upper[, nm] for a
zi_count trait back-transform the magnitude latent (log1p-z of the
count given it is non-zero) plus/minus the conformal half-width
that was calibrated on that same magnitude column
(fit_pigauto's compute_conformal_scores() scores
zi_count on latent_cols[2], never on the decoded E[X]).
They are not an interval for the expected value
E[X] = P(nonzero) * E[count | nonzero] – constructing a naive
\(\pm\)quantile interval directly on E[X] would conflate gate
and magnitude uncertainty into one number with no valid coverage
interpretation. The gate probability P(nonzero) is available
separately in pred$probabilities[[nm]]; combine it with the
conditional interval yourself if you need an E[X]-scale bound.
What $se means per trait type
pred$se is a single n x n_original_traits matrix, but its
per-column meaning differs by trait type — do not treat every column as
a Gaussian standard error. This table is the definitive reference; other
pigauto documentation (e.g. impute's return value) points
here instead of repeating it.
| Type | Value | Is it a Gaussian SE? |
| continuous / count / ordinal / proportion | model-dependent BM/joint conditional SD, delta-method back-transformed to original scale | It is not total final blended-prediction uncertainty and not a package-certified interval input. |
| binary | min(p, 1-p) | No. Probability of being wrong (0 = certain, 0.5 = maximally uncertain). |
| categorical | 1 - max(p_k) | No. Margin from certainty (0 = certain, (K-1)/K = maximally uncertain). |
| zi_count | delta-method SE of E[X] = P(nonzero) * E[count | nonzero] | No. Mixes gate and magnitude uncertainty into one number; not a magnitude-scale SE. Use conformal_lower / conformal_upper (conditional-on-nonzero; see above) plus probabilities (the gate) for interval-scale zi_count uncertainty instead. |
| multi_proportion | NA | Not computed; use the CLR latent SE (se_latent) instead. |
These families are incompatible objects sharing one column name:
only the continuous-family entries are standard errors in the Gaussian
sense, and none of the binary / categorical / zi_count entries
may be used in Rubin's-rules arithmetic. The supported downstream
multiple-imputation routes start with
multi_impute_analysis or, for continuous traits,
multi_impute with draws_method = "posterior", then
use with_imputations and pool_mi.
Examples
# \donttest{
data(avonet300, tree300)
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
data <- preprocess_traits(traits, tree)
splits <- make_missing_splits(data$X_scaled, trait_map = data$trait_map)
fit <- fit_pigauto(data, tree, splits = splits, epochs = 5L,
verbose = FALSE)
#> Warning: phylo_signal_gate requires the 'phytools' package; returning NA for all traits.
#> Warning: Small validation set for 2 trait(s): Mass (n=2), Wing.Length (n=1). Calibrated gate and conformal scores will be noisy for these trait(s). 95% split-conformal coverage is NOT achievable for 2 trait(s) with fewer than 19 validation cells (Mass (n=2), Wing.Length (n=1)): the achievable ceiling is n_val / (n_val + 1), which only reaches 0.95 at n_val >= 19. See `?fit_pigauto` under 'Calibration at small n' for smoothing options.
pred <- predict(fit, return_se = TRUE)
pred$imputed
#> Mass Wing.Length
#> Nothoprocta_pentlandii 300.7 148.2
#> Eudromia_formosa 772.9 223.9
#> Rhea_americana 23000.0 604.5
#> Ptilopachus_petrosus 193.0 117.2
#> Bambusicola_fytchii 313.7 142.8
#> Francolinus_psilolaemus 478.3 167.3
#> Meleagris_ocellata 5525.0 362.5
#> Dendragapus_obscurus 1047.3 226.5
#> Tympanuchus_phasianellus 882.4 200.6
#> Tragopan_temminckii 1163.4 236.8
#> Arborophila_campbelli 336.0 129.5
#> Megapodius_nicobariensis 836.7 226.8
#> Dendrocygna_guttata 800.0 206.6
#> Chloephaga_rubidiceps 2086.0 325.6
#> Somateria_spectabilis 1616.7 266.3
#> Rhynochetos_jubatus 860.0 264.3
#> Caprimulgus_longirostris 45.6 151.6
#> Caprimulgus_saturatus 52.0 144.5
#> Aegotheles_cristatus 42.8 135.8
#> Lampornis_clemenciae 7.6 73.7
#> Lampornis_calolaemus 5.1 62.6
#> Stellula_calliope 2.6 41.4
#> Hylocharis_sapphirina 4.4 49.5
#> Hylocharis_chrysura 4.5 51.8
#> Amazilia_cyanifrons 5.0 55.6
#> Campylopterus_phainopeplus 7.7 68.1
#> Metallura_iracunda 3.9 58.1
#> Anthracothorax_viridis 6.6 62.8
#> Polytmus_theresiae 3.8 56.6
#> Phaethornis_striigularis 3.0 39.1
pred$se
#> Mass Wing.Length
#> Nothoprocta_pentlandii 0 0
#> Eudromia_formosa 0 0
#> Rhea_americana 0 0
#> Ptilopachus_petrosus 0 0
#> Bambusicola_fytchii 0 0
#> Francolinus_psilolaemus 0 0
#> Meleagris_ocellata 0 0
#> Dendragapus_obscurus 0 0
#> Tympanuchus_phasianellus 0 0
#> Tragopan_temminckii 0 0
#> Arborophila_campbelli 0 0
#> Megapodius_nicobariensis 0 0
#> Dendrocygna_guttata 0 0
#> Chloephaga_rubidiceps 0 0
#> Somateria_spectabilis 0 0
#> Rhynochetos_jubatus 0 0
#> Caprimulgus_longirostris 0 0
#> Caprimulgus_saturatus 0 0
#> Aegotheles_cristatus 0 0
#> Lampornis_clemenciae 0 0
#> Lampornis_calolaemus 0 0
#> Stellula_calliope 0 0
#> Hylocharis_sapphirina 0 0
#> Hylocharis_chrysura 0 0
#> Amazilia_cyanifrons 0 0
#> Campylopterus_phainopeplus 0 0
#> Metallura_iracunda 0 0
#> Anthracothorax_viridis 0 0
#> Polytmus_theresiae 0 0
#> Phaethornis_striigularis 0 0
pred$probabilities
#> list()
pred2 <- predict(fit, n_imputations = 2L)
pred2$imputed_datasets
#> [[1]]
#> Mass Wing.Length
#> Nothoprocta_pentlandii 300.7 148.2
#> Eudromia_formosa 772.9 223.9
#> Rhea_americana 23000.0 604.5
#> Ptilopachus_petrosus 193.0 117.2
#> Bambusicola_fytchii 313.7 142.8
#> Francolinus_psilolaemus 478.3 167.3
#> Meleagris_ocellata 5525.0 362.5
#> Dendragapus_obscurus 1047.3 226.5
#> Tympanuchus_phasianellus 882.4 200.6
#> Tragopan_temminckii 1163.4 236.8
#> Arborophila_campbelli 336.0 129.5
#> Megapodius_nicobariensis 836.7 226.8
#> Dendrocygna_guttata 800.0 206.6
#> Chloephaga_rubidiceps 2086.0 325.6
#> Somateria_spectabilis 1616.7 266.3
#> Rhynochetos_jubatus 860.0 264.3
#> Caprimulgus_longirostris 45.6 151.6
#> Caprimulgus_saturatus 52.0 144.5
#> Aegotheles_cristatus 42.8 135.8
#> Lampornis_clemenciae 7.6 73.7
#> Lampornis_calolaemus 5.1 62.6
#> Stellula_calliope 2.6 41.4
#> Hylocharis_sapphirina 4.4 49.5
#> Hylocharis_chrysura 4.5 51.8
#> Amazilia_cyanifrons 5.0 55.6
#> Campylopterus_phainopeplus 7.7 68.1
#> Metallura_iracunda 3.9 58.1
#> Anthracothorax_viridis 6.6 62.8
#> Polytmus_theresiae 3.8 56.6
#> Phaethornis_striigularis 3.0 39.1
#>
#> [[2]]
#> Mass Wing.Length
#> Nothoprocta_pentlandii 300.7 148.2
#> Eudromia_formosa 772.9 223.9
#> Rhea_americana 23000.0 604.5
#> Ptilopachus_petrosus 193.0 117.2
#> Bambusicola_fytchii 313.7 142.8
#> Francolinus_psilolaemus 478.3 167.3
#> Meleagris_ocellata 5525.0 362.5
#> Dendragapus_obscurus 1047.3 226.5
#> Tympanuchus_phasianellus 882.4 200.6
#> Tragopan_temminckii 1163.4 236.8
#> Arborophila_campbelli 336.0 129.5
#> Megapodius_nicobariensis 836.7 226.8
#> Dendrocygna_guttata 800.0 206.6
#> Chloephaga_rubidiceps 2086.0 325.6
#> Somateria_spectabilis 1616.7 266.3
#> Rhynochetos_jubatus 860.0 264.3
#> Caprimulgus_longirostris 45.6 151.6
#> Caprimulgus_saturatus 52.0 144.5
#> Aegotheles_cristatus 42.8 135.8
#> Lampornis_clemenciae 7.6 73.7
#> Lampornis_calolaemus 5.1 62.6
#> Stellula_calliope 2.6 41.4
#> Hylocharis_sapphirina 4.4 49.5
#> Hylocharis_chrysura 4.5 51.8
#> Amazilia_cyanifrons 5.0 55.6
#> Campylopterus_phainopeplus 7.7 68.1
#> Metallura_iracunda 3.9 58.1
#> Anthracothorax_viridis 6.6 62.8
#> Polytmus_theresiae 3.8 56.6
#> Phaethornis_striigularis 3.0 39.1
#>
# }