## ----setup, include=FALSE-----------------------------------------------------
knitr::opts_chunk$set(
  collapse  = TRUE,
  comment   = "#>",
  fig.width = 6,
  fig.height = 4
)

## ----install, eval=FALSE------------------------------------------------------
# # CRAN release
# install.packages("pigauto")
# 
# # Development version
# pak::pak("itchyshin/pigauto")
# 
# # torch backend (required; ~1 GB first-time download)
# torch::install_torch()

## ----load---------------------------------------------------------------------
library(pigauto)
library(ape)

## ----quickstart, eval=FALSE---------------------------------------------------
# result <- impute(traits, tree)
# 
# result$completed    # data.frame: observed values preserved, NAs filled
# result$imputed_mask # logical matrix: TRUE where a cell was imputed
# result$prediction$se # per-cell uncertainty (original units)

## ----data---------------------------------------------------------------------
data(avonet300, tree300, package = "pigauto")

head(avonet300)
ape::Ntip(tree300)

## ----preprocess---------------------------------------------------------------
traits <- avonet300[, -1]              # drop Species_Key column
rownames(traits) <- avonet300$Species_Key

pd <- preprocess_traits(traits, tree300, log_transform = TRUE)
print(pd)

## ----splits-------------------------------------------------------------------
splits <- make_missing_splits(pd$X_scaled, missing_frac = 0.25, seed = 42)

cat("Total held-out cells:", length(splits$val_idx) + length(splits$test_idx), "\n")
cat("  Validation:", length(splits$val_idx), "\n")
cat("  Test:      ", length(splits$test_idx), "\n")

## ----baseline, eval=TRUE------------------------------------------------------
baseline <- fit_baseline(pd, tree300, splits = splits, model = "BM")

cat("Baseline object contains:\n")
cat("  mu matrix:", nrow(baseline$mu), "x", ncol(baseline$mu), "\n")
cat("  se matrix:", nrow(baseline$se), "x", ncol(baseline$se), "\n")

## ----graph, eval=TRUE---------------------------------------------------------
graph <- build_phylo_graph(
  tree300,
  k_eigen    = 8,
  sigma_mult = 0.5
)

cat("Graph: n =", graph$n, "species\n")
cat("Adjacency: [", nrow(graph$adj), "x", ncol(graph$adj), "]\n")
cat("Spectral coords: [", nrow(graph$coords), "x", ncol(graph$coords), "]\n")
cat("Kernel bandwidth sigma:", round(graph$sigma, 3), "\n")

## ----train, eval=FALSE--------------------------------------------------------
# fit <- fit_pigauto(
#   data            = pd,
#   tree            = tree300,
#   splits          = splits,
#   graph           = graph,
#   baseline        = baseline,
#   hidden_dim      = 64,
#   k_eigen         = 8,
#   dropout         = 0.10,
#   lr              = 3e-3,
#   epochs          = 2000,
#   corruption_rate = 0.55,
#   lambda_shrink   = 0.03,
#   eval_every      = 100,
#   patience        = 10,
#   verbose         = TRUE,
#   seed            = 1
# )

## ----print-fit, eval=FALSE----------------------------------------------------
# print(fit)

## ----predict, eval=FALSE------------------------------------------------------
# pred <- predict(fit, return_se = TRUE)
# 
# # pred$imputed: 300 x 4 matrix in original units
# # pred$se:      300 x 4 uncertainty matrix (original units)
# head(pred$imputed)
# 
# # Conformal prediction intervals (95% marginal coverage guarantee)
# pred$conformal_lower[["Mass"]]    # lower bound per species (original units)
# pred$conformal_upper[["Mass"]]    # upper bound per species (original units)
# pred$conformal_coverage           # empirical coverage on val set (target ≈ 0.95)

## ----evaluate, eval=FALSE-----------------------------------------------------
# # BM baseline RMSE on test cells
# eval_bm <- evaluate_imputation(baseline$mu, pd$X_scaled, splits)
# eval_bm[eval_bm$split == "test", c("trait", "n", "rmse", "pearson_r")]

## ----compare, eval=FALSE------------------------------------------------------
# # GNN test RMSE stored in fit object
# data.frame(
#   trait    = fit$trait_names,
#   bm_rmse  = eval_bm$rmse[eval_bm$split == "test"],
#   gnn_rmse = fit$test_rmse
# )

## ----plot-history, eval=FALSE-------------------------------------------------
# plot(fit, type = "history")

## ----plot-uncertainty, eval=FALSE---------------------------------------------
# plot(pred, type = "intervals", trait = "Mass")

## ----mi-workflow, eval=FALSE--------------------------------------------------
# analysis_data$z_sq <- analysis_data$z^2
# 
# mi <- multi_impute_analysis(
#   data = analysis_data,
#   formula = y ~ x + z,
#   missing = "x",
#   model = "lm",
#   m = 50L,
#   auxiliary = "z_sq",
#   seed = 1L
# )
# 
# fits <- with_imputations(mi, function(d) lm(y ~ x + z, data = d))
# pool_mi(fits)

## ----active-impute, eval=FALSE------------------------------------------------
# res <- impute(traits, tree)
# 
# # Top-10 individual (species, trait) cells, ordered by expected
# # total variance reduction:
# suggest_next_observation(res, top_n = 10, by = "cell")
# 
# # Or aggregated by species (sum of variance reductions across the
# # species' currently-missing continuous-family traits):
# suggest_next_observation(res, top_n = 10, by = "species")

## ----phylo-signal-gate, eval=FALSE--------------------------------------------
# result <- impute(traits, tree,
#                  phylo_signal_gate     = TRUE,   # default
#                  phylo_signal_threshold = 0.2,   # default; min lambda to keep BM/GNN
#                  phylo_signal_method   = "lambda")

## ----phylo-signal-gate-off, eval=FALSE----------------------------------------
# result <- impute(traits, tree, phylo_signal_gate = FALSE)

## ----covariate-helpers, eval=FALSE--------------------------------------------
# # Step 1: GBIF occurrence centroids (median lat/lon across cleaned points)
# gbif <- pull_gbif_centroids(species          = rownames(my_traits),
#                             cache_dir        = "cache/gbif",
#                             occurrence_limit = 500L)
# #> data.frame with columns species, centroid_lat, centroid_lon, n_occurrences
# 
# # Step 2: WorldClim bioclim summaries (median + IQR of all 19 bio variables
# # across each species' GBIF occurrences)
# clim <- pull_worldclim_per_species(species             = rownames(my_traits),
#                                     gbif_cache_dir      = "cache/gbif",
#                                     worldclim_cache_dir = "cache/worldclim",
#                                     resolution          = "10m")
# #> data.frame with bio1..bio19 medians + IQRs + n_extracted per species
# 
# # Step 3: hand the whole climate frame to impute() as covariates
# result <- impute(my_traits, my_tree, covariates = clim)

## ----gpu, eval=FALSE----------------------------------------------------------
# torch::cuda_is_available()       # NVIDIA GPU
# torch::backends_mps_is_available() # Apple Silicon GPU

## ----cache, eval=FALSE--------------------------------------------------------
# graph <- build_phylo_graph(tree300, k_eigen = 8, cache_path = "tree300_graph.rds")

## ----own-data, eval=FALSE-----------------------------------------------------
# tree   <- ape::read.tree("my_phylogeny.nwk")
# traits <- read_traits("my_traits.csv", species_col = "species")
# pd     <- preprocess_traits(traits, tree)
# # ...proceed as above

