ML - рефактринг, исправление неточностей

This commit is contained in:
Mikhalkovich Stanislav 2026-04-11 20:35:52 +03:00
parent 0f0de28a00
commit b9aa6ae65d
15 changed files with 1964 additions and 784 deletions

View file

@ -15,7 +15,7 @@ internal static class RevisionClass
public const string Major = "3";
public const string Minor = "11";
public const string Build = "1";
public const string Revision = "3801";
public const string Revision = "3804";
public const string MainVersion = Major + "." + Minor;
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;

View file

@ -1,4 +1,4 @@
%MINOR%=11
%REVISION%=3801
%COREVERSION%=1
%REVISION%=3804
%MINOR%=11
%MAJOR%=3

View file

@ -1 +1 @@
3.11.1.3801
3.11.1.3804

View file

@ -1 +1 @@
!define VERSION '3.11.1.3801'
!define VERSION '3.11.1.3804'

View file

@ -2,42 +2,14 @@
interface
uses DataFrameABC;
uses LinearAlgebraML;
/// Кодирует строковые метки классов в целочисленные индексы.
/// Каждому уникальному значению присваивается номер 0,1,2,...
/// Порядок кодирования соответствует порядку первого появления меток.
/// Используется при обучении моделей и визуализации.
function EncodeLabels(labels: array of string): array of integer;
/// Кодирует строковые метки классов в целочисленные индексы.
/// Каждому уникальному значению присваивается номер 0,1,2,...
/// Порядок кодирования соответствует порядку первого появления меток.
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
/// Используется при обучении моделей и визуализации
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
/// Преобразует строковые метки классов в целочисленные индексы
/// с использованием заранее заданного массива classes (mapping).
/// classes должен быть получен из EncodeLabels.
/// Если встречается неизвестная метка выбрасывается исключение.
/// Используется для применения кодирования к тестовым данным (Transform).
function TransformLabels(labels: array of string; classes: array of string): array of integer;
/// Преобразует целочисленные индексы классов обратно в строковые метки.
/// Массив classes задаёт соответствие: classes[i] имя класса с индексом i.
/// Используется для получения текстовых предсказаний моделей.
function DecodeLabels(y: array of integer; classes: array of string): array of string;
/// Возвращает список уникальных меток классов.
/// Порядок соответствует первому появлению значений во входном массиве.
/// Используется для определения множества классов в задаче классификации.
function UniqueLabels(labels: array of string): array of string;
// Здесь пока только методы расширения поэтому секция interface - пуста
implementation
uses MLUtilsABC;
uses DataFrameABC;
uses MLExceptions;
uses LinearAlgebraML;
const
ER_TO_MATRIX_NO_COLUMNS =
@ -55,66 +27,7 @@ const
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
ER_UNKNOWN_CLASS_IN_TRANSFORM =
'Неизвестное значение класса "{0}" при преобразовании меток!!Unknown class value "{0}" in TransformLabels';
ER_LABEL_INDEX_OUT_OF_RANGE =
'Индекс метки {0} вне диапазона [0, {1})!!Label index {0} is out of range [0, {1})';
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
begin
if labels = nil then
ArgumentNullError(ER_ARG_NULL, 'labels');
var classList := new List<string>;
var map := new Dictionary<string, integer>;
// собираем классы в порядке первого появления
for var i := 0 to labels.Length - 1 do
begin
var lbl := labels[i];
if not map.ContainsKey(lbl) then
begin
map[lbl] := classList.Count;
classList.Add(lbl);
end;
end;
// кодируем
var res := new integer[labels.Length];
for var i := 0 to labels.Length - 1 do
res[i] := map[labels[i]];
classes := classList.ToArray;
Result := res;
end;
function TransformLabels(labels: array of string; classes: array of string): array of integer;
begin
if labels = nil then
ArgumentNullError(ER_ARG_NULL, 'labels');
var map := new Dictionary<string, integer>;
for var i := 0 to classes.Length - 1 do
map[classes[i]] := i;
var res := new integer[labels.Length];
for var i := 0 to labels.Length - 1 do
begin
var lbl := labels[i];
if not map.ContainsKey(lbl) then
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, lbl);
res[i] := map[lbl];
end;
Result := res;
end;
function EncodeLabels(labels: array of string): array of integer;
begin
var classes: array of string;
Result := EncodeLabels(labels, classes);
end;
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
begin
@ -162,29 +75,6 @@ begin
end;
end;
// Helper
function EncodeLabelsIntHelper(labels: array of integer): array of integer;
begin
var classList := new List<integer>;
var map := new Dictionary<integer, integer>;
for var i := 0 to labels.Length - 1 do
begin
var lbl := labels[i];
if not map.ContainsKey(lbl) then
begin
map[lbl] := classList.Count;
classList.Add(lbl);
end;
end;
var y := new integer[labels.Length];
for var i := 0 to labels.Length - 1 do
y[i] := map[labels[i]];
Result := y;
end;
/// Кодирует строковые метки классов в целочисленные индексы.
/// Каждому уникальному значению присваивается номер 0,1,2,...
/// Порядок кодирования соответствует порядку первого появления меток.
@ -213,40 +103,15 @@ begin
if Self.GetColumnType(target) = ColumnType.ctInt then
begin
var labels := Self.GetIntColumn(target).ToArray;
Result := EncodeLabelsIntHelper(labels);
var classes: array of integer;
Result := EncodeLabelsInt(labels,classes);
exit;
end;
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
end;
function EncodeLabelsInt(labels: array of integer; var classes: array of integer): array of integer;
begin
if labels = nil then
ArgumentNullError(ER_ARG_NULL, 'labels');
var classList := new List<integer>;
var map := new Dictionary<integer, integer>;
// собираем уникальные значения в порядке первого появления
for var i := 0 to labels.Length - 1 do
begin
var lbl := labels[i];
if not map.ContainsKey(lbl) then
begin
map[lbl] := classList.Count;
classList.Add(lbl);
end;
end;
// кодируем
var res := new integer[labels.Length];
for var i := 0 to labels.Length - 1 do
res[i] := map[labels[i]];
classes := classList.ToArray;
Result := res;
end;
/// Кодирует значения категориального столбца DataFrame в целочисленные индексы.
/// Каждому уникальному значению присваивается номер 0,1,2,...
@ -436,26 +301,6 @@ begin
Result := EncodeLabelsInt(labels, classes);
end;
function DecodeLabels(y: array of integer; classes: array of string): array of string;
begin
var res := new string[y.Length];
for var i := 0 to y.Length - 1 do
begin
var idx := y[i];
if (idx < 0) or (idx >= classes.Length) then
Error(ER_LABEL_INDEX_OUT_OF_RANGE, idx, classes.Length);
res[i] := classes[idx];
end;
Result := res;
end;
function UniqueLabels(labels: array of string): array of string;
begin
Result := labels.Distinct.ToArray;
end;
end.

View file

@ -2073,7 +2073,7 @@ begin
names[i] := info.Name;
types[i] := info.ColType;
// 🔥 берем из старой schema
// берем из старой schema
if (fSchema <> nil) and (i < fSchema.ColumnCount) then
cats[i] := fSchema.CategoricalFlags[i]
else
@ -2970,7 +2970,7 @@ begin
Error(ER_UNKNOWN_COLUMN_TYPE);
end;
// 🔥 КЛЮЧЕВОЕ: перенос schema
// КЛЮЧЕВОЕ: перенос schema
var cats := new boolean[ColumnCount];
for var i := 0 to ColumnCount - 1 do
cats[i] := fSchema.CategoricalFlags[i];
@ -3070,7 +3070,7 @@ begin
end;
end;
// 🔥 КЛЮЧЕВОЕ: пересобираем schema
// КЛЮЧЕВОЕ: пересобираем schema
var n := ColumnCount;
var names := new string[n];
var types := new ColumnType[n];
@ -3622,7 +3622,7 @@ begin
end;
end;
// 🔥 КЛЮЧЕВОЕ: schema НЕ меняется
// КЛЮЧЕВОЕ: schema НЕ меняется
res.SetSchema(fSchema);
Result := res;
@ -3677,7 +3677,7 @@ begin
end;
end;
// 🔥 schema просто копируется
// schema просто копируется
res.SetSchema(fSchema);
Result := res;
@ -3732,7 +3732,7 @@ begin
end;
end;
// 🔥 schema НЕ меняется
// schema НЕ меняется
res.SetSchema(fSchema);
Result := res;
@ -3787,7 +3787,7 @@ begin
end;
end;
// 🔥 schema просто копируется
// schema просто копируется
res.SetSchema(fSchema);
Result := res;
@ -4312,7 +4312,7 @@ begin
res.AddColumnView(col);
end;
// 🔥 пересобираем schema (меняются ТИПЫ)
// пересобираем schema (меняются ТИПЫ)
var n := fSchema.ColumnCount;
var namesArr := new string[n];
@ -4743,7 +4743,7 @@ begin
end;
names.Add(colName);
cats.Add(true); // 🔥 ключи categorical
cats.Add(true); // ключи categorical
end;
res.AddIntColumn('count', counts, nil);
@ -4752,7 +4752,7 @@ begin
cats.Add(false);
end;
// 🔥 устанавливаем schema
// устанавливаем schema
res.SetSchema(new DataFrameSchema(
names.ToArray,
types.ToArray,
@ -5322,7 +5322,7 @@ begin
schemaNames.Add('Feature');
schemaTypes.Add(ctStr);
schemaCats.Add(true); // 🔥 categorical
schemaCats.Add(true); // categorical
// 2️⃣ корреляции
for var j := 0 to n - 1 do
@ -5342,7 +5342,7 @@ begin
schemaCats.Add(false);
end;
// 🔥 schema
// schema
res.SetSchema(new DataFrameSchema(
schemaNames.ToArray,
schemaTypes.ToArray,
@ -6324,7 +6324,7 @@ begin
else
types[j] := ctStr;
// 🔥 вот сюда переносим логику categorical
// вот сюда переносим логику categorical
cats[j] := ((catSet <> nil) and (headers[j] in catSet)) or autoCat[j];
end;

View file

@ -64,7 +64,7 @@ type
ColumnInfo = auto class
Name: string;
ColType: ColumnType;
//IsCategorical: boolean; - мы убрали это отсюда - только Schema - источник истины!
//IsCategorical - только в Schema!
end;
DataFrameCursor = class;
@ -84,15 +84,11 @@ type
function TryGetNumericValue(i: integer; var value: real): boolean; virtual; abstract;
/// Возвращает количество строк в столбце
function RowCount: integer; virtual; abstract;
/// Добавляет невалидное (NA) значение в конец столбца
//procedure AppendInvalid; virtual; abstract;
/// Добавляет значение из курсора в указанной позиции
//procedure AppendFromCursor(cur: DataFrameCursor; colIndex: integer); virtual; abstract;
end;
/// Столбец целых чисел
IntColumn = class(Column)
// ⚠️ Data и IsValid считаются immutable после создания
// Data и IsValid считаются immutable после создания
Data: array of integer; // Данные столбца
IsValid: array of boolean; // Флаги валидности (может быть nil)
public
@ -103,17 +99,6 @@ type
function TryGetNumericValue(i: integer; var value: real): boolean; override;
/// Возвращает количество строк в столбце
function RowCount: integer; override := Data.Length;
/// Добавляет невалидное (NA) значение в конец столбца
/// УСТАРЕВШИЙ МЕТОД.
/// Не должен использоваться в новом коде.
/// Сохраняется только для обратной совместимости.
//procedure AppendInvalid; override;
/// Добавляет значение из курсора в указанной позиции
/// УСТАРЕВШИЙ МЕТОД.
/// Использует неэффективное поэлементное добавление (O()).
/// Не должен использоваться в новом коде.
/// Сохраняется только для обратной совместимости.
//procedure AppendFromCursor(cur: DataFrameCursor; colIndex: integer); override;
end;
/// Столбец вещественных чисел
@ -128,10 +113,6 @@ type
function TryGetNumericValue(i: integer; var value: real): boolean; override;
/// Возвращает количество строк в столбце
function RowCount: integer; override := Data.Length;
/// Добавляет невалидное (NA) значение в конец столбца
//procedure AppendInvalid; override;
/// Добавляет значение из курсора в указанной позиции
//procedure AppendFromCursor(cur: DataFrameCursor; colIndex: integer); override;
end;
/// Столбец строк
@ -164,10 +145,6 @@ type
function TryGetNumericValue(i: integer; var value: real): boolean; override;
/// Возвращает количество строк в столбце
function RowCount: integer; override := Data.Length;
/// Добавляет невалидное (NA) значение в конец столбца
//procedure AppendInvalid; override;
/// Добавляет значение из курсора в указанной позиции
//procedure AppendFromCursor(cur: DataFrameCursor; colIndex: integer); override;
end;
// Accessor типы для курсора

View file

@ -195,6 +195,8 @@ type
function GetRow(i: integer): Vector;
function GetCol(j: integer): Vector;
function TakeRows(indices: array of integer): Matrix;
// ---------- Статические методы ----------
/// Возвращает единичную матрицу размера n
@ -931,7 +933,6 @@ begin
end;
static function Matrix.operator *(A: Matrix; x: Vector): Vector;
begin
CheckVecSize(A, x);
@ -945,7 +946,7 @@ begin
end;
end;
static function Matrix.operator *(A, B: Matrix): Matrix;
{static function Matrix.operator *(A, B: Matrix): Matrix;
begin
CheckMulSize(A, B);
Result := new Matrix(A.RowCount, B.ColCount);
@ -957,6 +958,30 @@ begin
for var j := 0 to B.ColCount - 1 do
Result.fdata[i, j] += aik * B.fdata[k, j];
end;
end;}
static function Matrix.operator *(A, B: Matrix): Matrix;
begin
CheckMulSize(A, B);
var m := A.RowCount;
var p := A.ColCount;
var n := B.ColCount;
var BT := B.Transpose;
Result := new Matrix(m, n);
for var i := 0 to m - 1 do
for var j := 0 to n - 1 do
begin
var sum := 0.0;
for var k := 0 to p - 1 do
sum += A.fdata[i, k] * BT.fdata[j, k];
Result.fdata[i, j] := sum;
end;
end;
static function Matrix.operator +=(A, B: Matrix): Matrix;
@ -1002,6 +1027,61 @@ begin
Result[j] := fdata[i, j];
end;
function Matrix.TakeRows(indices: array of integer): Matrix;
begin
var n := indices.Length;
var p := ColCount;
var res := new Matrix(n, p);
var src := Data;
var dst := res.Data;
// --- fast path: полный срез [0..n-1]
var isFull := n = RowCount;
if isFull then
begin
for var i := 0 to n - 1 do
if indices[i] <> i then
begin
isFull := False;
break;
end;
end;
if isFull then
begin
System.Array.Copy(src, 0, dst, 0, n * p);
Result := res;
exit;
end;
// --- обычный block-copy
var i := 0;
var dstOffset := 0;
while i < n do
begin
var start := indices[i];
var len := 1;
while (i + len < n) and (indices[i + len] = start + len) do
len += 1;
System.Array.Copy(
src, start * p,
dst, dstOffset,
len * p
);
dstOffset += len * p;
i += len;
end;
Result := res;
end;
function Matrix.GetCol(j: integer): Vector;
begin
if (j < 0) or (j >= ColCount) then

View file

@ -125,6 +125,6 @@ begin
Result := MLUtilsABC.LabelsToInts(y);
end;
function EncodeLabels(labels: array of string): array of integer := DataAdapters.EncodeLabels(labels);
function EncodeLabels(labels: array of string): array of integer := MLUtilsABC.EncodeLabels(labels);
end.

File diff suppressed because it is too large Load diff

View file

@ -56,11 +56,6 @@ type
fTask: TaskKind;
fTarget: string;
// fDataSteps: List<IPreprocessor>;
// fMatrixSteps: List<ITransformer>;
// fFeatures: array of string;
// fFinalFeatures: array of string;
// fFitted: boolean;
protected
procedure ValidateSchema(df: DataFrame); override;
public
@ -75,88 +70,6 @@ type
/// Запрещено добавлять шаги после вызова Fit/FitTransform.
function Add(step: IPipelineStep): DataPipeline;
{ // Примеры использования препроцессоров и моделей в DataPipeline.
// Препроцессоры работают на уровне DataFrame.
// После них Pipeline автоматически преобразует данные в Matrix/Vector.
// Далее выполняются матричные трансформеры и модель.
// ------------------------------------------------------------
// Пример 1. Классификация с категориальным целевым признаком
var pipe :=
DataPipeline.Build(
'species',
['length','width'],
new LabelEncoder('species'), // DataFrame-препроцессор (строки числа)
new StandardScaler, // матричный трансформер
new LogisticRegression // модель
);
// ------------------------------------------------------------
// Пример 2. Регрессия с пропущенными значениями и категориальным признаком
var pipe :=
DataPipeline.Build(
'price',
['area','floor','district'],
new Imputer('area'), // DataFrame-препроцессор (заполнение NA)
new OneHotEncoder('district'), // DataFrame-препроцессор
new RandomForestRegressor // модель
);
// ------------------------------------------------------------
// Пример 3. Сложный pipeline
var pipe :=
DataPipeline.Build(
'target',
['f1','f2','f3','category'],
new Imputer('f2'), // DataFrame-препроцессор
new OneHotEncoder('category'), // DataFrame-препроцессор
new StandardScaler, // матричный трансформер
new PCATransformer(2), // матричный трансформер
new GradientBoostingRegressor // модель
);
// ------------------------------------------------------------
// Пример 4. Классификация без Pipeline
var df := Datasets.Flowers;
// --- Encode target (DataFrame уровень)
df := df.SetCategorical(['species']);
var labels := df.EncodeLabels('species');
// --- X, y
var X := df.ToMatrix(['length','width']);
var y := new Vector(labels);
// --- Matrix уровень
var scaler := new StandardScaler;
X := scaler.FitTransform(X);
// --- Модель
var model := new LogisticRegression;
model.Fit(X, y);
// Pipeline.Build используется, когда данные уже представлены
// в виде числовой матрицы признаков X и вектора целевой переменной y.
// В этом случае DataFrame и препроцессоры уровня таблицы не требуются.
//
// Типичные ситуации:
// экспериментирование с ML-алгоритмами
// сравнение моделей
// кросс-валидация
// подбор гиперпараметров
// тестирование моделей
// Пример 5. Pipeline на матричном уровне
var pipe :=
Pipeline.Build(
new StandardScaler,
new PCATransformer(2),
new LogisticRegression
);
}
/// Строит конвейер из шагов обработки данных и модели.
///
/// Используется в задачах с учителем (с target).

View file

@ -18,6 +18,43 @@ function LabelsToInts(y: Vector): array of integer;
/// Преобразует массив целых меток в Vector.
function IntsToLabels(a: array of integer): Vector;
/// Кодирует строковые метки классов в целочисленные индексы.
/// Каждому уникальному значению присваивается номер 0,1,2,...
/// Порядок кодирования соответствует порядку первого появления меток.
/// Используется при обучении моделей и визуализации.
function EncodeLabels(labels: array of string): array of integer;
/// Кодирует строковые метки классов в целочисленные индексы.
/// Каждому уникальному значению присваивается номер 0,1,2,...
/// Порядок кодирования соответствует порядку первого появления меток.
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
/// Используется при обучении моделей и визуализации
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
/// Кодирует целые метки классов в целочисленные индексы.
/// Каждому уникальному значению присваивается номер 0,1,2,...
/// Порядок кодирования соответствует порядку первого появления меток.
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
/// Используется при обучении моделей и визуализации
function EncodeLabelsInt(labels: array of integer; var classes: array of integer): array of integer;
/// Преобразует строковые метки классов в целочисленные индексы
/// с использованием заранее заданного массива classes (mapping).
/// classes должен быть получен из EncodeLabels.
/// Если встречается неизвестная метка выбрасывается исключение.
/// Используется для применения кодирования к тестовым данным (Transform).
function TransformLabels(labels: array of string; classes: array of string): array of integer;
/// Преобразует целочисленные индексы классов обратно в строковые метки.
/// Массив classes задаёт соответствие: classes[i] имя класса с индексом i.
/// Используется для получения текстовых предсказаний моделей.
function DecodeLabels(y: array of integer; classes: array of string): array of string;
/// Возвращает список уникальных меток классов.
/// Порядок соответствует первому появлению значений во входном массиве.
/// Используется для определения множества классов в задаче классификации.
function UniqueLabels(labels: array of string): array of string;
implementation
uses MLExceptions;
@ -27,6 +64,11 @@ const
'y не может быть nil!!y cannot be nil';
ER_LABELS_ARRAY_NULL =
'labels не может быть nil!!labels cannot be nil';
ER_UNKNOWN_CLASS_IN_TRANSFORM =
'Неизвестное значение класса "{0}" при преобразовании меток!!Unknown class value "{0}" in TransformLabels';
ER_LABEL_INDEX_OUT_OF_RANGE =
'Индекс метки {0} вне диапазона [0, {1})!!Label index {0} is out of range [0, {1})';
function LabelsToInts(y: Vector): array of integer;
begin
@ -47,4 +89,112 @@ begin
Result := new Vector(a);
end;
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
begin
if labels = nil then
ArgumentNullError(ER_ARG_NULL, 'labels');
var classList := new List<string>;
var map := new Dictionary<string, integer>;
// собираем классы в порядке первого появления
for var i := 0 to labels.Length - 1 do
begin
var lbl := labels[i];
if not map.ContainsKey(lbl) then
begin
map[lbl] := classList.Count;
classList.Add(lbl);
end;
end;
// кодируем
var res := new integer[labels.Length];
for var i := 0 to labels.Length - 1 do
res[i] := map[labels[i]];
classes := classList.ToArray;
Result := res;
end;
function TransformLabels(labels: array of string; classes: array of string): array of integer;
begin
if labels = nil then
ArgumentNullError(ER_ARG_NULL, 'labels');
var map := new Dictionary<string, integer>;
for var i := 0 to classes.Length - 1 do
map[classes[i]] := i;
var res := new integer[labels.Length];
for var i := 0 to labels.Length - 1 do
begin
var lbl := labels[i];
if not map.ContainsKey(lbl) then
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, lbl);
res[i] := map[lbl];
end;
Result := res;
end;
function EncodeLabels(labels: array of string): array of integer;
begin
var classes: array of string;
Result := EncodeLabels(labels, classes);
end;
function EncodeLabelsInt(labels: array of integer; var classes: array of integer): array of integer;
begin
if labels = nil then
ArgumentNullError(ER_ARG_NULL, 'labels');
var classList := new List<integer>;
var map := new Dictionary<integer, integer>;
// собираем уникальные значения в порядке первого появления
for var i := 0 to labels.Length - 1 do
begin
var lbl := labels[i];
if not map.ContainsKey(lbl) then
begin
map[lbl] := classList.Count;
classList.Add(lbl);
end;
end;
// кодируем
var res := new integer[labels.Length];
for var i := 0 to labels.Length - 1 do
res[i] := map[labels[i]];
classes := classList.ToArray;
Result := res;
end;
function DecodeLabels(y: array of integer; classes: array of string): array of string;
begin
var res := new string[y.Length];
for var i := 0 to y.Length - 1 do
begin
var idx := y[i];
if (idx < 0) or (idx >= classes.Length) then
Error(ER_LABEL_INDEX_OUT_OF_RANGE, idx, classes.Length);
res[i] := classes[idx];
end;
Result := res;
end;
function UniqueLabels(labels: array of string): array of string;
begin
Result := labels.Distinct.ToArray;
end;
end.

File diff suppressed because it is too large Load diff

View file

@ -248,22 +248,50 @@ begin
NotFittedError(ER_FIT_NOT_CALLED);
var idx := df.Schema.IndexOf(col);
var n := df.RowCount;
Result := df.ReplaceColumnInt(
col,
c ->
if not c.IsValid(idx) then
Error(ER_LABELENCODER_NA) // будет поймано как NA
else
var data := new integer[n];
var valid: array of boolean := nil;
var cur := df.GetCursor;
var row := 0;
while cur.MoveNext do
begin
if not cur.IsValid(idx) then
begin
if valid = nil then
begin
var s := c.Str(idx);
if not mapping.ContainsKey(s) then
Error(ER_LABELENCODER_UNSEEN_CATEGORY, s);
Result := mapping[s];
end
);
Result := Result.SetCategorical([col]);
valid := new boolean[n];
for var j := 0 to row - 1 do
valid[j] := true;
end;
valid[row] := false;
data[row] := 0;
end
else
begin
var s := cur.Str(idx);
if not mapping.ContainsKey(s) then
Error(ER_LABELENCODER_UNSEEN_CATEGORY, s);
data[row] := mapping[s];
if valid <> nil then
valid[row] := true;
end;
row += 1;
end;
var res := new DataFrame;
foreach var src in df.GetColumns do
if src.Info.Name <> col then
res.AddColumnView(src)
else
res.AddIntColumn(col, data, valid);
Result := res.SetCategorical([col]);
end;
function LabelEncoder.FitTransform(df: DataFrame): DataFrame;

View file

@ -311,6 +311,19 @@ begin
end;
end;
// --- 1.1 ПРОВЕРКА НА МИНИМАЛЬНЫЙ РАЗМЕР КЛАССА
foreach var pair in classMap do
begin
var cls := pair.Key;
var cnt := pair.Value.Count;
if cnt < k then
ArgumentError(
'StratifiedKFold: класс %d содержит %d объектов, что меньше числа фолдов (%d)',
cls, cnt, k
);
end;
// --- 2. Контейнеры фолдов
var folds := new List<integer>[k];
for var f := 0 to k - 1 do