Module Modelkit.Stratified_group_k_fold

Greedy class balancing while keeping every group intact.

type params = {
  1. folds : int;
  2. shuffle : bool;
}
type t
val create : ?folds:int -> ?shuffle:bool -> unit -> (t, Error.t) result

Defaults to five folds without shuffling. Requires at least two folds, aligned classification labels and groups, and at least as many distinct groups as folds. Every fold is nonempty, every row is tested exactly once, and a group never crosses training/test boundaries within a fold.

Groups are considered in descending standard deviation of their class counts. Ties use ascending group IDs, or seeded shuffled order when shuffle=true; unequal dispersions retain their ordering. Classes are accumulated in ascending label order. Each group minimizes the mean across classes of the standard deviation of per-fold fractions of that class. Objectives within 1e-12 tie on fewer rows already allocated to the fold, then the lowest fold index. Remaining groups fill empty folds when necessary to guarantee nonempty partitions. Output row views retain source order.

Class balance is a heuristic, not an optimum or a guarantee of class coverage. Classes confined to too few groups may be absent from training or test partitions. This remains a valid split; estimator/scorer requirements are checked downstream. RNG behavior is portable but does not reproduce NumPy streams. Sparse group histograms use space linear in the observed group/class pairs, plus folds times class count; returned row indices require space proportional to folds times row count.

include SPLITTER with type t := t and type params := params and type target = Target.classification Target.t and type rng = Rng.t
include SPECIFICATION with type t := t with type params := params
val clone : t -> t
val params : t -> params
type rng = Rng.t
val split : t -> rng:rng -> ?groups:Groups.t -> x:Matrix.t -> y:target option -> unit -> ((Row_view.t * Row_view.t) array, Error.t) result