Исправления в ML модулях
This commit is contained in:
parent
a8d8ce82bf
commit
91346d8cbe
|
|
@ -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 = "3784";
|
||||
public const string Revision = "3788";
|
||||
|
||||
public const string MainVersion = Major + "." + Minor;
|
||||
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%COREVERSION%=1
|
||||
%REVISION%=3784
|
||||
%REVISION%=3788
|
||||
%MINOR%=11
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
uses MLABC;
|
||||
|
||||
procedure TestModel(model: IModel; Xtrain, Xtest: Matrix; ytrain, ytest: Vector);
|
||||
procedure TestModel(model: ISupervisedModel; Xtrain, Xtest: Matrix; ytrain, ytest: Vector);
|
||||
begin
|
||||
model.Fit(Xtrain, ytrain);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
uses MLABC;
|
||||
|
||||
procedure TestModel(model: IModel; X: Matrix; y: Vector);
|
||||
procedure TestModel(model: ISupervisedModel; X: Matrix; y: Vector);
|
||||
begin
|
||||
var acc :=
|
||||
Validation.StratifiedCrossValidate(
|
||||
|
|
@ -21,7 +21,7 @@ begin
|
|||
var X := df.ToMatrix(ds.Features);
|
||||
var y := df.EncodeLabels(ds.Target);
|
||||
|
||||
var models: array of IModel := (
|
||||
var models: array of ISupervisedModel := (
|
||||
new LogisticRegression,
|
||||
new DecisionTreeClassifier(seed := -1),
|
||||
new RandomForestClassifier(seed := -1),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ begin
|
|||
var price := df.ToVector(ds.Target);
|
||||
|
||||
Plot.Hist(price, bins := 40);
|
||||
Plot.Title('Распределение цен на квартиры в Москве');
|
||||
Plot.Title := 'Распределение цен на квартиры в Москве';
|
||||
Plot.XLabel('Цена (руб)');
|
||||
Plot.YLabel('Количество');
|
||||
end.
|
||||
|
|
@ -11,7 +11,7 @@ begin
|
|||
seed := 1
|
||||
);
|
||||
|
||||
Plot.Title('MakeBlobs синтетический датасет');
|
||||
Plot.Title := 'MakeBlobs синтетический датасет';
|
||||
|
||||
var xs := X.Col(0);
|
||||
var ys := X.Col(1);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.11.1.3784
|
||||
3.11.1.3788
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.11.1.3784'
|
||||
!define VERSION '3.11.1.3788'
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@ function LabelsToInts(y: Vector): array of integer;
|
|||
/// Используется при обучении моделей и визуализации.
|
||||
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 задаёт соответствие: classes[i] — имя класса с индексом i.
|
||||
/// Используется для получения текстовых предсказаний моделей.
|
||||
|
|
@ -42,7 +49,11 @@ const
|
|||
'Столбец "{0}" содержит нечисловые или NA значения!!Column "{0}" contains non-numeric or NA values';
|
||||
ER_ENCODELABELS_COLUMN_NOT_STRING =
|
||||
'Столбец {0} должен быть строковым для EncodeLabels!!Column {0} must be string for EncodeLabels';
|
||||
|
||||
ER_ENCODELABELS_NOT_CATEGORICAL =
|
||||
'Столбец "{0}" должен быть категориальным для EncodeLabels!!Column "{0}" must be categorical for EncodeLabels';
|
||||
ER_ENCODELABELS_UNSUPPORTED_TYPE =
|
||||
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
|
||||
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
begin
|
||||
if y = nil then
|
||||
|
|
@ -51,25 +62,39 @@ begin
|
|||
Result := ArrGen(y.Length, i -> Round(y[i]));
|
||||
end;
|
||||
|
||||
function EncodeLabels(labels: array of string): array of integer;
|
||||
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 classes := labels.Distinct.Order.ToArray;
|
||||
var classList := new List<string>;
|
||||
var map := new Dictionary<string, integer>;
|
||||
|
||||
var map := new Dictionary<string,integer>;
|
||||
|
||||
for var i := 0 to classes.Length-1 do
|
||||
map[classes[i]] := i;
|
||||
// собираем классы в порядке первого появления
|
||||
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
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
res[i] := map[labels[i]];
|
||||
|
||||
classes := classList.ToArray;
|
||||
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
|
||||
|
|
@ -117,6 +142,29 @@ 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,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
|
|
@ -132,12 +180,125 @@ begin
|
|||
if not Self.HasColumn(target) then
|
||||
ArgumentError(ER_COLUMN_NOT_FOUND, target);
|
||||
|
||||
if Self.GetColumnType(target) <> ColumnType.ctStr then
|
||||
ArgumentError(ER_ENCODELABELS_COLUMN_NOT_STRING, target);
|
||||
if not Self.IsCategorical(target) then
|
||||
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
||||
|
||||
var labels := Self.GetStrColumn(target);
|
||||
if Self.GetColumnType(target) = ColumnType.ctStr then
|
||||
begin
|
||||
var labels := Self.GetStrColumn(target);
|
||||
Result := EncodeLabels(labels);
|
||||
exit;
|
||||
end;
|
||||
|
||||
Result := EncodeLabels(labels);
|
||||
if Self.GetColumnType(target) = ColumnType.ctInt then
|
||||
begin
|
||||
var labels := Self.GetIntColumn(target).ToArray;
|
||||
Result := EncodeLabelsIntHelper(labels);
|
||||
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,...
|
||||
/// Порядок кодирования соответствует порядку первого появления значений в столбце.
|
||||
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
||||
/// Работает только для категориальных столбцов типов string и integer.
|
||||
/// Для целочисленных категориальных столбцов значения преобразуются в строки.
|
||||
/// Используется при подготовке данных для задач классификации и визуализации.
|
||||
function EncodeLabels(Self: DataFrame; target: string; var classes: array of string): array of integer; extensionmethod;
|
||||
begin
|
||||
if Self = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'Self');
|
||||
|
||||
if target = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'target');
|
||||
|
||||
if not Self.HasColumn(target) then
|
||||
ArgumentError(ER_COLUMN_NOT_FOUND, target);
|
||||
|
||||
if not Self.IsCategorical(target) then
|
||||
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
||||
|
||||
case Self.GetColumnType(target) of
|
||||
|
||||
ColumnType.ctStr:
|
||||
begin
|
||||
var labels := Self.GetStrColumn(target).ToArray;
|
||||
Result := EncodeLabels(labels, classes);
|
||||
end;
|
||||
|
||||
ColumnType.ctInt:
|
||||
begin
|
||||
var labels := Self.GetIntColumn(target).ToArray;
|
||||
|
||||
var intClasses: array of integer;
|
||||
Result := EncodeLabelsInt(labels, intClasses);
|
||||
|
||||
// если API требует string classes:
|
||||
classes := intClasses.Select(x -> x.ToString).ToArray;
|
||||
end;
|
||||
|
||||
else
|
||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||
end;
|
||||
end;
|
||||
|
||||
/// Кодирует значения целочисленного категориального столбца DataFrame
|
||||
/// в плотные целочисленные индексы 0,1,2,...
|
||||
/// Каждому уникальному значению присваивается номер в порядке первого появления.
|
||||
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
||||
/// Работает только для категориальных столбцов типа integer.
|
||||
/// Используется при подготовке данных для задач классификации и анализа.
|
||||
function EncodeLabelsInt(Self: DataFrame; target: string; var classes: array of integer): array of integer; extensionmethod;
|
||||
begin
|
||||
if Self = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'Self');
|
||||
|
||||
if target = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'target');
|
||||
|
||||
if not Self.HasColumn(target) then
|
||||
ArgumentError(ER_COLUMN_NOT_FOUND, target);
|
||||
|
||||
if not Self.IsCategorical(target) then
|
||||
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
||||
|
||||
if Self.GetColumnType(target) <> ColumnType.ctInt then
|
||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||
|
||||
var labels := Self.GetIntColumn(target).ToArray;
|
||||
|
||||
Result := EncodeLabelsInt(labels, classes);
|
||||
end;
|
||||
|
||||
function DecodeLabels(y: array of integer; classes: array of string): array of string;
|
||||
|
|
|
|||
|
|
@ -104,6 +104,8 @@ type
|
|||
|
||||
function SetCategorical(names: array of string): DataFrame;
|
||||
|
||||
function IsCategorical(name: string): boolean;
|
||||
|
||||
property Item[name: string]: Column read GetColumn; default;
|
||||
|
||||
function GetColumns: sequence of Column;
|
||||
|
|
@ -1548,7 +1550,7 @@ begin
|
|||
|
||||
// 🔥 берем из старой schema
|
||||
if (fSchema <> nil) and (i < fSchema.ColumnCount) then
|
||||
cats[i] := fSchema.IsCategorical[i]
|
||||
cats[i] := fSchema.CategoricalFlags[i]
|
||||
else
|
||||
cats[i] := false;
|
||||
end;
|
||||
|
|
@ -2314,7 +2316,7 @@ begin
|
|||
|
||||
// копируем старые значения
|
||||
for var i := 0 to n - 1 do
|
||||
cats[i] := fSchema.IsCategorical[i];
|
||||
cats[i] := fSchema.CategoricalFlags[i];
|
||||
|
||||
// применяем новые
|
||||
foreach var name in names do
|
||||
|
|
@ -2331,6 +2333,17 @@ begin
|
|||
Result := new DataFrame(columns, newSchema);
|
||||
end;
|
||||
|
||||
function DataFrame.IsCategorical(name: string): boolean;
|
||||
begin
|
||||
if name = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'name');
|
||||
|
||||
if not HasColumn(name) then
|
||||
ArgumentError(ER_COLUMN_NOT_FOUND, name);
|
||||
|
||||
Result := Schema.IsCategorical(name);
|
||||
end;
|
||||
|
||||
function DataFrame.Filter(pred: CursorPredicate): DataFrame;
|
||||
begin
|
||||
var cursor := GetCursor;
|
||||
|
|
@ -2440,7 +2453,7 @@ begin
|
|||
// 🔥 КЛЮЧЕВОЕ: перенос schema
|
||||
var cats := new boolean[ColumnCount];
|
||||
for var i := 0 to ColumnCount - 1 do
|
||||
cats[i] := fSchema.IsCategorical[i];
|
||||
cats[i] := fSchema.CategoricalFlags[i];
|
||||
|
||||
res.SetSchema(new DataFrameSchema(
|
||||
fSchema.ColumnNames,
|
||||
|
|
@ -2612,7 +2625,7 @@ begin
|
|||
|
||||
names[i] := newName;
|
||||
types[i] := fSchema.Types[i];
|
||||
cats[i] := fSchema.IsCategorical[i];
|
||||
cats[i] := fSchema.CategoricalFlags[i];
|
||||
end;
|
||||
|
||||
res.SetSchema(new DataFrameSchema(names, types, cats));
|
||||
|
|
@ -3059,7 +3072,7 @@ begin
|
|||
begin
|
||||
names[j] := fSchema.ColumnNames[j];
|
||||
types[j] := fSchema.Types[j];
|
||||
cats[j] := fSchema.IsCategorical[j];
|
||||
cats[j] := fSchema.CategoricalFlags[j];
|
||||
end;
|
||||
|
||||
names[oldN] := name;
|
||||
|
|
@ -3834,7 +3847,7 @@ begin
|
|||
for var i := 0 to ColumnCount - 1 do
|
||||
begin
|
||||
var t := ColumnTypeToString(GetColumnType(i));
|
||||
if fSchema.IsCategorical[i] then
|
||||
if fSchema.CategoricalFlags[i] then
|
||||
t += ' (categorical)';
|
||||
|
||||
types[i] := t;
|
||||
|
|
@ -3974,7 +3987,7 @@ begin
|
|||
var name := fSchema.ColumnNames[i];
|
||||
|
||||
namesArr[i] := name;
|
||||
cats[i] := fSchema.IsCategorical[i];
|
||||
cats[i] := fSchema.CategoricalFlags[i];
|
||||
|
||||
if toCast.Contains(name) then
|
||||
types[i] := ctInt
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ type
|
|||
private
|
||||
fNames: array of string;
|
||||
fTypes: array of ColumnType;
|
||||
fIsCategorical: array of boolean;
|
||||
fCategoricalFlags: array of boolean;
|
||||
fIndexByName: Dictionary<string, integer>;
|
||||
|
||||
class function BuildIndex(names: array of string): Dictionary<string, integer>;
|
||||
|
|
@ -24,13 +24,17 @@ type
|
|||
property ColumnCount: integer read fNames.Length;
|
||||
property ColumnNames: array of string read fNames;
|
||||
property Types: array of ColumnType read fTypes;
|
||||
property IsCategorical: array of boolean read fIsCategorical;
|
||||
property CategoricalFlags: array of boolean read fCategoricalFlags;
|
||||
|
||||
function IndexOf(name: string): integer;
|
||||
function HasColumn(name: string): boolean;
|
||||
|
||||
function ColumnTypeAt(i: integer): ColumnType;
|
||||
function IsCategoricalAt(i: integer): boolean;
|
||||
|
||||
function GetColumnType(name: string): ColumnType;
|
||||
function IsCategorical(name: string): boolean;
|
||||
|
||||
function NameAt(i: integer): string;
|
||||
|
||||
constructor Create(names: array of string; types: array of ColumnType;
|
||||
|
|
@ -338,7 +342,7 @@ begin
|
|||
fNames := Copy(names);
|
||||
fTypes := Copy(types);
|
||||
|
||||
fIsCategorical :=
|
||||
fCategoricalFlags :=
|
||||
if isCategorical = nil then
|
||||
new boolean[names.Length]
|
||||
else
|
||||
|
|
@ -372,7 +376,7 @@ begin
|
|||
ctBool: t := 'bool';
|
||||
end;
|
||||
|
||||
if fIsCategorical[i] then
|
||||
if fCategoricalFlags[i] then
|
||||
PABCSystem.Println(name, ':', t, '(categorical)')
|
||||
else
|
||||
PABCSystem.Println(name, ':', t);
|
||||
|
|
@ -414,10 +418,20 @@ function DataFrameSchema.IsCategoricalAt(i: integer): boolean;
|
|||
begin
|
||||
if (i < 0) or (i >= ColumnCount) then
|
||||
ArgumentOutOfRangeError(ER_INDEX_OUT_OF_RANGE, i, ColumnCount);
|
||||
if fIsCategorical = nil then
|
||||
if fCategoricalFlags = nil then
|
||||
Result := false
|
||||
else
|
||||
Result := fIsCategorical[i];
|
||||
Result := fCategoricalFlags[i];
|
||||
end;
|
||||
|
||||
function DataFrameSchema.GetColumnType(name: string): ColumnType;
|
||||
begin
|
||||
Result := ColumnTypeAt(IndexOf(name));
|
||||
end;
|
||||
|
||||
function DataFrameSchema.IsCategorical(name: string): boolean;
|
||||
begin
|
||||
Result := IsCategoricalAt(IndexOf(name));
|
||||
end;
|
||||
|
||||
function DataFrameSchema.Select(indices: array of integer): DataFrameSchema;
|
||||
|
|
@ -428,7 +442,7 @@ begin
|
|||
var n := indices.Length;
|
||||
var names := new string[n];
|
||||
var types := new ColumnType[n];
|
||||
var cats := if fIsCategorical = nil then nil else new boolean[n];
|
||||
var cats := if fCategoricalFlags = nil then nil else new boolean[n];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
|
|
@ -437,7 +451,7 @@ begin
|
|||
ArgumentOutOfRangeError(ER_INDEX_OUT_OF_RANGE, k, ColumnCount);
|
||||
names[i] := fNames[k];
|
||||
types[i] := fTypes[k];
|
||||
if cats <> nil then cats[i] := fIsCategorical[k];
|
||||
if cats <> nil then cats[i] := fCategoricalFlags[k];
|
||||
end;
|
||||
|
||||
Result := new DataFrameSchema(names, types, cats);
|
||||
|
|
@ -474,7 +488,7 @@ begin
|
|||
var names := Copy(fNames);
|
||||
names[IndexOf(oldName)] := newName;
|
||||
|
||||
Result := new DataFrameSchema(names, fTypes, fIsCategorical);
|
||||
Result := new DataFrameSchema(names, fTypes, fCategoricalFlags);
|
||||
end;
|
||||
|
||||
function DataFrameSchema.WithCategorical(name: string; value: boolean): DataFrameSchema;
|
||||
|
|
@ -482,7 +496,7 @@ begin
|
|||
if not HasColumn(name) then
|
||||
ArgumentError(ER_COLUMN_NOT_EXISTS, name);
|
||||
|
||||
var cats := if fIsCategorical = nil then new boolean[ColumnCount] else Copy(fIsCategorical);
|
||||
var cats := if fCategoricalFlags = nil then new boolean[ColumnCount] else Copy(fCategoricalFlags);
|
||||
cats[IndexOf(name)] := value;
|
||||
|
||||
Result := new DataFrameSchema(fNames, fTypes, cats);
|
||||
|
|
@ -533,7 +547,7 @@ end;
|
|||
procedure DataFrameSchema.AssertConsistent;
|
||||
begin
|
||||
Assert(fNames.Length = fTypes.Length);
|
||||
if fIsCategorical <> nil then Assert(fIsCategorical.Length = fNames.Length);
|
||||
if fCategoricalFlags <> nil then Assert(fCategoricalFlags.Length = fNames.Length);
|
||||
Assert(fIndexByName.Count = fNames.Length);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -89,8 +89,11 @@ type
|
|||
Datasets = MLDatasets.Datasets;
|
||||
|
||||
IModel = MLCoreABC.IModel;
|
||||
ISupervisedModel = MLCoreABC.ISupervisedModel;
|
||||
IUnSupervisedModel = MLCoreABC.IUnSupervisedModel;
|
||||
UPipeline = MLModelsABC.UPipeline;
|
||||
UDataPipeline = MLPipelineABC.UDataPipeline;
|
||||
TaskKind = MLPipelineABC.TaskKind;
|
||||
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
function EncodeLabels(labels: array of string): array of integer;
|
||||
|
|
|
|||
|
|
@ -64,8 +64,13 @@ type
|
|||
/// Наследуется от IModel.
|
||||
/// Предназначен для моделей, выполняющих классификацию (предсказание меток классов).
|
||||
IClassifier = interface(ISupervisedModel)
|
||||
/// Возвращает метки классов
|
||||
/// Возвращает индексы классов (0,1,2,...)
|
||||
function PredictLabels(X: Matrix): array of integer;
|
||||
|
||||
/// Возвращает метки классов в порядке кодирования
|
||||
function GetClassLabels: array of string;
|
||||
|
||||
procedure SetClassLabels(classes: array of string);
|
||||
end;
|
||||
|
||||
/// Интерфейс классификатора, возвращающего вероятности.
|
||||
|
|
|
|||
|
|
@ -1151,7 +1151,13 @@ end;
|
|||
|
||||
static function Datasets.Iris: Dataset;
|
||||
begin
|
||||
Result := Load('Iris');
|
||||
var ds := Load('Iris');
|
||||
|
||||
ds.Data := ds.Data.SetCategorical([
|
||||
'species'
|
||||
]);
|
||||
|
||||
Result := ds;
|
||||
end;
|
||||
|
||||
static function Datasets.MoscowHousing: Dataset;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const
|
|||
'Пустой набор данных для {0}!!Empty dataset for {0}';
|
||||
ER_COLUMN_NOT_FOUND =
|
||||
'В DataFrame отсутствует столбец "{0}"!!DataFrame does not contain column "{0}"';
|
||||
|
||||
|
||||
type
|
||||
/// Базовое исключение ML-библиотеки
|
||||
MLException = class(Exception);
|
||||
|
|
|
|||
|
|
@ -232,6 +232,8 @@ type
|
|||
fCheckConvergence: boolean := true;
|
||||
fMinImprovement: real := 1e-8;
|
||||
|
||||
fClassLabels: array of string; // В каждой модели классификации
|
||||
|
||||
function GetWeights: Matrix;
|
||||
function GetIntercept: Vector;
|
||||
public
|
||||
|
|
@ -282,6 +284,10 @@ type
|
|||
property Intercept: Vector read GetIntercept;
|
||||
|
||||
function Name: string := Self.GetType.Name;
|
||||
|
||||
procedure SetClassLabels(classes: array of string);
|
||||
|
||||
function GetClassLabels: array of string;
|
||||
end;
|
||||
|
||||
DecisionTreeNode = class
|
||||
|
|
@ -452,6 +458,8 @@ type
|
|||
fClassToIndex: Dictionary<integer, integer>;
|
||||
fIndexToClass: array of integer;
|
||||
fClassCount: integer;
|
||||
|
||||
fClassLabels: array of string;
|
||||
|
||||
function PredictOne(X: Matrix; rowIndex: integer): integer;
|
||||
function MajorityClass(y: Vector; indices: array of integer): integer;
|
||||
|
|
@ -496,6 +504,10 @@ type
|
|||
function IndexToClass: array of integer := Copy(fIndexToClass);
|
||||
|
||||
function Name: string := Self.GetType.Name;
|
||||
|
||||
procedure SetClassLabels(classes: array of string);
|
||||
|
||||
function GetClassLabels: array of string;
|
||||
end;
|
||||
|
||||
//============================
|
||||
|
|
@ -713,6 +725,7 @@ type
|
|||
fIndexToClass: array of integer;
|
||||
fClassToIndex: Dictionary<integer, integer>;
|
||||
fClassCount: integer;
|
||||
fClassLabels: array of string;
|
||||
public
|
||||
/// Создает классификационный случайный лес:
|
||||
/// • nTrees — число деревьев в ансамбле.
|
||||
|
|
@ -762,6 +775,10 @@ type
|
|||
function GetClasses: array of real;
|
||||
|
||||
function Name: string := Self.GetType.Name;
|
||||
|
||||
procedure SetClassLabels(classes: array of string);
|
||||
|
||||
function GetClassLabels: array of string;
|
||||
end;
|
||||
|
||||
{ Gradient Boosting v1.0 — Freeze Checklist
|
||||
|
|
@ -988,6 +1005,8 @@ type
|
|||
fRandomSeed: integer;
|
||||
fRng: System.Random;
|
||||
fUserProvidedSeed: boolean;
|
||||
|
||||
fClassLabels: array of string;
|
||||
|
||||
private
|
||||
function FitInternal(XTrain: Matrix; yTrain: Vector; XVal: Matrix; yVal: Vector; useValidation: boolean)
|
||||
|
|
@ -1083,6 +1102,10 @@ type
|
|||
function Name: string := Self.GetType.Name;
|
||||
|
||||
property IsFitted: boolean read fFitted;
|
||||
|
||||
procedure SetClassLabels(classes: array of string);
|
||||
|
||||
function GetClassLabels: array of string;
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
|
|
@ -1155,6 +1178,7 @@ type
|
|||
fYEnc: array of integer;
|
||||
fClasses: array of double;
|
||||
fClassCount: integer;
|
||||
fClassLabels: array of string;
|
||||
|
||||
// ==== voting buffers ====
|
||||
fVotes: array of double;
|
||||
|
|
@ -1192,6 +1216,10 @@ type
|
|||
function Clone: IModel; override;
|
||||
|
||||
function Name: string := Self.GetType.Name;
|
||||
|
||||
procedure SetClassLabels(classes: array of string);
|
||||
|
||||
function GetClassLabels: array of string;
|
||||
end;
|
||||
|
||||
|
||||
|
|
@ -1979,6 +2007,10 @@ const
|
|||
'DBSCAN: Predict поддерживается только для обучающей выборки!!DBSCAN: Predict is only supported for training data';
|
||||
ER_MODEL_NOT_FITTED =
|
||||
'Модель "{0}" не обучена. Сначала вызовите Fit()|Model "{0}" is not fitted. Call Fit() first';
|
||||
ER_DBSCAN_PREDICT_NEW_DATA =
|
||||
'DBSCAN не поддерживает предсказание для новых данных!!DBSCAN does not support prediction for new data';
|
||||
ER_CLASSES_NOT_AVAILABLE =
|
||||
'Метки классов недоступны. Убедитесь, что модель обучена и метки установлены!!Class labels are not available. Ensure the model is fitted and class labels are set';
|
||||
|
||||
{$endregion ErrConstants}
|
||||
|
||||
|
|
@ -2799,6 +2831,19 @@ begin
|
|||
Result := fIntercept;
|
||||
end;
|
||||
|
||||
procedure LogisticRegression.SetClassLabels(classes: array of string);
|
||||
begin
|
||||
fClassLabels := Copy(classes);
|
||||
end;
|
||||
|
||||
function LogisticRegression.GetClassLabels: array of string;
|
||||
begin
|
||||
if fClassLabels = nil then
|
||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||
|
||||
Result := fClassLabels;
|
||||
end;
|
||||
|
||||
function GiniCriterion.Impurity(y: Vector; indices: array of integer): real;
|
||||
begin
|
||||
var n := indices.Length;
|
||||
|
|
@ -3614,6 +3659,19 @@ begin
|
|||
')';
|
||||
end;
|
||||
|
||||
procedure DecisionTreeClassifier.SetClassLabels(classes: array of string);
|
||||
begin
|
||||
fClassLabels := Copy(classes);
|
||||
end;
|
||||
|
||||
function DecisionTreeClassifier.GetClassLabels: array of string;
|
||||
begin
|
||||
if fClassLabels = nil then
|
||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||
|
||||
Result := fClassLabels;
|
||||
end;
|
||||
|
||||
// DecisionTreeRegressor
|
||||
|
||||
constructor DecisionTreeRegressor.Create(maxDepth: integer; minSamplesSplit: integer;
|
||||
|
|
@ -4513,6 +4571,19 @@ begin
|
|||
Result[i] := fIndexToClass[i];
|
||||
end;
|
||||
|
||||
procedure RandomForestClassifier.SetClassLabels(classes: array of string);
|
||||
begin
|
||||
fClassLabels := Copy(classes);
|
||||
end;
|
||||
|
||||
function RandomForestClassifier.GetClassLabels: array of string;
|
||||
begin
|
||||
if fClassLabels = nil then
|
||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||
|
||||
Result := fClassLabels;
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// GradientBoostingRegressor
|
||||
//-----------------------------
|
||||
|
|
@ -6266,6 +6337,19 @@ begin
|
|||
Result[i] := integer(v[i]);
|
||||
end;
|
||||
|
||||
procedure GradientBoostingClassifier.SetClassLabels(classes: array of string);
|
||||
begin
|
||||
fClassLabels := Copy(classes);
|
||||
end;
|
||||
|
||||
function GradientBoostingClassifier.GetClassLabels: array of string;
|
||||
begin
|
||||
if fClassLabels = nil then
|
||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||
|
||||
Result := fClassLabels;
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// KNNBase
|
||||
//-----------------------------
|
||||
|
|
@ -6720,6 +6804,20 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure KNNClassifier.SetClassLabels(classes: array of string);
|
||||
begin
|
||||
fClassLabels := Copy(classes);
|
||||
end;
|
||||
|
||||
function KNNClassifier.GetClassLabels: array of string;
|
||||
begin
|
||||
if fClassLabels = nil then
|
||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||
|
||||
Result := fClassLabels;
|
||||
end;
|
||||
|
||||
|
||||
//-----------------------------
|
||||
// KNNRegressor
|
||||
//-----------------------------
|
||||
|
|
@ -7326,9 +7424,15 @@ end;
|
|||
|
||||
function DBSCAN.PredictLabels(X: Matrix): array of integer;
|
||||
begin
|
||||
if X = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'X');
|
||||
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
if X.RowCount <> Length(fLabels) then
|
||||
ArgumentError(ER_DBSCAN_PREDICT_NEW_DATA);
|
||||
|
||||
Result := Copy(fLabels);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ uses PreprocessorABC;
|
|||
uses DataFrameABC;
|
||||
uses LinearAlgebraML;
|
||||
|
||||
type
|
||||
TaskKind = (tkRegression, tkClassification);
|
||||
|
||||
type
|
||||
/// DataPipeline — конвейер подготовки данных и обучения модели с учителем на DataFrame.
|
||||
///
|
||||
|
|
@ -30,6 +33,7 @@ type
|
|||
fDataSteps: List<IPreprocessor>;
|
||||
fMatrixSteps: List<ITransformer>;
|
||||
fModel: IModel;
|
||||
fTask: TaskKind;
|
||||
|
||||
fTarget: string;
|
||||
fFeatures: array of string;
|
||||
|
|
@ -93,17 +97,23 @@ type
|
|||
// ------------------------------------------------------------
|
||||
// Пример 4. Классификация без Pipeline
|
||||
var df := Datasets.Flowers;
|
||||
|
||||
// --- Encode target (DataFrame уровень)
|
||||
df := df.SetCategorical(['species']);
|
||||
|
||||
var enc := new LabelEncoder('species');
|
||||
df := enc.FitTransform(df); // DataFrame уровень
|
||||
var labels := df.EncodeLabels('species');
|
||||
|
||||
var (X,y) := df.ToXY(['length','width'],'species');
|
||||
// --- X, y
|
||||
var X := df.ToMatrix(['length','width']);
|
||||
var y := new Vector(labels);
|
||||
|
||||
// --- Matrix уровень
|
||||
var scaler := new StandardScaler;
|
||||
X := scaler.FitTransform(X); // Matrix уровень
|
||||
X := scaler.FitTransform(X);
|
||||
|
||||
// --- Модель
|
||||
var model := new LogisticRegression;
|
||||
model.Fit(X,y);
|
||||
model.Fit(X, y);
|
||||
|
||||
// Pipeline.Build используется, когда данные уже представлены
|
||||
// в виде числовой матрицы признаков X и вектора целевой переменной y.
|
||||
|
|
@ -128,11 +138,16 @@ type
|
|||
|
||||
/// Строит конвейер из шагов обработки данных и модели.
|
||||
///
|
||||
/// Используется в задачах с учителем (с target).
|
||||
/// Используется в задачах с учителем (с target).
|
||||
/// task задает тип модели - регрессия или классификация
|
||||
/// Шаги выполняются последовательно:
|
||||
/// DataFrame-преобразования → (при необходимости) матричные шаги → модель.
|
||||
static function Build(target: string; features: array of string;
|
||||
params steps: array of IPipelineStep): DataPipeline;
|
||||
static function Build(
|
||||
task: TaskKind;
|
||||
target: string;
|
||||
features: array of string;
|
||||
params steps: array of IPipelineStep
|
||||
): DataPipeline;
|
||||
|
||||
/// Обучает конвейер на DataFrame.
|
||||
/// Семантика:
|
||||
|
|
@ -157,9 +172,10 @@ type
|
|||
/// Доступен только если конечная модель поддерживает IProbabilisticClassifier.
|
||||
function PredictProba(df: DataFrame): Matrix;
|
||||
|
||||
/// Возвращает список классов в порядке столбцов PredictProba.
|
||||
/// Доступен только если конечная модель поддерживает IProbabilisticClassifier.
|
||||
function GetClasses: array of real;
|
||||
/// Возвращает метки классов в порядке кодирования (0,1,2,...),
|
||||
/// используемом при EncodeLabels.
|
||||
/// Доступен только для задач классификации после Fit.
|
||||
function GetClassLabels: array of string;
|
||||
|
||||
/// Признак того, что был вызван Fit или FitTransform.
|
||||
property IsFitted: boolean read fFitted;
|
||||
|
|
@ -326,7 +342,14 @@ const
|
|||
'Признак "{0}" имеет тип {1} и должен быть числовым!!Feature "{0}" has type {1} but must be numeric';
|
||||
ER_MODEL_NOT_CLUSTERER =
|
||||
'Модель "{0}" не является алгоритмом кластеризации!!Model "{0}" is not a clustering algorithm';
|
||||
|
||||
ER_NOT_CLASSIFICATION =
|
||||
'Операция доступна только для задач классификации!!Operation is only available for classification tasks';
|
||||
ER_CLASSES_NOT_AVAILABLE =
|
||||
'Метки классов недоступны. Убедитесь, что конвейер обучен и задача — классификация!!Class labels are not available. Ensure the pipeline is fitted and the task is classification';
|
||||
ER_LABELENCODER_TARGET_NOT_ALLOWED =
|
||||
'LabelEncoder нельзя применять к целевому столбцу. Используйте EncodeLabels!!LabelEncoder cannot be applied to the target column. Use EncodeLabels instead';
|
||||
ER_ENCODELABELS_NOT_CATEGORICAL =
|
||||
'Целевой столбец должен быть категориальным для задач классификации!!Target column must be categorical for classification tasks';
|
||||
//-----------------------------
|
||||
// DataPipeline
|
||||
//-----------------------------
|
||||
|
|
@ -358,6 +381,10 @@ begin
|
|||
fDataSteps.Add(step as IPreprocessor);
|
||||
exit(Self);
|
||||
end;
|
||||
|
||||
if step is LabelEncoder(var enc) then
|
||||
if enc.ColumnName = fTarget then
|
||||
ArgumentError(ER_LABELENCODER_TARGET_NOT_ALLOWED, fTarget);
|
||||
|
||||
// --- Matrix transformer
|
||||
if step is ITransformer then
|
||||
|
|
@ -383,15 +410,19 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
class function DataPipeline.Build(target: string;
|
||||
features: array of string; params steps: array of IPipelineStep): DataPipeline;
|
||||
class function DataPipeline.Build(
|
||||
task: TaskKind;
|
||||
target: string;
|
||||
features: array of string;
|
||||
params steps: array of IPipelineStep
|
||||
): DataPipeline;
|
||||
begin
|
||||
if (target = nil) or (target = '') then
|
||||
ArgumentError(ER_TARGET_EMPTY);
|
||||
|
||||
if (features = nil) or (features.Length = 0) then
|
||||
ArgumentError(ER_FEATURES_EMPTY);
|
||||
|
||||
|
||||
var seen := new HashSet<string>;
|
||||
|
||||
foreach var f in features do
|
||||
|
|
@ -410,10 +441,12 @@ begin
|
|||
|
||||
var p := new DataPipeline;
|
||||
p.fTarget := target;
|
||||
p.fFeatures := features;
|
||||
p.fFeatures := Copy(features);
|
||||
p.fTask := task;
|
||||
|
||||
for var i := 0 to High(steps) do
|
||||
p.Add(steps[i]);
|
||||
if steps <> nil then
|
||||
for var i := 0 to High(steps) do
|
||||
p.Add(steps[i]);
|
||||
|
||||
Result := p;
|
||||
end;
|
||||
|
|
@ -465,7 +498,20 @@ begin
|
|||
fFinalFeatures := feats.ToArray;
|
||||
|
||||
var X := current.ToMatrix(fFinalFeatures);
|
||||
var y := current.ToVector(fTarget);
|
||||
|
||||
var classes: array of string;
|
||||
var y: Vector;
|
||||
|
||||
case fTask of
|
||||
tkRegression:
|
||||
y := current.ToVector(fTarget);
|
||||
|
||||
tkClassification:
|
||||
begin
|
||||
var labels := current.EncodeLabels(fTarget, classes);
|
||||
y := new Vector(labels);
|
||||
end;
|
||||
end;
|
||||
|
||||
if X.RowCount <> y.Length then
|
||||
DimensionError(ER_XY_SIZE_MISMATCH, X.RowCount, y.Length);
|
||||
|
|
@ -488,6 +534,10 @@ begin
|
|||
if fModel is ISupervisedModel(var supModel) then
|
||||
fModel := supModel.Fit(X, y)
|
||||
else ArgumentError(ER_MODEL_NOT_SUPERVISED, fModel.GetType.Name);
|
||||
|
||||
if fTask = tkClassification then
|
||||
if fModel is IClassifier(var cls) then
|
||||
cls.SetClassLabels(classes);
|
||||
|
||||
fFitted := true;
|
||||
Result := Self;
|
||||
|
|
@ -568,6 +618,13 @@ begin
|
|||
if fFinalFeatures = nil then
|
||||
Error(ER_PIPELINE_FINALFEATURES);
|
||||
|
||||
if fTask <> tkClassification then
|
||||
ArgumentError(ER_NOT_CLASSIFICATION);
|
||||
|
||||
for var i := 0 to High(fFinalFeatures) do
|
||||
if not current.HasColumn(fFinalFeatures[i]) then
|
||||
ArgumentError(ER_PIPELINE_FEATURE_NOT_FOUND, fFinalFeatures[i]);
|
||||
|
||||
var X := current.ToMatrix(fFinalFeatures);
|
||||
|
||||
foreach var t in fMatrixSteps do
|
||||
|
|
@ -576,14 +633,19 @@ begin
|
|||
Result := (fModel as IProbabilisticClassifier).PredictProba(X);
|
||||
end;
|
||||
|
||||
function DataPipeline.GetClasses: array of real;
|
||||
function DataPipeline.GetClassLabels: array of string;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
if not (fModel is IProbabilisticClassifier) then
|
||||
ArgumentError(ER_PROBA_NOT_SUPPORTED);
|
||||
|
||||
Result := (fModel as IProbabilisticClassifier).GetClasses;
|
||||
if fTask <> tkClassification then
|
||||
ArgumentError(ER_NOT_CLASSIFICATION);
|
||||
|
||||
var cls := fModel as IClassifier;
|
||||
if cls = nil then
|
||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||
|
||||
Result := cls.GetClassLabels;
|
||||
end;
|
||||
|
||||
procedure DataPipeline.ValidateSchema(df: DataFrame);
|
||||
|
|
@ -605,7 +667,11 @@ begin
|
|||
var cols := df.Schema.ColumnNames.JoinToString(', ');
|
||||
ArgumentError(ER_DATAPIPE_TARGET_NOT_FOUND, fTarget, cols);
|
||||
end;
|
||||
|
||||
|
||||
if fTask = tkClassification then
|
||||
if not df.IsCategorical(fTarget) then
|
||||
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, fTarget);
|
||||
|
||||
var seen := new HashSet<string>;
|
||||
|
||||
for var i := 0 to High(fFeatures) do
|
||||
|
|
|
|||
|
|
@ -1057,59 +1057,6 @@ begin
|
|||
Result := counts;
|
||||
end;
|
||||
|
||||
{class procedure Plot.PairPlot(X: array[,] of real; labels: array of integer; names: array of string);
|
||||
begin
|
||||
var n := names.Length;
|
||||
var fig := Plot.Grid(n,n);
|
||||
|
||||
var bins := Round(Sqrt(X.GetLength(0))); //20;
|
||||
|
||||
// Диапазоны признаков
|
||||
var xmin := new real[n];
|
||||
var xmax := new real[n];
|
||||
|
||||
// Верхние границы для гистограмм
|
||||
var histYMax := new real[n];
|
||||
|
||||
for var j := 0 to n-1 do
|
||||
begin
|
||||
var col := X.Col(j);
|
||||
|
||||
xmin[j] := Floor(col.Min);
|
||||
xmax[j] := Ceil(col.Max);
|
||||
|
||||
var counts := HistogramCounts(col, bins, xmin[j], xmax[j]);
|
||||
histYMax[j] := counts.Max * 1.1;
|
||||
end;
|
||||
|
||||
for var i := 0 to n-1 do
|
||||
for var j := 0 to n-1 do
|
||||
begin
|
||||
var ax := fig[i,j];
|
||||
|
||||
if i = j then
|
||||
begin
|
||||
ax.Hist(X.Col(i), bins := bins);
|
||||
|
||||
ax.XLim(xmin[j], xmax[j]);
|
||||
ax.YLim(0, histYMax[j]);
|
||||
end
|
||||
else
|
||||
begin
|
||||
ax.Points(X.Col(j), X.Col(i), labels, size := 3);
|
||||
|
||||
ax.XLim(xmin[j], xmax[j]);
|
||||
ax.YLim(xmin[i], xmax[i]);
|
||||
end;
|
||||
|
||||
if i = n-1 then
|
||||
ax.XLabel(names[j]);
|
||||
|
||||
if j = 0 then
|
||||
ax.YLabel(names[i]);
|
||||
end;
|
||||
end;}
|
||||
|
||||
class procedure Plot.PairPlot(X: array[,] of real; labels: array of integer; names: array of string);
|
||||
begin
|
||||
var n := names.Length;
|
||||
|
|
|
|||
|
|
@ -37,9 +37,11 @@ type
|
|||
function FitTransform(df: DataFrame): DataFrame;
|
||||
end;
|
||||
|
||||
/// Кодирует строковый категориальный столбец в числовые значения
|
||||
/// Категории фиксируются при Fit
|
||||
/// Работает только со строковыми столбцами
|
||||
/// Кодирует строковый категориальный столбец в целочисленные индексы (0,1,2,...).
|
||||
/// Соответствие значений и индексов фиксируется при вызове Fit
|
||||
/// в порядке первого появления категорий.
|
||||
/// Работает только со строковыми столбцами и предназначен для признаков.
|
||||
/// Не должен применяться к целевому столбцу (target).
|
||||
LabelEncoder = class(IPreprocessor)
|
||||
private
|
||||
col: string;
|
||||
|
|
@ -58,6 +60,8 @@ type
|
|||
function FitTransform(df: DataFrame): DataFrame;
|
||||
|
||||
function ToString: string; override;
|
||||
|
||||
property ColumnName: string read col;
|
||||
end;
|
||||
|
||||
/// Кодирует строковый категориальный столбец в набор бинарных (one-hot) столбцов
|
||||
|
|
@ -199,6 +203,12 @@ end;
|
|||
|
||||
function LabelEncoder.Fit(df: DataFrame): IPreprocessor;
|
||||
begin
|
||||
if df = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'df');
|
||||
|
||||
if not df.HasColumn(col) then
|
||||
ArgumentError(ER_COLUMN_NOT_FOUND, col);
|
||||
|
||||
var idx := df.Schema.IndexOf(col);
|
||||
|
||||
if df.Schema.ColumnTypeAt(idx) <> ColumnType.ctStr then
|
||||
|
|
@ -245,6 +255,8 @@ begin
|
|||
Result := mapping[s];
|
||||
end
|
||||
);
|
||||
|
||||
Result := Result.SetCategorical([col]);
|
||||
end;
|
||||
|
||||
function LabelEncoder.FitTransform(df: DataFrame): DataFrame;
|
||||
|
|
|
|||
Loading…
Reference in a new issue