///|
/// Result of a deterministic train/test split.
pub struct SplitResult {
train_indices : Array[Int]
test_indices : Array[Int]
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn SplitResult::train_indices(self : SplitResult) -> Array[Int] {
self.train_indices.copy()
}
///|
pub fn SplitResult::test_indices(self : SplitResult) -> Array[Int] {
self.test_indices.copy()
}
///|
pub fn SplitResult::train_size(self : SplitResult) -> Int {
self.train_indices.length()
}
///|
pub fn SplitResult::test_size(self : SplitResult) -> Int {
self.test_indices.length()
}
///|
/// Splits indices in their original order. This is preferable for time series
/// data where random shuffling would leak future observations.
pub fn chronological_split(size : Int, test_fraction : Double) -> SplitResult {
if size < 0 || test_fraction < 0.0 || test_fraction >= 1.0 {
abort("chronological_split: invalid size or fraction")
}
let test_size = (size.to_double() * test_fraction).ceil().to_int()
let split = size - test_size
let train = []
let test_values : Array[Int] = []
for index in 0.. Array[SplitResult] {
if size <= 0 || folds <= 0 || folds >= size {
abort("walk_forward_folds: invalid size or folds")
}
let result = []
let step = size / folds
for fold in 1..<=folds {
let test_start = fold * step
let test_end = if fold == folds { size } else { (fold + 1) * step }
let train = []
let test_values : Array[Int] = []
for index in 0.. RegressionReport {
if predicted.length() != actual.length() {
abort("RegressionReport: lengths must match")
}
{
count: predicted.length(),
mae: mean_absolute_error(predicted, actual),
rmse: root_mean_squared_error(predicted, actual),
mean_actual: mean(actual),
mean_prediction: mean(predicted),
}
}
///|
pub fn RegressionReport::count(self : RegressionReport) -> Int {
self.count
}
///|
pub fn RegressionReport::mae(self : RegressionReport) -> Double {
self.mae
}
///|
pub fn RegressionReport::rmse(self : RegressionReport) -> Double {
self.rmse
}
///|
pub fn RegressionReport::bias(self : RegressionReport) -> Double {
self.mean_prediction - self.mean_actual
}
///|
pub fn calibration_error(
probabilities : Array[Double],
observed : Array[Bool],
bins : Int,
) -> Double {
if bins <= 0 || probabilities.length() != observed.length() {
abort("calibration_error: invalid inputs")
}
if probabilities.length() == 0 {
return 0.0
}
let counts = Array::make(bins, 0)
let predicted = Array::make(bins, 0.0)
let actual = Array::make(bins, 0.0)
for index in 0.. 1.0 {
abort("calibration_error: probability out of range")
}
let bin = (probabilities[index] * bins.to_double())
.floor()
.to_int()
.min(bins - 1)
counts[bin] = counts[bin] + 1
predicted[bin] = predicted[bin] + probabilities[index]
if observed[index] {
actual[bin] = actual[bin] + 1.0
}
}
let mut error = 0.0
for bin in 0.. 0 {
error = error +
(predicted[bin] - actual[bin]).abs() /
probabilities.length().to_double()
}
}
error
}
///|
pub fn rank_descending(values : Array[Double]) -> Array[Int] {
let indices = []
for index in 0.. values[indices[i]] {
let temporary = indices[i]
indices[i] = indices[j]
indices[j] = temporary
}
}
}
indices
}