Почистил примеры

This commit is contained in:
Mikhalkovich Stanislav 2026-05-09 23:00:12 +03:00
parent ce84830349
commit de75f00031
23 changed files with 327 additions and 134 deletions

View file

@ -7,7 +7,7 @@ begin
var features := ['rooms','area','kitchen_area','floor','floors_total','metro_minutes'];
var target := 'price';
var (trainDf, testDf) := df.TrainTestSplit(0.2, 42);
var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 42);
var Xtrain := trainDf.ToMatrix(features);
var ytrain := trainDf.ToVector(target);

View file

@ -7,7 +7,7 @@ begin
var features := ['rooms','area','kitchen_area','floor','floors_total','metro_minutes'];
var target := 'price';
var (trainDf, testDf) := df.TrainTestSplit(0.2, 42);
var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 42);
var Xtrain := trainDf.ToMatrix(features);
var ytrain := trainDf.ToVector(target);

View file

@ -11,9 +11,10 @@ begin
var pipe :=
DataPipeline.Build(
TaskKind.tkRegression,
target,
features,
new LabelEncoder('renovation'),
new OrdinalEncoder('renovation'),
model
);

View file

@ -12,10 +12,10 @@ begin
df := portImputer.FitTransform(df);
// Кодируем категориальные признаки числами.
var sexEncoder := new LabelEncoder('Пол');
var sexEncoder := new OrdinalEncoder('Пол');
df := sexEncoder.FitTransform(df);
var portEncoder := new LabelEncoder('ПортПосадки');
var portEncoder := new OrdinalEncoder('ПортПосадки');
df := portEncoder.FitTransform(df);
var features := ['Класс', 'Пол', 'Возраст', 'БратьяИСупруги', 'РодителиИДети', 'ЦенаБилета', 'ПортПосадки'];

View file

@ -10,10 +10,10 @@ begin
var portImputer := new Imputer('Саутгемптон', ['ПортПосадки']);
df := portImputer.FitTransform(df);
var sexEncoder := new LabelEncoder('Пол');
var sexEncoder := new OrdinalEncoder('Пол');
df := sexEncoder.FitTransform(df);
var portEncoder := new LabelEncoder('ПортПосадки');
var portEncoder := new OrdinalEncoder('ПортПосадки');
df := portEncoder.FitTransform(df);
var features := ['Класс', 'Пол', 'Возраст', 'БратьяИСупруги', 'РодителиИДети', 'ЦенаБилета', 'ПортПосадки'];

View file

@ -11,10 +11,10 @@ begin
df := imputer.FitTransform(df);
// Кодируем категориальные признаки
var encoder := new LabelEncoder('region_name');
var encoder := new OrdinalEncoder('region_name');
df := encoder.FitTransform(df);
var encoder2 := new LabelEncoder('federal_district');
var encoder2 := new OrdinalEncoder('federal_district');
df := encoder2.FitTransform(df);
df.Print;

View file

@ -16,10 +16,10 @@ begin
var imputer := new Imputer(['population', 'lat', 'lon']);
df := imputer.FitTransform(df);
var le1 := new LabelEncoder('region_name');
var le1 := new OrdinalEncoder('region_name');
df := le1.FitTransform(df);
var le2 := new LabelEncoder('federal_district');
var le2 := new OrdinalEncoder('federal_district');
df := le2.FitTransform(df);
// Явно задаём признаки

View file

@ -7,10 +7,10 @@ begin
var imputer := new Imputer(['population', 'lat', 'lon']);
df := imputer.FitTransform(df);
var le1 := new LabelEncoder('region_name');
var le1 := new OrdinalEncoder('region_name');
df := le1.FitTransform(df);
var le2 := new LabelEncoder('federal_district');
var le2 := new OrdinalEncoder('federal_district');
df := le2.FitTransform(df);
var features := ['lat', 'lon', 'region_name', 'federal_district'];

View file

@ -1 +1 @@
3.11.1.3815
3.11.1.3821

View file

@ -1 +1 @@
!define VERSION '3.11.1.3815'
!define VERSION '3.11.1.3821'

View file

@ -6,7 +6,7 @@ begin
df.AddStrColumn('Region', Arr('South', 'North', 'South', 'East'));
df := df.SetCategorical(['Region']);
var enc := new LabelEncoder('Region');
var enc := new OrdinalEncoder('Region');
enc.Fit(df);
var res := enc.Transform(df);

View file

@ -10,7 +10,7 @@ begin
testDf.AddIntColumn('City', Arr(1, 2, 3));
testDf := testDf.SetCategorical(['City']);
var enc := new LabelEncoder('City');
var enc := new OrdinalEncoder('City');
enc.Fit(trainDf);
CheckRaises(procedure -> begin

View file

@ -120,7 +120,7 @@ type
Inspection = InspectionML.Inspection;
IPreprocessor = PreprocessorABC.IPreprocessor;
LabelEncoder = PreprocessorABC.LabelEncoder;
OrdinalEncoder = PreprocessorABC.OrdinalEncoder;
OneHotEncoder = PreprocessorABC.OneHotEncoder;
ImputeStrategy = PreprocessorABC.ImputeStrategy;
Imputer = PreprocessorABC.Imputer;

View file

@ -1157,6 +1157,7 @@ begin
ArgumentNullError(ER_ARG_NULL, 'name');
var baseDir := PascalABCDirectory + 'Files\Datasets\';
//var baseDir := 'C:\Program Files (x86)\PascalABC.NET\Files\Datasets\';
var metaPath := baseDir + name + '.meta';
var csvPath := baseDir + name + '.csv';

View file

@ -25,6 +25,14 @@ unit MLModelsABC;
// См. статистическую политику в модуле MLABC.
// =============================================================
{
Производительность
DecisionTreeRegressor.Fit - 340 мс против 156 мс в Питоне при той же точности
GradientBoostingRegressor.Fit - 5500 мс против 5500 мс в Питоне
RandomForestRegressor.Fit - 1280 мс против 480 мс в Питоне
}
interface
uses MLCoreABC;
@ -432,6 +440,8 @@ type
Found: boolean;
Feature: integer;
Threshold: real;
LeftCount: integer;
LeftOrderSize: integer;
WeightedScore: real;
static function Invalid: RegSplitResult;
@ -439,6 +449,8 @@ type
Result.Found := false;
Result.Feature := -1;
Result.Threshold := 0.0;
Result.LeftCount := 0;
Result.LeftOrderSize := 0;
Result.WeightedScore := real.PositiveInfinity;
end;
end;
@ -708,7 +720,8 @@ type
private
fLeafL2: real;
fSortedOrders: array of array of integer;
fSortedValues: array of array of real;
fUseSortedOrdersAsRoot: boolean;
fRowWeights: array of integer;
fVisitMarks: array of integer;
fVisitId: integer;
@ -726,12 +739,14 @@ type
function FindBestSplitReg(X: Matrix; y: Vector; nodeOrders: array of array of integer): RegSplitResult;
procedure BuildSortedOrders(X: Matrix; indices: array of integer);
function BuildInitialNodeOrders(indices: array of integer): array of array of integer;
procedure SplitNodeOrders(X: Matrix; nodeOrders: array of array of integer; feature: integer; threshold: real;
procedure SplitNodeOrders(nodeOrders: array of array of integer; feature: integer; leftCount, leftOrderSize: integer;
var leftOrders, rightOrders: array of array of integer);
function BuildMembershipMask(rowCount: integer; indices: array of integer): array of boolean;
procedure ComputeNodeStats(yData: array of real; indices: array of integer; var sumAll, sumSqAll: real);
function WeightedVariance(n, leftCount: integer; leftSum, leftSumSq, sumAll, sumSqAll: real): real;
function GetFeatureSubset(p: integer): array of integer;
function SampleWeight(rowIndex: integer): integer;
function TotalWeight(indices: array of integer): integer;
/// Проверяет, является ли узел "чистым".
/// Для регрессии это означает, что все значения y одинаковы
@ -759,6 +774,16 @@ type
/// Возвращает вектор вещественных значений.
function Predict(X: Matrix): Vector; override;
/// Внутренний hook для ансамблей: позволяет переиспользовать
/// уже отсортированные порядки строк по признакам.
/// Обычному пользовательскому коду не нужен.
procedure SetPreSortedOrders(sortedOrders: array of array of integer);
/// Внутренний hook для bootstrap-подвыборок с повторами.
/// Передаются уже готовые сортированные порядки именно для корневого узла.
procedure SetPreSortedRootOrders(sortedOrders: array of array of integer);
procedure SetBootstrapRootOrders(sortedOrders: array of array of integer; rowWeights: array of integer);
/// Копирует только конфигурацию модели (без обученного состояния).
/// Используется для создания независимых экземпляров модели.
function Clone: IModel; override;
@ -4022,7 +4047,7 @@ function DecisionTreeRegressor.BuildTreeNode(X: Matrix; y: Vector;
nodeOrders: array of array of integer; depth: integer): DecisionTreeNode;
begin
var indices := nodeOrders[0];
var n := indices.Length;
var n := TotalWeight(indices);
if (fMaxDepth >= 0) and (depth >= fMaxDepth) then
exit(LeafNode(LeafValue(y, indices)));
@ -4048,12 +4073,13 @@ begin
exit(LeafNode(LeafValue(y, indices)));
var leftOrders, rightOrders: array of array of integer;
SplitNodeOrders(X, nodeOrders, split.Feature, split.Threshold, leftOrders, rightOrders);
SplitNodeOrders(nodeOrders, split.Feature, split.LeftCount, split.LeftOrderSize, leftOrders, rightOrders);
var leftArr := leftOrders[0];
var rightArr := rightOrders[0];
var rightCount := n - split.LeftCount;
if (leftArr.Length < fMinSamplesLeaf) or
(rightArr.Length < fMinSamplesLeaf) then
if (split.LeftCount < fMinSamplesLeaf) or
(rightCount < fMinSamplesLeaf) then
exit(LeafNode(LeafValue(y, indices)));
var delta := parentVar - split.WeightedScore;
@ -4095,7 +4121,7 @@ begin
Result := RegSplitResult.Invalid;
var indices := nodeOrders[0];
var n := indices.Length;
var n := TotalWeight(indices);
if n < 2 then
exit;
@ -4108,6 +4134,11 @@ begin
var bestScore := real.PositiveInfinity;
var bestFeature := -1;
var bestThreshold := 0.0;
var bestLeftCount := 0;
var bestLeftOrderSize := 0;
var invN := 1.0 / n;
var minLeaf := fMinSamplesLeaf;
var maxLeftCount := n - minLeaf;
var features := GetFeatureSubset(X.ColCount);
@ -4117,63 +4148,61 @@ begin
var order := nodeOrders[j];
var orderLen := order.Length;
var leftCount := 0;
var leftSum := 0.0;
var leftSumSq := 0.0;
if orderLen < 2 then
continue;
var prevValue := 0.0;
var firstIncluded := true;
var firstIdx := order[0];
var firstWeight := SampleWeight(firstIdx);
var leftCount := firstWeight;
var leftSum := firstWeight * yData[firstIdx];
var leftSumSq := firstWeight * yData[firstIdx] * yData[firstIdx];
var prevValue := xData[firstIdx, j];
for var i := 0 to orderLen - 1 do
for var i := 1 to orderLen - 1 do
begin
if leftCount > maxLeftCount then
break;
var idx := order[i];
var xCur := xData[idx, j];
var yCur := yData[idx];
if not firstIncluded then
if (leftCount >= minLeaf) and (prevValue <> xCur) then
begin
var rightCount := n - leftCount;
if (leftCount >= fMinSamplesLeaf) and
(rightCount >= fMinSamplesLeaf) and
(prevValue <> xCur) then
begin
var rightCountReal := rightCount;
var leftMean := leftSum / leftCount;
var leftVar := leftSumSq / leftCount - leftMean * leftMean;
var rightSum := sumAll - leftSum;
var rightSumSq := sumSqAll - leftSumSq;
var rightMean := rightSum / rightCount;
var rightVar := rightSumSq / rightCount - rightMean * rightMean;
if leftVar < 0 then
leftVar := 0.0;
if rightVar < 0 then
rightVar := 0.0;
var leftScore := leftSumSq - (leftSum * leftSum) / leftCount;
var rightScore := rightSumSq - (rightSum * rightSum) / rightCount;
var weighted := (leftScore + rightScore) * invN;
var weighted := (leftCount * leftVar + rightCountReal * rightVar) / n;
if weighted < 0 then
weighted := 0.0;
if weighted < bestScore then
begin
bestScore := weighted;
bestFeature := j;
bestThreshold := (prevValue + xCur) * 0.5;
end;
bestLeftCount := leftCount;
bestLeftOrderSize := i;
end;
end;
leftCount += 1;
leftSum += yCur;
leftSumSq += yCur * yCur;
var wCur := SampleWeight(idx);
var yCur := yData[idx];
leftCount += wCur;
leftSum += wCur * yCur;
leftSumSq += wCur * yCur * yCur;
prevValue := xCur;
firstIncluded := false;
end;
end;
Result.Found := bestFeature <> -1;
Result.Feature := bestFeature;
Result.Threshold := bestThreshold;
Result.LeftCount := bestLeftCount;
Result.LeftOrderSize := bestLeftOrderSize;
Result.WeightedScore := bestScore;
end;
@ -4190,14 +4219,103 @@ type
end;
end;
function BuildPreSortedOrders(X: Matrix): array of array of integer;
begin
var p := X.ColCount;
var n := X.RowCount;
var xData := X.Data;
SetLength(Result, p);
for var j := 0 to p - 1 do
begin
var pairs: array of SortPair;
SetLength(pairs, n);
for var i := 0 to n - 1 do
begin
pairs[i].Value := xData[i, j];
pairs[i].Index := i;
end;
System.Array.Sort(pairs, new SortPairComparer);
Result[j] := new integer[n];
for var i := 0 to n - 1 do
Result[j][i] := pairs[i].Index;
end;
end;
function BuildRowCounts(rows: array of integer; rowCount: integer): array of integer;
begin
Result := new integer[rowCount];
for var i := 0 to rows.Length - 1 do
Result[rows[i]] += 1;
end;
function BuildSortedOrdersFromCounts(
fullSortedOrders: array of array of integer;
rowCounts: array of integer
): array of array of integer;
begin
var p := Length(fullSortedOrders);
SetLength(Result, p);
var total := 0;
for var i := 0 to rowCounts.Length - 1 do
total += rowCounts[i];
for var j := 0 to p - 1 do
begin
Result[j] := new integer[total];
var k := 0;
foreach var idx in fullSortedOrders[j] do
for var rep := 1 to rowCounts[idx] do
begin
Result[j][k] := idx;
k += 1;
end;
end;
end;
function BuildUniqueOrdersFromCounts(
fullSortedOrders: array of array of integer;
rowCounts: array of integer
): array of array of integer;
begin
var p := Length(fullSortedOrders);
SetLength(Result, p);
var total := 0;
for var i := 0 to rowCounts.Length - 1 do
if rowCounts[i] > 0 then
total += 1;
for var j := 0 to p - 1 do
begin
Result[j] := new integer[total];
var k := 0;
foreach var idx in fullSortedOrders[j] do
if rowCounts[idx] > 0 then
begin
Result[j][k] := idx;
k += 1;
end;
end;
end;
procedure DecisionTreeRegressor.BuildSortedOrders(X: Matrix; indices: array of integer);
begin
// Обычный путь для отдельного дерева:
// построить сортировку только по реально используемым строкам.
var p := X.ColCount;
var n := indices.Length;
var xData := X.Data;
SetLength(fSortedOrders, p);
SetLength(fSortedValues, p);
for var j := 0 to p - 1 do
begin
@ -4214,14 +4332,10 @@ begin
System.Array.Sort(pairs,new SortPairComparer);
fSortedOrders[j] := new integer[n];
fSortedValues[j] := new real[n];
for var i := 0 to n - 1 do
begin
fSortedValues[j][i] := pairs[i].Value;
fSortedOrders[j][i] := pairs[i].Index;
end;
end;
end;
function DecisionTreeRegressor.BuildInitialNodeOrders(indices: array of integer): array of array of integer;
@ -4252,13 +4366,13 @@ begin
end;
end;
procedure DecisionTreeRegressor.SplitNodeOrders(X: Matrix;
procedure DecisionTreeRegressor.SplitNodeOrders(
nodeOrders: array of array of integer;
feature: integer;
threshold: real;
leftCount: integer;
leftOrderSize: integer;
var leftOrders, rightOrders: array of array of integer);
begin
var xData := X.Data;
var p := Length(nodeOrders);
fVisitId += 1;
@ -4266,31 +4380,33 @@ begin
var splitArr := nodeOrders[feature];
var splitLen := splitArr.Length;
var rightCount := splitLen - leftOrderSize;
var markLeft := leftOrderSize <= rightCount;
var leftCount := 0;
for var i := 0 to splitLen - 1 do
begin
var idx := splitArr[i];
if xData[idx, feature] <= threshold then
begin
fVisitMarks[idx] := mark;
leftCount += 1;
end;
end;
var rightCount := splitLen - leftCount;
if markLeft then
for var i := 0 to leftOrderSize - 1 do
fVisitMarks[splitArr[i]] := mark
else
for var i := leftOrderSize to splitLen - 1 do
fVisitMarks[splitArr[i]] := mark;
SetLength(leftOrders, p);
SetLength(rightOrders, p);
leftOrders[feature] := new integer[leftOrderSize];
rightOrders[feature] := new integer[rightCount];
System.Array.Copy(splitArr, 0, leftOrders[feature], 0, leftOrderSize);
System.Array.Copy(splitArr, leftOrderSize, rightOrders[feature], 0, rightCount);
for var j := 0 to p - 1 do
begin
if j = feature then
continue;
var src := nodeOrders[j];
var n := src.Length;
var left := new integer[leftCount];
var left := new integer[leftOrderSize];
var right := new integer[rightCount];
var li := 0;
@ -4300,6 +4416,8 @@ begin
begin
var idx := src[k];
if markLeft then
begin
if fVisitMarks[idx] = mark then
begin
left[li] := idx;
@ -4310,6 +4428,20 @@ begin
right[ri] := idx;
ri += 1;
end;
end
else
begin
if fVisitMarks[idx] = mark then
begin
right[ri] := idx;
ri += 1;
end
else
begin
left[li] := idx;
li += 1;
end;
end;
end;
leftOrders[j] := left;
@ -4331,9 +4463,11 @@ begin
for var i := 0 to indices.Length - 1 do
begin
var v := yData[indices[i]];
sumAll += v;
sumSqAll += v * v;
var idx := indices[i];
var w := SampleWeight(idx);
var v := yData[idx];
sumAll += w * v;
sumSqAll += w * v * v;
end;
end;
@ -4380,6 +4514,48 @@ begin
end;
end;
procedure DecisionTreeRegressor.SetPreSortedOrders(sortedOrders: array of array of integer);
begin
// Внутренний fast-path для ансамблей:
// используем готовую полную сортировку X и затем фильтруем её по fRowIndices.
fSortedOrders := sortedOrders;
fRowWeights := nil;
fUseSortedOrdersAsRoot := false;
end;
procedure DecisionTreeRegressor.SetPreSortedRootOrders(sortedOrders: array of array of integer);
begin
// Внутренний fast-path для bootstrap-выборок с повторами:
// сортировка уже соответствует корневому узлу текущего дерева.
fSortedOrders := sortedOrders;
fRowWeights := nil;
fUseSortedOrdersAsRoot := true;
end;
procedure DecisionTreeRegressor.SetBootstrapRootOrders(
sortedOrders: array of array of integer;
rowWeights: array of integer);
begin
fSortedOrders := sortedOrders;
fRowWeights := rowWeights;
fUseSortedOrdersAsRoot := true;
end;
function DecisionTreeRegressor.SampleWeight(rowIndex: integer): integer;
begin
if fRowWeights = nil then
Result := 1
else
Result := fRowWeights[rowIndex];
end;
function DecisionTreeRegressor.TotalWeight(indices: array of integer): integer;
begin
Result := 0;
for var i := 0 to indices.Length - 1 do
Result += SampleWeight(indices[i]);
end;
//==============================
// DecisionTreeClassifier
//==============================
@ -4571,7 +4747,7 @@ end;
function DecisionTreeRegressor.LeafValue(y: Vector; indices: array of integer): real;
begin
var n := indices.Length;
var n := TotalWeight(indices);
if n = 0 then
exit(0.0); // безопасный fallback, не должен происходить
@ -4582,7 +4758,7 @@ begin
var sum := 0.0;
foreach var idx in indices do
sum += y[idx];
sum += SampleWeight(idx) * y[idx];
var denom: real;
@ -4637,11 +4813,15 @@ begin
if indices = nil then
indices := Arr(0..X.RowCount - 1);
if (fSortedOrders = nil) or (Length(fSortedOrders) = 0) then
BuildSortedOrders(X, indices);
SetLength(fVisitMarks, X.RowCount);
fVisitId := 0;
if fUseSortedOrdersAsRoot then
fRoot := BuildTreeNode(X, y, fSortedOrders, 0)
else
fRoot := BuildTreeNew(X, y, indices, 0);
var s := fFeatureImportances.Sum;
@ -4653,7 +4833,8 @@ begin
fRowIndices := nil;
fSortedOrders := nil;
fSortedValues := nil;
fUseSortedOrdersAsRoot := false;
fRowWeights := nil;
fVisitMarks := nil;
@ -4830,6 +5011,7 @@ begin
fFeatureCount := p;
SetLength(fTrees, fNTrees);
var fullSortedOrders := BuildPreSortedOrders(X);
// --- OOB buffers (regression) ---
var oobSum: Vector := nil;
@ -4859,7 +5041,10 @@ begin
var rows: array of integer;
BootstrapRowIndices(n, rows);
tree.SetRowIndices(rows);
var rowCounts := BuildRowCounts(rows, n);
var bootSortedOrders := BuildUniqueOrdersFromCounts(fullSortedOrders, rowCounts);
tree.SetBootstrapRootOrders(bootSortedOrders, rowCounts);
tree.Fit(X, y);
// --- OOB accumulate ---
@ -5730,6 +5915,9 @@ begin
for var i := 0 to nTrain - 1 do
yPredTrain[i] := fInitValue;
var residuals := new Vector(nTrain);
var preSortedOrders := BuildPreSortedOrders(XTrain);
// --- OOB logic ---
var useSubsample := fSubsample < 1.0;
var useOOB :=
@ -5754,8 +5942,7 @@ begin
for var m := 0 to fNEstimators - 1 do
begin
// 1. residuals
var r := new Vector(nTrain);
ComputePseudoResiduals(yTrain, yPredTrain, r);
ComputePseudoResiduals(yTrain, yPredTrain, residuals);
var stageSeed := fRng.Next(integer.MaxValue);
@ -5766,6 +5953,7 @@ begin
fLeafL2,
stageSeed
);
tree.SetPreSortedOrders(preSortedOrders);
// --- subsample ---
var rows: array of integer := nil;
@ -5784,7 +5972,7 @@ begin
end;
end;
tree.Fit(XTrain, r);
tree.Fit(XTrain, residuals);
fEstimators.Add(tree);
var deltaTrain := tree.Predict(XTrain);

View file

@ -48,14 +48,14 @@ type
/// Пропущенные значения (NA) игнорируются при обучении
/// и сохраняются как пропуски при преобразовании.
/// Работает только со строковыми столбцами и предназначен для признаков.
/// Не должен применяться к целевому столбцу (target).
LabelEncoder = class(IPreprocessor, IColumnBoundStep)
/// Не должен применяться к целевому столбцу (target)
OrdinalEncoder = class(IPreprocessor, IColumnBoundStep)
private
col: string;
mapping: Dictionary<string, integer>;
fitted: boolean;
public
/// Создаёт LabelEncoder для указанного столбца
/// Создаёт OrdinalEncoder для указанного столбца
constructor Create(column: string);
/// Определяет множество категорий столбца и сохраняет их числовое кодирование.
@ -66,8 +66,10 @@ type
/// отображение категорий НЕ копируется методом Clone
function Fit(df: DataFrame): IPreprocessor;
/// Заменяет категории их числовыми кодами
/// Возвращает новый DataFrame
/// Заменяет категории их числовыми кодами.
/// Неизвестные категории кодируются значением -1.
/// Пропущенные значения сохраняются как пропуски.
/// Возвращает новый DataFrame.
function Transform(df: DataFrame): DataFrame;
/// Выполняет Fit и Transform последовательно
@ -174,14 +176,14 @@ implementation
uses MLExceptions;
const
ER_LABELENCODER_NO_COLUMN =
'LabelEncoder: столбец не указан!!LabelEncoder: column not specified';
ER_LABELENCODER_NOT_STRING =
'LabelEncoder: столбец "{0}" не является строковым!!' +
'LabelEncoder: column "{0}" is not string';
ER_LABELENCODER_UNSEEN_CATEGORY =
'LabelEncoder: неизвестная категория "{0}"!!' +
'LabelEncoder: unseen category "{0}"';
ER_ORDINALENCODER_NO_COLUMN =
'OrdinalEncoder: столбец не указан!!OrdinalEncoder: column not specified';
ER_ORDINALENCODER_NOT_STRING =
'OrdinalEncoder: столбец "{0}" не является строковым!!' +
'OrdinalEncoder: column "{0}" is not string';
ER_ORDINALENCODER_UNSEEN_CATEGORY =
'OrdinalEncoder: неизвестная категория "{0}"!!' +
'OrdinalEncoder: unseen category "{0}"';
ER_ONEHOT_NO_COLUMN =
'OneHotEncoder: столбец не указан!!OneHotEncoder: column not specified';
ER_ONEHOT_NOT_STRING =
@ -229,19 +231,19 @@ const
//-----------------------------
// LabelEncoder
// OrdinalEncoder
//-----------------------------
constructor LabelEncoder.Create(column: string);
constructor OrdinalEncoder.Create(column: string);
begin
if column = '' then
ArgumentError(ER_LABELENCODER_NO_COLUMN);
ArgumentError(ER_ORDINALENCODER_NO_COLUMN);
col := column;
fitted := false;
end;
function LabelEncoder.Fit(df: DataFrame): IPreprocessor;
function OrdinalEncoder.Fit(df: DataFrame): IPreprocessor;
begin
if df = nil then
ArgumentNullError(ER_ARG_NULL, 'df');
@ -252,7 +254,7 @@ begin
var idx := df.Schema.IndexOf(col);
if df.Schema.ColumnTypeAt(idx) <> ColumnType.ctStr then
Error(ER_LABELENCODER_NOT_STRING, col);
Error(ER_ORDINALENCODER_NOT_STRING, col);
mapping := new Dictionary<string, integer>;
@ -275,7 +277,7 @@ begin
Result := Self;
end;
function LabelEncoder.Transform(df: DataFrame): DataFrame;
function OrdinalEncoder.Transform(df: DataFrame): DataFrame;
begin
if not fitted then
NotFittedError(ER_FIT_NOT_CALLED);
@ -299,10 +301,11 @@ begin
begin
var s := cur.Str(idx);
if not mapping.ContainsKey(s) then
Error(ER_LABELENCODER_UNSEEN_CATEGORY, s);
if s not in mapping then
data[row] := -1
else
data[row] := mapping[s];
valid[row] := True;
end;
@ -328,20 +331,20 @@ begin
Result := res.SetCategorical(catCols.ToArray);
end;
function LabelEncoder.FitTransform(df: DataFrame): DataFrame;
function OrdinalEncoder.FitTransform(df: DataFrame): DataFrame;
begin
Fit(df);
Result := Transform(df);
end;
function LabelEncoder.ToString: string;
function OrdinalEncoder.ToString: string;
begin
Result := 'LabelEncoder(' + col + ')';
Result := 'OrdinalEncoder(' + col + ')';
end;
function LabelEncoder.Clone: IPreprocessor;
function OrdinalEncoder.Clone: IPreprocessor;
begin
Result := new LabelEncoder(col);
Result := new OrdinalEncoder(col);
end;
procedure AppendAllColumnsExcept(