Skip to contents
library(pigauto)
data(avonet300, tree300)
set.seed(42)
tree <- ape::keep.tip(tree300, sample(tree300$tip.label, 80))
traits <- avonet300[match(tree$tip.label, avonet300$Species_Key),
                    c("Mass", "Wing.Length", "Trophic.Level",
                      "Primary.Lifestyle"), drop = FALSE]
rownames(traits) <- tree$tip.label
traits$Mass[1:8] <- NA
traits$Wing.Length[6:13] <- NA
traits$Trophic.Level[4:11] <- NA
traits$Primary.Lifestyle[10:17] <- NA
x <- preprocess_traits(traits, tree)

Imagine assembling a comparative dataset of bird body mass, wing length, trophic level and lifestyle. The first two are continuous measurements; the last two are categorical traits. Here we use 80 birds from the bundled AVONET data and deliberately remove some entries to make the example reproducible. The gaps are assigned by position in the tree’s tip order, so they can cluster among relatives. They illustrate the plots; they do not reproduce a known real-world missingness process. Start with the missingness plots to decide whether the available measurements are distributed across the tree or concentrated in particular groups.

Before fitting, compare missing fractions and inspect the observation-by-trait matrix. Labels include trait types; each expanded categorical trait is represented once.

plot_missingness(x, "summary")

plot_missingness(x, "matrix")

The tree-ordered matrix places related tips close together, helping reveal clades with more missing data. The co-missingness map reports the fraction of all observations missing each pair of traits, including the marginal fraction on its diagonal. Such patterns guide investigation; neither plot establishes the missingness mechanism or proves data are missing completely at random.

plot_missingness(x, "phylo", tree)

plot_missingness(x, "co_missing")

Inspecting individual predictions

Fit the mixed traits together, then inspect the predicted mass and its conformal bounds for a few birds whose mass we removed. The bounds are on the original mass scale, so they can be read alongside the prediction. They give an exploratory sense of the range of plausible values for an individual missing measurement, which can help decide which values need closer scrutiny. The same dataset also contains categorical traits, but these numeric conformal bounds are not categorical confidence intervals. Here the true mass is known because we removed it ourselves; in a real application it would need to be measured independently to check an individual prediction.

result <- impute(traits, tree, gnn = FALSE, verbose = FALSE, seed = 7)
## Warning: Small validation set for 4 trait(s): Mass (n=6), Wing.Length (n=9),
## Trophic.Level (n=11), Primary.Lifestyle (n=10). 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=6),
## Wing.Length (n=9)): 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.
missing_mass <- which(is.na(traits$Mass))
mass_predictions <- data.frame(
  species = rownames(traits)[missing_mass],
  predicted_mass_g = result$prediction$imputed[missing_mass, "Mass"],
  lower_g = result$prediction$conformal_lower[missing_mass, "Mass"],
  upper_g = result$prediction$conformal_upper[missing_mass, "Mass"],
  hidden_mass_g = avonet300$Mass[match(rownames(traits)[missing_mass],
                                       avonet300$Species_Key)],
  row.names = NULL
)
mass_predictions
##                    species predicted_mass_g   lower_g   upper_g hidden_mass_g
## 1           Rhea_americana       1529.05331 442.31031 5285.8909       23000.0
## 2     Ptilopachus_petrosus         54.45561  15.75241  188.2514         193.0
## 3      Bambusicola_fytchii         81.56036  23.59302  281.9517         313.7
## 4 Tympanuchus_phasianellus        163.41141  47.27013  564.9083         882.4
## 5      Dendrocygna_guttata        173.56145  50.20624  599.9967         800.0
## 6      Rhynochetos_jubatus        180.04049  52.08044  622.3945         860.0
## 7     Lampornis_clemenciae        139.26004  40.28385  481.4177           7.6
## 8      Hylocharis_chrysura        139.26004  40.28385  481.4177           4.5

Plot just the missing masses, using a log scale because bird masses span a wide range. The dot is the point prediction, the line its exploratory conformal interval, and the cross the measurement we deliberately hid. In an actual dataset the cross would be unavailable for a genuinely missing value. An especially broad interval, or a hidden value outside its interval, prompts closer investigation rather than unquestioning reliance on the imputation.

ggplot2::ggplot(mass_predictions,
  ggplot2::aes(x = predicted_mass_g, y = species,
               xmin = lower_g, xmax = upper_g)) +
  ggplot2::geom_pointrange(orientation = "y") +
  ggplot2::geom_point(ggplot2::aes(x = hidden_mass_g),
                      shape = 4, colour = "#B33A3A", size = 2.5) +
  ggplot2::scale_x_log10() +
  ggplot2::labs(x = "Predicted body mass (g; log scale)", y = NULL,
                title = "Missing body masses: predictions and exploratory intervals") +
  ggplot2::theme_minimal()

In this particular example, none of the eight hidden masses falls inside its interval. The greater rhea’s hidden mass is 23,000 g, while its predicted mass is about 1,529 g. The eight were chosen by tree position and include very large and very small birds: the rhea is larger than every remaining observed bird, and one hummingbird is smaller than all of them. Their prediction errors are large relative to the calibration errors. These eight selected gaps are not an independent random sample, so their 0/8 coverage is not an estimate of general performance. The red crosses are possible here only because we started with recorded values and hid them ourselves.

The fit also warns that only six mass observations were available to calibrate the interval score. That is too few to support the usual finite-sample 95% split-conformal guarantee with a finite empirical bound, even before considering whether the calibration and missing species are exchangeable. We show the warning deliberately: inspect it when interpreting your own fit.

These are prediction intervals for missing trait values, not confidence intervals for a regression coefficient. Their nominal 95% coverage is not certified for this dataset. Standard conformal coverage depends on calibration and future cases being exchangeable; related species, clade-structured missingness and differences between observed and missing species can make that assumption questionable. A small calibration set also makes the bounds unstable, and wide bounds can cover values while offering little practical precision. Examine held-out coverage and interval width, using the number of test values and species- or clade-level holdouts relevant to your question. The single fit above does not establish 95% coverage.

For a categorical trait, inspect the predicted class separately. Here are the birds with an artificially missing trophic level. These labels are point predictions; a numeric mass interval does not quantify their class uncertainty.

missing_diet <- which(is.na(traits$Trophic.Level))
data.frame(
  species = rownames(traits)[missing_diet],
  predicted_trophic_level = result$prediction$imputed[missing_diet, "Trophic.Level"],
  hidden_trophic_level = avonet300$Trophic.Level[
    match(rownames(traits)[missing_diet], avonet300$Species_Key)],
  row.names = NULL
)
##                    species predicted_trophic_level hidden_trophic_level
## 1 Tympanuchus_phasianellus               Herbivore            Herbivore
## 2      Dendrocygna_guttata               Herbivore             Omnivore
## 3      Rhynochetos_jubatus               Carnivore            Carnivore
## 4     Lampornis_clemenciae               Carnivore            Herbivore
## 5      Hylocharis_chrysura               Carnivore            Herbivore
## 6       Tachymarptis_melba               Carnivore            Carnivore
## 7     Telacanthura_ussheri               Carnivore            Carnivore
## 8       Geotrygon_linearis               Herbivore            Herbivore

The hidden labels let us see individual mistakes in this demonstration. In practice, use a separate held-out evaluation to assess accuracy and compare it with a simple baseline; do not read these eight examples as a performance study.

Conformal scores are quantiles of held-out errors on an encoded trait scale; they help construct bounds but do not measure prediction accuracy or establish coverage on their own. Conformal-based multiple-imputation draws are for prediction diagnostics, not Rubin pooling. For downstream estimates and confidence intervals, use a supported analysis-aware or posterior multiple- imputation route, and check its documented scope and validation limitations. Eligibility for pooling does not itself establish reliable inference for your analysis; see the validation results.

Checking prediction accuracy

Use held-out observed values to assess point predictions. For example:

evaluate(result$fit)
##      method             trait        type                metric     value
## 1   pigauto              Mass  continuous                  rmse 0.6013815
## 2   pigauto              Mass  continuous             pearson_r 0.9728627
## 3   pigauto              Mass  continuous                   mae 0.4897527
## 4   pigauto              Mass  continuous conformal_coverage_95 0.8461538
## 5   pigauto       Wing.Length  continuous                  rmse 0.2880914
## 6   pigauto       Wing.Length  continuous             pearson_r 0.9465866
## 7   pigauto       Wing.Length  continuous                   mae 0.2573158
## 8   pigauto       Wing.Length  continuous conformal_coverage_95 1.0000000
## 9   pigauto     Trophic.Level categorical              accuracy 0.5000000
## 10  pigauto     Trophic.Level categorical    accuracy_Carnivore 1.0000000
## 11  pigauto     Trophic.Level categorical    accuracy_Herbivore 0.0000000
## 12  pigauto     Trophic.Level categorical     accuracy_Omnivore 0.0000000
## 13  pigauto Primary.Lifestyle categorical              accuracy 0.7500000
## 14  pigauto Primary.Lifestyle categorical       accuracy_Aerial 0.0000000
## 15  pigauto Primary.Lifestyle categorical  accuracy_Insessorial 1.0000000
## 16  pigauto Primary.Lifestyle categorical  accuracy_Terrestrial 0.0000000
## 17 baseline              Mass  continuous                  rmse 0.6013815
## 18 baseline              Mass  continuous             pearson_r 0.9728627
## 19 baseline              Mass  continuous                   mae 0.4897527
## 20 baseline       Wing.Length  continuous                  rmse 0.2880914
## 21 baseline       Wing.Length  continuous             pearson_r 0.9465866
## 22 baseline       Wing.Length  continuous                   mae 0.2573158
## 23 baseline     Trophic.Level categorical              accuracy 0.5000000
## 24 baseline     Trophic.Level categorical    accuracy_Carnivore 1.0000000
## 25 baseline     Trophic.Level categorical    accuracy_Herbivore 0.0000000
## 26 baseline     Trophic.Level categorical     accuracy_Omnivore 0.0000000
## 27 baseline Primary.Lifestyle categorical              accuracy 0.7500000
## 28 baseline Primary.Lifestyle categorical       accuracy_Aerial 0.0000000
## 29 baseline Primary.Lifestyle categorical  accuracy_Insessorial 1.0000000
## 30 baseline Primary.Lifestyle categorical  accuracy_Terrestrial 0.0000000
##    n_test
## 1      13
## 2      13
## 3      13
## 4      13
## 5       9
## 6       9
## 7       9
## 8       9
## 9       6
## 10      3
## 11      2
## 12      1
## 13      8
## 14      1
## 15      6
## 16      1
## 17     13
## 18     13
## 19     13
## 20      9
## 21      9
## 22      9
## 23      6
## 24      3
## 25      2
## 26      1
## 27      8
## 28      1
## 29      6
## 30      1

Reading the prediction metrics

The test values are observations that pigauto deliberately hid before fitting. Their true values remain available for checking predictions. They are different from the genuinely missing values, whose prediction errors we cannot measure without collecting new observations. Use evaluate(result$fit) for this check. If you need a prediction object for lower-level scoring, request predict(result$fit, evaluation = TRUE); production predictions may use the observed values you would otherwise be treating as hidden.

Metric Meaning How to read it
RMSE on the standardized latent scale (zRMSE) Square root of the average squared difference between predictions and hidden values, after the trait’s transformation and standardization. Smaller is better; zero means exact agreement on these cells. A value of 0.5 is half a preprocessing standard deviation on this scale, not half an original measurement unit. Squaring gives larger errors more weight.
Accuracy for binary or categorical traits Fraction of hidden class labels predicted correctly. Larger is better; 0.8 means 80% correct. Compare with the majority-class prediction: 80% accuracy is uninformative if predicting the commonest class already achieves 90%.
Correlation How predictions and hidden values vary together. High correlation does not rule out a systematic shift or an incorrect prediction scale. Inspect error measures too.
Conformal score A high quantile of absolute validation errors, used to set prediction-bound width for supported traits. It is measured on the encoded trait scale. A larger value gives wider bounds; it does not by itself demonstrate that those bounds cover missing values at the nominal rate.
Held-out coverage Fraction of hidden test values inside the reported bounds. Read it with the number of test values and interval width. A small test set gives an imprecise coverage estimate; broad bounds can cover many values without being useful.

For continuous, count, ordinal and proportion traits, evaluate() reports RMSE on the encoded scale. Depending on the trait, that scale uses log, log1p, integer ranks or logit before standardization. The HTML report labels this “RMSE (latent scale)”. The standardization makes units explicit but provides no universal good/bad threshold: compare predictions on the same hidden cells with a suitable simple reference and consider the biological cost of an error. Other metrics, such as zero-inflated-count RMSE and compositional errors, have their own scales; see ?evaluate_imputation.

A point prediction and an individual completed-data draw answer different questions. Score the point prediction to assess its error. To assess a predictive distribution, consider coverage together with width or a suitable distributional score. Scoring each stochastic completion adds its draw variation to prediction error; that alone does not establish whether the uncertainty is appropriate. mi$pooled_point is the selected route’s point completion, not generally an average of the returned m datasets.