Use this workflow to answer one question: do predicted
missing trait values change across plausible trees? A posterior
tree sample is a set of trees sampled to represent uncertainty about the
evolutionary relationships among species. The worked example compares
one predicted value per tree; you can request multiple draws within each
tree with m_per_tree.
Treat the spread across trees as a prediction-sensitivity diagnostic. pigauto does not pass these results into regression-fitting or pooling tools. The spread tells you how predictions change across trees; it does not measure uncertainty in a downstream effect estimate, standard error, confidence interval, or p-value. Do not report it as any of those quantities.
These tree comparisons have not been tested as a way to quantify uncertainty in downstream effects. The separate validation results describe other inferential settings and their limits.
Use this article if you already have several plausible trees from your phylogenetic analysis (for example, from BEAST or MrBayes) and want to see whether predictions for missing values change across them. BirdTree.org also provides sets of trees.
pigauto uses the tree or trees you provide. It does not download tree
data while imputing. For the 100-tree BirdTree sample available through
the optional megatrees package, install the package and
request the sample explicitly:
An uncached request downloads the release asset through
megatrees and needs internet access. For a species-specific
sample, use the BirdTree subset
tool, then read the downloaded Newick file with
read_tree(). The subset tool asks for an email address and
job ID to check when the request is ready. Cite Jetz et al. (2012) for
the tree and BirdTree.org when you use its website. Record the
megatrees version and preserve the tree object with your
analysis. For a single tree, pass the object to impute();
pass a posterior sample to multi_impute_trees() for the
prediction-sensitivity workflow described below.
Follow these steps:
Check species labels before masking. By default,
multi_impute_trees() uses one reference tree, so the
supplied trees should share a tip set. This set may include tips without
rows in df; pigauto represents them internally with
all-missing trait rows. Species in df outside the trees are
not imputed for those trees. The code below scores only species present
in every tree. If your trees have different tip sets, set
share_gnn = FALSE; the example then still scores only
species shared by all trees.
Choose measured trait values to test. Save their original values, then temporarily replace those same cells with missing values before imputation. Keep the saved values separate and score predictions only for the cells you deliberately hid.
Run multi_impute_trees() with your posterior tree
sample. Here, gnn = FALSE turns off the optional neural
network, so predictions use the phylogeny-based baseline.
m_per_tree = 1 returns one completed dataset per tree. This
lets you compare predictions across trees, but it does not measure
variation among repeated predictions within one tree. See the function help to request
multiple draws per tree.
Compare predictions for the hidden cells with their saved measurements. Report prediction error against those known values separately from the spread of predictions across trees.
The two summaries answer different questions:
The bundled trees300 object contains 50 trees sampled
from the 100-tree BirdTree collection retrieved by
megatrees::get_tree_bird_n100() and pruned to the 300
AVONET species. That collection includes trees built with either the
Ericson or Hackett backbone; the original studies are Ericson et
al. (2006) and Hackett et al. (2008). Jetz et al. (2012) describes the
BirdTree distributions. Each of the 50 trees is an individual sample
from the collection. A maximum-clade-credibility tree is one summary of
that collection.
The default gnn = FALSE fits no network and recomputes
the baseline across posterior trees. If you explicitly set
gnn = TRUE, share_gnn = TRUE (the default)
trains one GNN on a reference tree and shares it across trees. The
returned datasets are for prediction-sensitivity checks. Do not use them
for downstream inference or pass them to with_imputations()
or pool_mi(); those functions reject this diagnostic
workflow.
The example hides ten measured Mass values and compares predictions
only for those cells. In a verified run with seed = 2026,
all ten hidden values received predictions. Their RMSE was 35.3 and
their MAE was 15.9 in the original Mass units. These are example outputs
from ten selected species, not a precise estimate of prediction
accuracy. For an accuracy assessment, use a pre-specified held-out set
sized for your question and report how many held-out species received
predictions. Imputation preserves observed Mass values, so unchanged
observed values cannot show sensitivity to tree choice.
The two summaries can differ for the same species. For example, one species in this run had a large prediction error (about 109 Mass units) and a tree-to-tree SD of 85 units. The first number compares the mean prediction with the hidden measurement; the second describes how much its predictions varied across the 50 supplied trees. Neither number measures uncertainty in a downstream regression result.
To adapt the example, make a copy of your data and follow these steps. This worked example uses one row per species:
data(avonet300, trees300) with code that loads your data
and tree list, or remove the line if those objects are already loaded.
Change df <- avonet300 to name your data frame, and
replace trees300 in the later stages with the name of your
tree list.Species_Key with your column name. These lines move the
species names to row names and remove that ID column from the traits. If
species names are already row names, skip those two lines. Data and tree
tip sets do not have to be identical. Pigauto aligns rows by species
name, not by row order.Mass. For
another numeric trait, replace every Mass reference in
Stages 1 and 3 with its column name and update the displayed trait label
when interpreting results. Binary and categorical traits need different
prediction scores.mass_truth before replacing those cells with
NA. The example selects 10 values; change
n_test to the number you want to test, up to the number of
observed values for species present in every tree. The code stops if too
few values are available. Other missing values stay missing and are not
included in the error calculation.The complete example was run with all 50 bundled trees; it took about 67 seconds in the recorded environment. Run the stages in order so later stages can use the objects created earlier. Your run time will vary with hardware and package versions.
library(pigauto)
data(avonet300, trees300)
# Work on a copy; keep the original measurements unchanged.
df <- avonet300
# Move the species-name column to row names. Skip these two lines if your
# species names are already the row names of your data frame.
rownames(df) <- df$Species_Key
df$Species_Key <- NULL
# Set FALSE for trees with different tip sets. The default shared-reference
# route below uses a simple common-tip-set check.
share_gnn <- TRUE
if (share_gnn) {
tip_names <- trees300[[1L]]$tip.label
common_tip_set <- vapply(trees300, function(tree) {
setequal(tree$tip.label, tip_names)
}, logical(1))
stopifnot(all(common_tip_set))
}
# Score only species represented in every tree; extra tree-only tips are okay.
common_species <- Reduce(intersect, lapply(trees300, function(tree) {
tree$tip.label
}))
scoreable_rows <- which(!is.na(df$Mass) & rownames(df) %in% common_species)
n_test <- 10L
if (length(scoreable_rows) < n_test) {
stop("Choose n_test no larger than the number of observed values for ",
"species present in every tree.")
}
set.seed(2026L)
# Select measured values so predictions can be checked against their truth.
target_rows <- scoreable_rows[sample.int(length(scoreable_rows), n_test)]
mass_truth <- df$Mass[target_rows]
# Hide only these selected cells. Naturally missing values stay missing and
# cannot be scored because their true values are unknown.
df$Mass[target_rows] <- NA_real_The spread across trees does not tell you how uncertain a later statistical result is. It is not a standard error or confidence interval for a regression effect, variance component, correlation, predicted species effect (BLUP or conditional mode), or latent loading. Applying Rubin’s rules to these completed datasets does not establish valid uncertainty for those results. Assessing bias or confidence-interval coverage in a downstream workflow requires tests on simulated data with known answers.
Across 44 registered downstream-inference results, none met the support criteria: 12 failed and 32 were too imprecise to classify. Some complete-data controls also failed. These results describe the tested settings; they do not show that every future analysis will fail.
The separate tree checks compared predictions with trait values held out of fitting, using root mean square error (RMSE). They did not test bias or whether confidence intervals for downstream effects achieved their stated coverage. See the validation results for the tested regimes and their limits. Do not use the spread to claim that a downstream analysis accounts reliably for tree uncertainty.