## ----setup, include=FALSE-----------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  message = FALSE,
  warning = FALSE,
  eval = TRUE
)

package_root <- if (file.exists("../DESCRIPTION")) ".." else "."
if (requireNamespace("pkgload", quietly = TRUE) &&
    file.exists(file.path(package_root, "DESCRIPTION"))) {
  pkgload::load_all(package_root, export_all = FALSE, helpers = FALSE, quiet = TRUE)
} else {
  library(splitGraph)
}

## ----build-spec---------------------------------------------------------------
meta <- data.frame(
  sample_id    = c("S1", "S2", "S3", "S4", "S5", "S6"),
  subject_id   = c("P1", "P1", "P2", "P2", "P3", "P3"),
  batch_id     = c("B1", "B2", "B1", "B2", "B1", "B2"),
  timepoint_id = c("T0", "T1", "T0", "T1", "T0", "T1"),
  time_index   = c(0, 1, 0, 1, 0, 1),
  outcome_id   = c("ctrl", "case", "ctrl", "case", "case", "ctrl"),
  stringsAsFactors = FALSE
)

g <- graph_from_metadata(meta, graph_name = "cookbook")
subject_constraint <- derive_split_constraints(g, mode = "subject")
spec <- as_split_spec(subject_constraint, graph = g)
spec

## ----show-sample-data---------------------------------------------------------
as.data.frame(spec)[, c("sample_id", "group_id", "batch_group", "order_rank")]

## ----adapter-base-r-----------------------------------------------------------
logo_folds <- function(spec, observation_data, sample_id_col = "sample_id") {
  stopifnot(inherits(spec, "split_spec"))
  if (!sample_id_col %in% names(observation_data)) {
    stop("`observation_data` must contain a `", sample_id_col, "` column.")
  }

  joined <- merge(
    observation_data,
    spec$sample_data[, c("sample_id", spec$group_var)],
    by.x = sample_id_col, by.y = "sample_id", sort = FALSE
  )
  joined$.row <- seq_len(nrow(joined))
  groups <- split(joined$.row, joined[[spec$group_var]])

  lapply(names(groups), function(g) {
    list(
      group   = g,
      train   = unlist(groups[setdiff(names(groups), g)], use.names = FALSE),
      assess  = groups[[g]]
    )
  })
}

# Pretend we have an observation frame keyed by sample_id.
obs <- data.frame(
  sample_id = meta$sample_id,
  x = rnorm(nrow(meta)),
  y = rbinom(nrow(meta), 1, 0.5)
)

folds <- logo_folds(spec, obs)
length(folds)
folds[[1]]

## ----block-vars---------------------------------------------------------------
spec$block_vars
head(spec$sample_data[, c("sample_id", spec$group_var, spec$block_vars)])

## ----block-audit--------------------------------------------------------------
block <- spec$block_vars[[1]]
block_of <- setNames(spec$sample_data[[block]], spec$sample_data$sample_id)

do.call(rbind, lapply(folds, function(f) {
  data.frame(
    held_out_group    = f$group,
    straddling_batches = paste(
      intersect(block_of[obs$sample_id[f$train]],
                block_of[obs$sample_id[f$assess]]),
      collapse = ", "
    )
  )
}))

## ----adapter-rsample-group, eval=FALSE----------------------------------------
# spec_to_group_vfold <- function(spec, observation_data,
#                                 v = NULL,
#                                 sample_id_col = "sample_id") {
#   stopifnot(inherits(spec, "split_spec"))
#   if (!requireNamespace("rsample", quietly = TRUE)) {
#     stop("Install rsample to use this adapter.")
#   }
# 
#   joined <- merge(
#     observation_data,
#     spec$sample_data[, c("sample_id", spec$group_var)],
#     by.x = sample_id_col, by.y = "sample_id", sort = FALSE
#   )
# 
#   n_groups <- length(unique(joined[[spec$group_var]]))
#   if (is.null(v)) v <- n_groups
# 
#   rsample::group_vfold_cv(
#     data  = joined,
#     group = !!spec$group_var,
#     v     = v
#   )
# }

## ----adapter-rsample-rolling, eval=FALSE--------------------------------------
# spec_to_rolling_origin <- function(spec, observation_data,
#                                    sample_id_col = "sample_id",
#                                    initial = NULL,
#                                    assess = 1L) {
#   stopifnot(inherits(spec, "split_spec"))
#   if (is.null(spec$time_var)) {
#     stop("This split_spec has no `time_var`; ordered evaluation is not available.")
#   }
#   if (!requireNamespace("rsample", quietly = TRUE)) {
#     stop("Install rsample to use this adapter.")
#   }
# 
#   joined <- merge(
#     observation_data,
#     spec$sample_data[, c("sample_id", spec$time_var)],
#     by.x = sample_id_col, by.y = "sample_id", sort = FALSE
#   )
#   ordered <- joined[order(joined[[spec$time_var]]), , drop = FALSE]
# 
#   if (is.null(initial)) initial <- max(1L, floor(nrow(ordered) * 0.6))
#   rsample::rolling_origin(ordered, initial = initial, assess = assess)
# }

## ----serialize, eval = requireNamespace("jsonlite", quietly = TRUE)-----------
tmp <- tempfile(fileext = ".json")
write_split_spec(spec, tmp)

# The file opens with its $schema reference and schema_version.
cat(readLines(tmp, n = 5), sep = "\n")

# Validate the file against the shipped JSON Schema, then read it back exactly.
validate_split_spec_json(tmp)$valid
spec2 <- read_split_spec(tmp)
identical(spec$sample_data$group_id, spec2$sample_data$group_id)

unlink(tmp)

## ----xlang-pointer, eval = FALSE----------------------------------------------
# vignette("cross-language-handoff", package = "splitGraph")

## ----dispatch-----------------------------------------------------------------
recommend_adapter <- function(spec) {
  switch(
    spec$recommended_resampling,
    grouped_cv          = "group_vfold_cv (group = group_id)",
    blocked_cv          = "group_vfold_cv (group = group_id)",
    custom_grouped_cv   = "group_vfold_cv (group = group_id)",
    leave_one_group_out = "leave-one-group-out over group_id",
    ordered_split       = "rolling_origin (order by order_rank)",
    "group_vfold_cv (default)"
  )
}

# The subject spec recommends grouped CV; a time-mode spec recommends ordering.
recommend_adapter(spec)
time_spec <- as_split_spec(derive_split_constraints(g, mode = "time"), graph = g)
recommend_adapter(time_spec)

