v2026.10.0
All Bundles
Bundle

Core machine learning types: the seedable Random generator, Matrix2D linear algebra and activations, matrix reference holders, the NeuralNetwork and CSV-backed MatrixReader. Compile with -lib ml.

CrossValidation

Cross-validation utilities for model evaluation

Operations

Evaluate # function

Runs stratified k-fold cross-validation and reports per-fold scores. The model is refitted on each fold's training split, so it is left holding the last fold's fit. Standardization, when asked for, fits the scaler on the TRAINING split only and reapplies it to the test split -- scaling test data by its own statistics leaks the test distribution and flatters any model measured at a fixed false-alarm rate.

function : Evaluate(model:ScoreModel, X:Float[,], y:Bool[], k:Int, seed:Int, max_fpr:Float, standardize:Bool) ~ CrossValResult

Parameters

NameTypeDescription
modelScoreModelanything that fits on Bool labels and scores rows
XFloatrows x features
yBoolone label per row
kIntfolds
seedIntseeds the fold assignment, so a run can be repeated
max_fprFloatthe false-alarm budget recall is measured at
standardizeBoolfit a FeatureScaler on each training split

Return

TypeDescription
CrossValResultper-fold and averaged scores, or Nil on bad input

Evaluate # function

Cross-validation whose folds are stratified by a key of your choosing. The overload above stratifies by the binary label, which keeps the positive rate steady across folds but says nothing about what the positives are made of. When the positive class is a union of rare sub-populations -- attack families, fault types, device models -- a fold can be balanced on the label and still hold none of a sub-population that only has a few dozen members. Pass those category codes as strata and each fold keeps their proportions too. strata is a fold-assignment key only. The model still fits and scores against y, so the metrics are unchanged.

function : Evaluate(model:ScoreModel, X:Float[,], y:Bool[], k:Int, seed:Int, max_fpr:Float, standardize:Bool, strata:Float[]) ~ CrossValResult

Parameters

NameTypeDescription
modelScoreModelanything that fits on Bool labels and scores rows
XFloatrows x features
yBoolone label per row
kIntfolds
seedIntseeds the fold assignment, so a run can be repeated
max_fprFloatthe false-alarm budget recall is measured at
standardizeBoolfit a FeatureScaler on each training split
strataFloatone code per row; folds preserve its distribution

Return

TypeDescription
CrossValResultper-fold scores, averages and pooled out-of-fold scores, or Nil on bad input

Evaluate # function

Cross-validation with standardization on, which is the usual case.

function : Evaluate(model:ScoreModel, X:Float[,], y:Bool[], k:Int, seed:Int, max_fpr:Float) ~ CrossValResult

Parameters

NameTypeDescription
modelScoreModelanything that fits on Bool labels and scores rows
XFloatrows x features
yBoolone label per row
kIntfolds
seedIntseeds the fold assignment
max_fprFloatthe false-alarm budget recall is measured at

Return

TypeDescription
CrossValResultper-fold and averaged scores, or Nil on bad input

KFold # native

Splits data into K folds for cross-validation

function : native : KFold(data:Float[,], k:Int) ~ Vector[]<IntRef>

Parameters

NameTypeDescription
dataFloatinput matrix
kIntnumber of folds

Return

TypeDescription
Vectorarray of K fold indices

Split # function

Builds the train/test matrices for one fold. StratifiedKFold returns row indexes; turning those into matrices is about twenty lines of copying that every experiment otherwise rewrites, and getting it subtly wrong shifts labels against features without any error. Takes ONE fold's row indexes rather than the whole array plus an index: a `Vector[]` parameter compiles inside a library but does not resolve across a library boundary, so the caller passes `folds[i]`.

function : Split(X:Float[,], y:Bool[], test_rows:Vector<IntRef>) ~ FoldSplit

Parameters

NameTypeDescription
XFloatrows x features
yBoolone label per row
test_rowsVector<IntRef>row indexes for the test split, i.e. folds[i]

Return

TypeDescription
FoldSplitthe split, or Nil on bad input

StratifiedKFold # function

Splits row indices into K folds that each keep the class ratio of the whole, shuffled from a caller-supplied seed so the same seed gives the same folds. KFold above shuffles with the global generator and ignores labels. Both hurt on imbalanced data: an unseeded split cannot be reproduced, and an unstratified split lets the rare class land unevenly, so a fold may hold too few positives to measure anything at a low false-positive rate. Intrusion-detection sets are exactly that shape. Classes are taken from the label values as they first appear. Each class is shuffled independently and dealt round-robin across the folds, continuing from where the previous class stopped, so a remainder cannot pile into fold 0.

function : StratifiedKFold(labels:Float[], k:Int, seed:Int) ~ Vector[]<IntRef>

Parameters

NameTypeDescription
labelsFloatone label per row; only the labels are needed to stratify
kIntnumber of folds, at least 2
seedIntshuffle seed; the same seed reproduces the same folds

Return

TypeDescription
Vectork vectors of row indices, or Nil on bad input

Example

folds := CrossValidation->StratifiedKFold(y, 5, 42);
each(f : folds) {
  test_rows := folds[f];
};

ToBool # function

Converts scored labels to the Bool[] the classifiers and Metrics take. TableEncoder hands back Float[] and StratifiedKFold wants that form, but every classifier Fit and every Metrics function takes Bool[]. Without this, each caller keeps both representations alive and writes the same conversion loop.

function : ToBool(labels:Float[]) ~ Bool[]

Parameters

NameTypeDescription
labelsFloatone value per row; anything above 0.5 is the positive class

Return

TypeDescription
Boolone Bool per row, or Nil for Nil input

ToFloat # function

Converts Bool labels to the Float[] StratifiedKFold takes.

function : ToFloat(labels:Bool[]) ~ Float[]

Parameters

NameTypeDescription
labelsBoolone label per row

Return

TypeDescription
Float1.0 for true and 0.0 for false, or Nil for Nil input

TrainTestSplit # native

Splits data into training and test sets

function : native : TrainTestSplit(data:Float[,], test_ratio:Float) ~ FloatMatrixRef[]

Parameters

NameTypeDescription
dataFloatinput matrix
test_ratioFloatpercentage of data for testing (0.0 to 1.0)

Return

TypeDescription
FloatMatrixRefarray with [training_data, test_data]