///|
pub(all) struct CrossValidation {
predictions : Array[Double]
aggregate : Evaluation
folds : Array[Evaluation]
} derive(Debug)
///|
/// No shuffle, hidden random state or fold selection. Every row is validated once.
pub fn cross_validate(
samples : Array[Sample],
assignments : Array[Int],
fold_count : Int,
loss? : Loss = Squared,
direction? : Direction = Increasing,
) -> Result[CrossValidation, String] {
match validate_samples(samples) {
Err(e) => return Err(e)
Ok(_) => ()
}
if assignments.length() != samples.length() {
return Err("fold assignment length mismatch")
}
if fold_count < 2 || fold_count > samples.length() || fold_count > 1000 {
return Err("invalid fold count")
}
let counts = Array::make(fold_count, 0)
for f in assignments {
if f < 0 || f >= fold_count {
return Err("fold index outside range")
}
counts[f] = counts[f] + 1
}
for count in counts {
if count == 0 {
return Err("empty validation fold")
}
}
let predictions = Array::make(samples.length(), 0.0)
let folds = []
for f = 0; f < fold_count; f = f + 1 {
let training_indices = []
let validation_indices = []
for i = 0; i < samples.length(); i = i + 1 {
if assignments[i] == f {
validation_indices.push(i)
} else {
training_indices.push(i)
}
}
let result = match
holdout(samples, training_indices, validation_indices, loss~, direction~) {
Err(e) => return Err(e)
Ok(v) => v
}
folds.push(result.validation)
for j = 0; j < validation_indices.length(); j = j + 1 {
predictions[validation_indices[j]] = result.predictions[j]
}
}
let aggregate = match
evaluate_predictions(
samples.map(fn(s) { s.y }),
predictions,
samples.map(fn(s) { s.weight }),
loss~,
) {
Err(e) => return Err(e)
Ok(v) => v
}
Ok({ predictions, aggregate, folds, })
}
///|
/// Caller-controlled chronological block folds, useful when random mixing is undesirable.
pub fn contiguous_folds(
size : Int,
fold_count : Int,
) -> Result[Array[Int], String] {
if size <= 0 || fold_count < 2 || fold_count > size || size > 1000000 {
return Err("invalid fold dimensions")
}
let base = size / fold_count
let extra = size % fold_count
let assignments = []
for f = 0; f < fold_count; f = f + 1 {
let count = base + (if f < extra { 1 } else { 0 })
for _i = 0; _i < count; _i = _i + 1 {
assignments.push(f)
}
}
Ok(assignments)
}
///|
#deprecated
pub extend CrossValidation with @debug.Debug::{to_repr}