Preprocessing knnimputer transform - CyrilB1531/lodestar GitHub Wiki

Development build. This page describes main, not a released package. The latest published Lodestar.Preprocessing is 0.2.0 — read its documentation.

Home › Preprocessing › Encoding and imputation

KnnImputer.Transform

Fills every missing value from its nearest donors among the fitted rows.

public double[] Transform(ReadOnlySpan<double> samples)

Parameters — samples is the matrix to fill, row-major, FeatureCount values per row, where a NaN is a missing value.

Returns — a new matrix, OutputFeatureCount values per row; the input is never written to.

Exceptions — ArgumentException when samples holds a partial row or an infinity. ArgumentOutOfRangeException when the number of distances needed exceeds the ceiling below.

Example — the same two gaps, weighted two ways.

using Lodestar.Preprocessing;

double[] rows =
[
    1.0, 2.0, double.NaN,
    3.0, 4.0, 3.0,
    double.NaN, 6.0, 5.0,
    8.0, 8.0, 7.0,
];

KnnImputer uniform = KnnImputer.Fit(
    rows, 3, new KnnImputerOptions { NeighbourCount = 2 });

KnnImputer weighted = KnnImputer.Fit(
    rows, 3, new KnnImputerOptions { NeighbourCount = 2, Weights = NeighbourWeights.Distance });

double byMean = uniform.Transform(rows)[2];     // => 4
double byDistance = Math.Round(weighted.Transform(rows)[2], 4);   // => 3.6667

Ties between donors go to the lower row. The reference breaks them by numpy.argpartition, whose pick varies with the CPU's SIMD tier, so it has no single order to match; the nearest are kept in a bounded heap rather than by sorting every donor per missing cell (#1230). The distances themselves are summed directly, where the reference expands them through BLAS, so two donors a few ulps apart can rank the other way round.

Remarks — the call is refused past 100 million distance terms. The cost is rows × fitted rows × features, and on a Ryzen 7 8700G a hundred million terms over one feature take about 0.35 s and the same count over ten features about 1.6 s (measured 2026-09-23). Past that the call throws rather than running for however long it would take — the same stance MannWhitney takes on its exact table. Split the receiving rows into batches to go further; the fitted donors are the expensive half and they are shared.

A donor is a fitted row that has the missing feature, not simply a near row. The nearest rows are ranked by nan_euclidean distance, and the first NeighbourCount of them that actually carry the feature being filled are the ones averaged — so a gap in a rare feature can draw on donors further away than the nominal neighbours.

A value with no donor at all falls back to the feature's mean over the fitted rows, which is the reference's behaviour. A feature with no value anywhere in the fitted matrix was already dropped at Fit.

Applies to — net10.0, netstandard2.0.

See also — KnnImputer.Fit, NeighbourWeights, KnnImputer.

⚠️ **GitHub.com Fallback** ⚠️