LastMaxBy LastMinBy исправления
This commit is contained in:
parent
dd7a305533
commit
2294ce5844
|
|
@ -15,7 +15,7 @@ internal static class RevisionClass
|
||||||
public const string Major = "3";
|
public const string Major = "3";
|
||||||
public const string Minor = "11";
|
public const string Minor = "11";
|
||||||
public const string Build = "1";
|
public const string Build = "1";
|
||||||
public const string Revision = "3808";
|
public const string Revision = "3810";
|
||||||
|
|
||||||
public const string MainVersion = Major + "." + Minor;
|
public const string MainVersion = Major + "." + Minor;
|
||||||
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
%COREVERSION%=1
|
%COREVERSION%=1
|
||||||
%REVISION%=3808
|
%REVISION%=3810
|
||||||
%MINOR%=11
|
%MINOR%=11
|
||||||
%MAJOR%=3
|
%MAJOR%=3
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
3.11.1.3808
|
3.11.1.3810
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
!define VERSION '3.11.1.3808'
|
!define VERSION '3.11.1.3810'
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ const
|
||||||
'Столбец "{0}" должен быть категориальным для EncodeLabels!!Column "{0}" must be categorical for EncodeLabels';
|
'Столбец "{0}" должен быть категориальным для EncodeLabels!!Column "{0}" must be categorical for EncodeLabels';
|
||||||
ER_ENCODELABELS_UNSUPPORTED_TYPE =
|
ER_ENCODELABELS_UNSUPPORTED_TYPE =
|
||||||
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
|
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
|
||||||
|
ER_TARGET_HAS_NA =
|
||||||
|
'Целевой столбец "{0}" содержит NA!!Target column "{0}" contains NA values';
|
||||||
|
|
||||||
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
|
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
|
||||||
begin
|
begin
|
||||||
|
|
@ -89,6 +91,11 @@ begin
|
||||||
|
|
||||||
if not Self.IsCategorical(target) then
|
if not Self.IsCategorical(target) then
|
||||||
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
||||||
|
|
||||||
|
var col := Self[target];
|
||||||
|
for var i := 0 to Self.RowCount - 1 do
|
||||||
|
if not col.IsValid[i] then
|
||||||
|
ArgumentError(ER_TARGET_HAS_NA, target);
|
||||||
|
|
||||||
if Self.GetColumnType(target) = ColumnType.ctStr then
|
if Self.GetColumnType(target) = ColumnType.ctStr then
|
||||||
begin
|
begin
|
||||||
|
|
@ -130,15 +137,18 @@ begin
|
||||||
|
|
||||||
if not Self.IsCategorical(target) then
|
if not Self.IsCategorical(target) then
|
||||||
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
||||||
|
|
||||||
|
var col := Self[target];
|
||||||
|
for var i := 0 to Self.RowCount - 1 do
|
||||||
|
if not col.IsValid[i] then
|
||||||
|
ArgumentError(ER_TARGET_HAS_NA, target);
|
||||||
|
|
||||||
case Self.GetColumnType(target) of
|
case Self.GetColumnType(target) of
|
||||||
|
|
||||||
ColumnType.ctStr:
|
ColumnType.ctStr:
|
||||||
begin
|
begin
|
||||||
var labels := Self.GetStrColumn(target).ToArray;
|
var labels := Self.GetStrColumn(target).ToArray;
|
||||||
Result := EncodeLabels(labels, classes);
|
Result := EncodeLabels(labels, classes);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ColumnType.ctInt:
|
ColumnType.ctInt:
|
||||||
begin
|
begin
|
||||||
var labels := Self.GetIntColumn(target).ToArray;
|
var labels := Self.GetIntColumn(target).ToArray;
|
||||||
|
|
@ -149,7 +159,6 @@ begin
|
||||||
// если API требует string classes:
|
// если API требует string classes:
|
||||||
classes := intClasses.Select(x -> x.ToString).ToArray;
|
classes := intClasses.Select(x -> x.ToString).ToArray;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
else
|
else
|
||||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||||
end;
|
end;
|
||||||
|
|
@ -176,15 +185,18 @@ begin
|
||||||
|
|
||||||
if not Self.IsCategorical(target) then
|
if not Self.IsCategorical(target) then
|
||||||
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
||||||
|
|
||||||
|
var col := Self[target];
|
||||||
|
for var i := 0 to Self.RowCount - 1 do
|
||||||
|
if not col.IsValid[i] then
|
||||||
|
ArgumentError(ER_TARGET_HAS_NA, target);
|
||||||
|
|
||||||
case Self.GetColumnType(target) of
|
case Self.GetColumnType(target) of
|
||||||
|
|
||||||
ColumnType.ctStr:
|
ColumnType.ctStr:
|
||||||
begin
|
begin
|
||||||
var data := Self.GetStrColumn(target).ToArray;
|
var data := Self.GetStrColumn(target).ToArray;
|
||||||
Result := TransformLabels(data, classes);
|
Result := TransformLabels(data, classes);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ColumnType.ctInt:
|
ColumnType.ctInt:
|
||||||
begin
|
begin
|
||||||
var data := Self.GetIntColumn(target).ToArray;
|
var data := Self.GetIntColumn(target).ToArray;
|
||||||
|
|
@ -195,7 +207,6 @@ begin
|
||||||
|
|
||||||
Result := TransformLabels(strData, classes);
|
Result := TransformLabels(strData, classes);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
else
|
else
|
||||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||||
end;
|
end;
|
||||||
|
|
@ -224,6 +235,11 @@ begin
|
||||||
|
|
||||||
if Self.GetColumnType(target) <> ColumnType.ctInt then
|
if Self.GetColumnType(target) <> ColumnType.ctInt then
|
||||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||||
|
|
||||||
|
var col := Self[target];
|
||||||
|
for var i := 0 to Self.RowCount - 1 do
|
||||||
|
if not col.IsValid[i] then
|
||||||
|
ArgumentError(ER_TARGET_HAS_NA, target);
|
||||||
|
|
||||||
var data := Self.GetIntColumn(target).ToArray;
|
var data := Self.GetIntColumn(target).ToArray;
|
||||||
|
|
||||||
|
|
@ -253,6 +269,11 @@ begin
|
||||||
if Self.GetColumnType(target) <> ColumnType.ctInt then
|
if Self.GetColumnType(target) <> ColumnType.ctInt then
|
||||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||||
|
|
||||||
|
var col := Self[target];
|
||||||
|
for var i := 0 to Self.RowCount - 1 do
|
||||||
|
if not col.IsValid[i] then
|
||||||
|
ArgumentError(ER_TARGET_HAS_NA, target);
|
||||||
|
|
||||||
var labels := Self.GetIntColumn(target).ToArray;
|
var labels := Self.GetIntColumn(target).ToArray;
|
||||||
|
|
||||||
Result := EncodeLabelsInt(labels, classes);
|
Result := EncodeLabelsInt(labels, classes);
|
||||||
|
|
|
||||||
|
|
@ -49,24 +49,14 @@ type
|
||||||
|
|
||||||
procedure RebuildSchema;
|
procedure RebuildSchema;
|
||||||
|
|
||||||
// Join методы
|
|
||||||
|
|
||||||
//procedure AppendJoinedRow(leftCur, rightCur: DataFrameCursor; leftKeyIdx, rightKeyIdx: array of integer);
|
|
||||||
//procedure AppendLeftOnlyRow(leftCur: DataFrameCursor; leftKeyIdx, rightKeyIdx: array of integer);
|
|
||||||
//procedure AppendRightOnlyRow(rightCur: DataFrameCursor; leftKeyIdx, rightKeyIdx: array of integer; leftColumnCount: integer);
|
|
||||||
|
|
||||||
// Single key методы
|
// Single key методы
|
||||||
{function DataFrame.JoinInnerSingleKey(other: DataFrame; leftKey, rightKey: integer;
|
|
||||||
resultSchema: DataFrameSchema): DataFrame;}
|
|
||||||
function JoinInnerSingleKey(other: DataFrame; key: string): DataFrame;
|
function JoinInnerSingleKey(other: DataFrame; key: string): DataFrame;
|
||||||
function JoinInnerSingleKeyInt(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
function JoinInnerSingleKeyInt(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||||
function JoinInnerSingleKeyFloat(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
|
||||||
function JoinInnerSingleKeyStr(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
function JoinInnerSingleKeyStr(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||||
function JoinInnerSingleKeyBool(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
function JoinInnerSingleKeyBool(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||||
|
|
||||||
function LeftJoinSingleKey(other: DataFrame; key: string): DataFrame;
|
function LeftJoinSingleKey(other: DataFrame; key: string): DataFrame;
|
||||||
function LeftJoinSingleKeyInt(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
function LeftJoinSingleKeyInt(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||||
function LeftJoinSingleKeyFloat(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
|
||||||
function LeftJoinSingleKeyStr(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
function LeftJoinSingleKeyStr(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||||
function LeftJoinSingleKeyBool(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
function LeftJoinSingleKeyBool(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||||
|
|
||||||
|
|
@ -96,7 +86,6 @@ type
|
||||||
|
|
||||||
function CreateEmptyBySchema(schema: DataFrameSchema): DataFrame;
|
function CreateEmptyBySchema(schema: DataFrameSchema): DataFrame;
|
||||||
|
|
||||||
function GetColumnIndex(name: string): integer;
|
|
||||||
function GetColumn(name: string): Column;
|
function GetColumn(name: string): Column;
|
||||||
|
|
||||||
function CloneWithCopiedColumns: DataFrame;
|
function CloneWithCopiedColumns: DataFrame;
|
||||||
|
|
@ -713,43 +702,6 @@ begin
|
||||||
res.AddIntColumn(name, data, valid);
|
res.AddIntColumn(name, data, valid);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BuildMergedFloatKeyColumnFromFullJoin(
|
|
||||||
res: DataFrame;
|
|
||||||
name: string;
|
|
||||||
leftCol: FloatColumn;
|
|
||||||
rightCol: FloatColumn;
|
|
||||||
leftIdx, rightIdx: array of integer
|
|
||||||
);
|
|
||||||
begin
|
|
||||||
var n := leftIdx.Length;
|
|
||||||
var data := new real[n];
|
|
||||||
var valid := new boolean[n];
|
|
||||||
|
|
||||||
for var i := 0 to n - 1 do
|
|
||||||
begin
|
|
||||||
var li := leftIdx[i];
|
|
||||||
var ri := rightIdx[i];
|
|
||||||
|
|
||||||
if li >= 0 then
|
|
||||||
begin
|
|
||||||
data[i] := leftCol.Data[li];
|
|
||||||
valid[i] := leftCol.IsValid[li];
|
|
||||||
end
|
|
||||||
else if ri >= 0 then
|
|
||||||
begin
|
|
||||||
data[i] := rightCol.Data[ri];
|
|
||||||
valid[i] := rightCol.IsValid[ri];
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
data[i] := 0.0;
|
|
||||||
valid[i] := False;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
res.AddFloatColumn(name, data, valid);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure BuildMergedStrKeyColumnFromFullJoin(
|
procedure BuildMergedStrKeyColumnFromFullJoin(
|
||||||
res: DataFrame;
|
res: DataFrame;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -1032,23 +984,21 @@ function DataFrame.BuildJoinKey(cur: DataFrameCursor; layout: JoinKeyLayout; var
|
||||||
begin
|
begin
|
||||||
hasNA := false;
|
hasNA := false;
|
||||||
|
|
||||||
var ic := 0; var fc := 0; var sc := 0; var bc := 0;
|
var ic := 0; var sc := 0; var bc := 0;
|
||||||
|
|
||||||
// считаем размеры
|
// считаем размеры
|
||||||
for var i := 0 to layout.ColTypes.Length - 1 do
|
for var i := 0 to layout.ColTypes.Length - 1 do
|
||||||
case layout.ColTypes[i] of
|
case layout.ColTypes[i] of
|
||||||
ctInt: inc(ic);
|
ctInt: inc(ic);
|
||||||
ctFloat: inc(fc);
|
|
||||||
ctStr: inc(sc);
|
ctStr: inc(sc);
|
||||||
ctBool: inc(bc);
|
ctBool: inc(bc);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result.Ints := new integer[ic];
|
Result.Ints := new integer[ic];
|
||||||
Result.Floats := new real[fc];
|
|
||||||
Result.Strs := new string[sc];
|
Result.Strs := new string[sc];
|
||||||
Result.Bools := new boolean[bc];
|
Result.Bools := new boolean[bc];
|
||||||
|
|
||||||
ic := 0; fc := 0; sc := 0; bc := 0;
|
ic := 0; sc := 0; bc := 0;
|
||||||
|
|
||||||
for var i := 0 to layout.ColIndices.Length - 1 do
|
for var i := 0 to layout.ColIndices.Length - 1 do
|
||||||
begin
|
begin
|
||||||
|
|
@ -1066,10 +1016,7 @@ begin
|
||||||
inc(ic);
|
inc(ic);
|
||||||
end;
|
end;
|
||||||
ctFloat:
|
ctFloat:
|
||||||
begin
|
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||||
Result.Floats[fc] := cur.Float(col);
|
|
||||||
inc(fc);
|
|
||||||
end;
|
|
||||||
ctStr:
|
ctStr:
|
||||||
begin
|
begin
|
||||||
Result.Strs[sc] := cur.Str(col);
|
Result.Strs[sc] := cur.Str(col);
|
||||||
|
|
@ -1080,6 +1027,8 @@ begin
|
||||||
Result.Bools[bc] := cur.Bool(col);
|
Result.Bools[bc] := cur.Bool(col);
|
||||||
inc(bc);
|
inc(bc);
|
||||||
end;
|
end;
|
||||||
|
else
|
||||||
|
Error(ER_UNSUPPORTED_COLUMN_TYPE, layout.ColTypes[i]);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
@ -1104,23 +1053,24 @@ end;
|
||||||
|
|
||||||
function DataFrame.LeftJoinSingleKey(other: DataFrame; key: string): DataFrame;
|
function DataFrame.LeftJoinSingleKey(other: DataFrame; key: string): DataFrame;
|
||||||
begin
|
begin
|
||||||
// 1. индексы ключей — через Schema
|
|
||||||
var leftKey := fSchema.IndexOf(key);
|
var leftKey := fSchema.IndexOf(key);
|
||||||
var rightKey := other.fSchema.IndexOf(key);
|
var rightKey := other.fSchema.IndexOf(key);
|
||||||
|
|
||||||
// 2. проверка типов ключей — через Schema
|
|
||||||
var lt := fSchema.ColumnTypeAt(leftKey);
|
var lt := fSchema.ColumnTypeAt(leftKey);
|
||||||
var rt := other.fSchema.ColumnTypeAt(rightKey);
|
var rt := other.fSchema.ColumnTypeAt(rightKey);
|
||||||
|
|
||||||
if lt <> rt then
|
if lt <> rt then
|
||||||
Error(ER_JOIN_KEY_TYPE_MISMATCH);
|
Error(ER_JOIN_KEY_TYPE_MISMATCH);
|
||||||
|
|
||||||
// 3. типоспецифичный алгоритм (КАК РАНЬШЕ)
|
if lt = ctFloat then
|
||||||
|
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||||
|
|
||||||
case lt of
|
case lt of
|
||||||
ctInt: Result := LeftJoinSingleKeyInt(other, leftKey, rightKey);
|
ctInt: Result := LeftJoinSingleKeyInt(other, leftKey, rightKey);
|
||||||
ctFloat: Result := LeftJoinSingleKeyFloat(other, leftKey, rightKey);
|
|
||||||
ctStr: Result := LeftJoinSingleKeyStr(other, leftKey, rightKey);
|
ctStr: Result := LeftJoinSingleKeyStr(other, leftKey, rightKey);
|
||||||
ctBool: Result := LeftJoinSingleKeyBool(other, leftKey, rightKey);
|
ctBool: Result := LeftJoinSingleKeyBool(other, leftKey, rightKey);
|
||||||
|
else
|
||||||
|
Error(ER_UNSUPPORTED_COLUMN_TYPE, lt);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -1177,62 +1127,6 @@ begin
|
||||||
Result := BuildLeftJoinResult(Self, other, leftIdx, rightIdx, leftKey, rightKey);
|
Result := BuildLeftJoinResult(Self, other, leftIdx, rightIdx, leftKey, rightKey);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DataFrame.LeftJoinSingleKeyFloat(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
|
||||||
begin
|
|
||||||
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
|
||||||
Result := nil;
|
|
||||||
|
|
||||||
{var index := new Dictionary<real, List<integer>>;
|
|
||||||
|
|
||||||
var rcur := other.GetCursor;
|
|
||||||
while rcur.MoveNext do
|
|
||||||
if rcur.IsValid(rightKey) then
|
|
||||||
begin
|
|
||||||
var k := rcur.Float(rightKey);
|
|
||||||
|
|
||||||
var lst: List<integer>;
|
|
||||||
if not index.TryGetValue(k, lst) then
|
|
||||||
begin
|
|
||||||
lst := new List<integer>;
|
|
||||||
index[k] := lst;
|
|
||||||
end;
|
|
||||||
|
|
||||||
lst.Add(rcur.Position);
|
|
||||||
end;
|
|
||||||
|
|
||||||
var leftIdx := new List<integer>;
|
|
||||||
var rightIdx := new List<integer>;
|
|
||||||
|
|
||||||
var lcur := GetCursor;
|
|
||||||
while lcur.MoveNext do
|
|
||||||
begin
|
|
||||||
var lpos := lcur.Position;
|
|
||||||
|
|
||||||
if not lcur.IsValid(leftKey) then
|
|
||||||
begin
|
|
||||||
leftIdx.Add(lpos);
|
|
||||||
rightIdx.Add(-1);
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var k := lcur.Float(leftKey);
|
|
||||||
|
|
||||||
if index.ContainsKey(k) then
|
|
||||||
foreach var r in index[k] do
|
|
||||||
begin
|
|
||||||
leftIdx.Add(lpos);
|
|
||||||
rightIdx.Add(r);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
leftIdx.Add(lpos);
|
|
||||||
rightIdx.Add(-1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
Result := BuildLeftJoinResult(Self, other, leftIdx, rightIdx, leftKey, rightKey);}
|
|
||||||
end;
|
|
||||||
|
|
||||||
function DataFrame.LeftJoinSingleKeyStr(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
function DataFrame.LeftJoinSingleKeyStr(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||||
begin
|
begin
|
||||||
var index := new Dictionary<string, List<integer>>;
|
var index := new Dictionary<string, List<integer>>;
|
||||||
|
|
@ -1540,14 +1434,7 @@ begin
|
||||||
);
|
);
|
||||||
|
|
||||||
ctFloat:
|
ctFloat:
|
||||||
BuildMergedFloatKeyColumnFromFullJoin(
|
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||||
res,
|
|
||||||
name,
|
|
||||||
FloatColumn(col),
|
|
||||||
FloatColumn(other.columns[ri]),
|
|
||||||
leftArr,
|
|
||||||
rightArr
|
|
||||||
);
|
|
||||||
|
|
||||||
ctStr:
|
ctStr:
|
||||||
BuildMergedStrKeyColumnFromFullJoin(
|
BuildMergedStrKeyColumnFromFullJoin(
|
||||||
|
|
@ -1685,15 +1572,7 @@ begin
|
||||||
IntColumn(other.columns[ri]),
|
IntColumn(other.columns[ri]),
|
||||||
leftArr, rightArr
|
leftArr, rightArr
|
||||||
);
|
);
|
||||||
|
|
||||||
ctFloat:
|
|
||||||
BuildMergedFloatKeyColumnFromFullJoin(
|
|
||||||
res, name,
|
|
||||||
FloatColumn(col),
|
|
||||||
FloatColumn(other.columns[ri]),
|
|
||||||
leftArr, rightArr
|
|
||||||
);
|
|
||||||
|
|
||||||
ctStr:
|
ctStr:
|
||||||
BuildMergedStrKeyColumnFromFullJoin(
|
BuildMergedStrKeyColumnFromFullJoin(
|
||||||
res, name,
|
res, name,
|
||||||
|
|
@ -1740,11 +1619,15 @@ begin
|
||||||
if lt <> rt then
|
if lt <> rt then
|
||||||
Error(ER_JOIN_KEY_TYPE_MISMATCH);
|
Error(ER_JOIN_KEY_TYPE_MISMATCH);
|
||||||
|
|
||||||
|
if lt = ctFloat then
|
||||||
|
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||||
|
|
||||||
case lt of
|
case lt of
|
||||||
ctInt: Result := JoinInnerSingleKeyInt(other, leftKey, rightKey);
|
ctInt: Result := JoinInnerSingleKeyInt(other, leftKey, rightKey);
|
||||||
ctFloat: Result := JoinInnerSingleKeyFloat(other, leftKey, rightKey);
|
|
||||||
ctStr: Result := JoinInnerSingleKeyStr(other, leftKey, rightKey);
|
ctStr: Result := JoinInnerSingleKeyStr(other, leftKey, rightKey);
|
||||||
ctBool: Result := JoinInnerSingleKeyBool(other, leftKey, rightKey);
|
ctBool: Result := JoinInnerSingleKeyBool(other, leftKey, rightKey);
|
||||||
|
else
|
||||||
|
Error(ER_UNSUPPORTED_COLUMN_TYPE, lt);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -1823,49 +1706,6 @@ begin
|
||||||
Result := BuildJoinResult(Self, other, leftIdx, rightIdx, leftKey, rightKey);
|
Result := BuildJoinResult(Self, other, leftIdx, rightIdx, leftKey, rightKey);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DataFrame.JoinInnerSingleKeyFloat(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
|
||||||
begin
|
|
||||||
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
|
||||||
Result := nil;
|
|
||||||
{var index := new Dictionary<real, List<integer>>;
|
|
||||||
|
|
||||||
var rcur := other.GetCursor;
|
|
||||||
while rcur.MoveNext do
|
|
||||||
if rcur.IsValid(rightKey) then
|
|
||||||
begin
|
|
||||||
var k := rcur.Float(rightKey);
|
|
||||||
|
|
||||||
var lst: List<integer>;
|
|
||||||
if not index.TryGetValue(k, lst) then
|
|
||||||
begin
|
|
||||||
lst := new List<integer>;
|
|
||||||
index[k] := lst;
|
|
||||||
end;
|
|
||||||
|
|
||||||
lst.Add(rcur.Position);
|
|
||||||
end;
|
|
||||||
|
|
||||||
var leftIdx := new List<integer>;
|
|
||||||
var rightIdx := new List<integer>;
|
|
||||||
|
|
||||||
var lcur := GetCursor;
|
|
||||||
while lcur.MoveNext do
|
|
||||||
if lcur.IsValid(leftKey) then
|
|
||||||
begin
|
|
||||||
var k := lcur.Float(leftKey);
|
|
||||||
|
|
||||||
var rows: List<integer>;
|
|
||||||
if index.TryGetValue(k, rows) then
|
|
||||||
foreach var r in rows do
|
|
||||||
begin
|
|
||||||
leftIdx.Add(lcur.Position);
|
|
||||||
rightIdx.Add(r);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
Result := BuildJoinResult(Self, other, leftIdx, rightIdx, leftKey, rightKey);}
|
|
||||||
end;
|
|
||||||
|
|
||||||
function DataFrame.JoinInnerSingleKeyStr(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
function DataFrame.JoinInnerSingleKeyStr(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||||
begin
|
begin
|
||||||
var index := new Dictionary<string, List<integer>>;
|
var index := new Dictionary<string, List<integer>>;
|
||||||
|
|
@ -2028,9 +1868,17 @@ end;
|
||||||
function DataFrame.BuildJoinKeyLayout(keyIndices: array of integer): JoinKeyLayout;
|
function DataFrame.BuildJoinKeyLayout(keyIndices: array of integer): JoinKeyLayout;
|
||||||
begin
|
begin
|
||||||
Result.ColIndices := keyIndices;
|
Result.ColIndices := keyIndices;
|
||||||
Result.ColTypes := ArrGen(keyIndices.Length,
|
Result.ColTypes := new ColumnType[keyIndices.Length];
|
||||||
i -> columns[keyIndices[i]].Info.ColType
|
|
||||||
);
|
for var i := 0 to keyIndices.Length - 1 do
|
||||||
|
begin
|
||||||
|
var t := columns[keyIndices[i]].Info.ColType;
|
||||||
|
|
||||||
|
if t = ctFloat then
|
||||||
|
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||||
|
|
||||||
|
Result.ColTypes[i] := t;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DataFrame.Join(other: DataFrame; keys: array of string; kind: JoinKind): DataFrame;
|
function DataFrame.Join(other: DataFrame; keys: array of string; kind: JoinKind): DataFrame;
|
||||||
|
|
@ -2131,10 +1979,7 @@ function DataFrame.ColumnCount: integer := columns.Count;
|
||||||
|
|
||||||
function DataFrame.ColumnIndex(name: string): integer;
|
function DataFrame.ColumnIndex(name: string): integer;
|
||||||
begin
|
begin
|
||||||
for var i := 0 to columns.Count - 1 do
|
Result := fSchema.IndexOf(name);
|
||||||
if columns[i].Info.Name = name then
|
|
||||||
exit(i);
|
|
||||||
Error(ER_COLUMN_NOT_FOUND, name);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DataFrame.HasColumn(name: string): boolean;
|
function DataFrame.HasColumn(name: string): boolean;
|
||||||
|
|
@ -3121,12 +2966,7 @@ begin
|
||||||
rightIdx[i] := right.fSchema.IndexOf(rightKeys[i]);
|
rightIdx[i] := right.fSchema.IndexOf(rightKeys[i]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result := BuildJoinSchema(
|
Result := BuildJoinSchema(right, leftIdx, rightIdx, 'right_');
|
||||||
right,
|
|
||||||
leftIdx,
|
|
||||||
rightIdx,
|
|
||||||
'right_'
|
|
||||||
);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DataFrame.CreateEmptyBySchema(schema: DataFrameSchema): DataFrame;
|
function DataFrame.CreateEmptyBySchema(schema: DataFrameSchema): DataFrame;
|
||||||
|
|
@ -3150,14 +2990,9 @@ begin
|
||||||
Result := new DataFrame(cols, schema);
|
Result := new DataFrame(cols, schema);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DataFrame.GetColumnIndex(name: string): integer;
|
|
||||||
begin
|
|
||||||
Result := fschema.IndexOf(name); // если у вас есть такой метод
|
|
||||||
end;
|
|
||||||
|
|
||||||
function DataFrame.GetColumn(name: string): Column;
|
function DataFrame.GetColumn(name: string): Column;
|
||||||
begin
|
begin
|
||||||
var idx := GetColumnIndex(name);
|
var idx := ColumnIndex(name);
|
||||||
Result := columns[idx];
|
Result := columns[idx];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -3212,20 +3047,8 @@ begin
|
||||||
|
|
||||||
while cur.MoveNext do
|
while cur.MoveNext do
|
||||||
begin
|
begin
|
||||||
try
|
data[i] := f(cur);
|
||||||
data[i] := f(cur);
|
|
||||||
except
|
|
||||||
on e: Exception do
|
|
||||||
begin
|
|
||||||
data[i] := 0;
|
|
||||||
valid[i] := False;
|
|
||||||
i += 1;
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
valid[i] := True;
|
valid[i] := True;
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -3251,20 +3074,8 @@ begin
|
||||||
|
|
||||||
while cur.MoveNext do
|
while cur.MoveNext do
|
||||||
begin
|
begin
|
||||||
try
|
data[i] := f(cur);
|
||||||
data[i] := f(cur);
|
|
||||||
except
|
|
||||||
on e: Exception do
|
|
||||||
begin
|
|
||||||
data[i] := 0.0;
|
|
||||||
valid[i] := False;
|
|
||||||
i += 1;
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
valid[i] := True;
|
valid[i] := True;
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -3290,20 +3101,8 @@ begin
|
||||||
|
|
||||||
while cur.MoveNext do
|
while cur.MoveNext do
|
||||||
begin
|
begin
|
||||||
try
|
data[i] := f(cur);
|
||||||
data[i] := f(cur);
|
|
||||||
except
|
|
||||||
on e: Exception do
|
|
||||||
begin
|
|
||||||
data[i] := nil;
|
|
||||||
valid[i] := False;
|
|
||||||
i += 1;
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
valid[i] := True;
|
valid[i] := True;
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -3329,20 +3128,8 @@ begin
|
||||||
|
|
||||||
while cur.MoveNext do
|
while cur.MoveNext do
|
||||||
begin
|
begin
|
||||||
try
|
data[i] := f(cur);
|
||||||
data[i] := f(cur);
|
|
||||||
except
|
|
||||||
on e: Exception do
|
|
||||||
begin
|
|
||||||
data[i] := False;
|
|
||||||
valid[i] := False;
|
|
||||||
i += 1;
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
valid[i] := True;
|
valid[i] := True;
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -3403,20 +3190,8 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- вычисление
|
// --- вычисление
|
||||||
try
|
data[row] := f(cur);
|
||||||
data[row] := f(cur);
|
|
||||||
except
|
|
||||||
on e: Exception do
|
|
||||||
begin
|
|
||||||
data[row] := 0.0;
|
|
||||||
valid[row] := False;
|
|
||||||
row += 1;
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
valid[row] := True;
|
valid[row] := True;
|
||||||
|
|
||||||
row += 1;
|
row += 1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -3455,18 +3230,22 @@ begin
|
||||||
|
|
||||||
var cur := GetCursor;
|
var cur := GetCursor;
|
||||||
var row := 0;
|
var row := 0;
|
||||||
|
|
||||||
while cur.MoveNext do
|
while cur.MoveNext do
|
||||||
begin
|
begin
|
||||||
try
|
// NA из исходных данных
|
||||||
data[row] := f(cur);
|
if not cur.IsValid(colIndex) then
|
||||||
valid[row] := True;
|
begin
|
||||||
except
|
data[row] := 0;
|
||||||
on e: Exception do
|
valid[row] := false;
|
||||||
begin
|
row += 1;
|
||||||
data[row] := 0;
|
continue;
|
||||||
valid[row] := False;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// строгий расчёт
|
||||||
|
data[row] := f(cur);
|
||||||
|
valid[row] := True;
|
||||||
|
|
||||||
row += 1;
|
row += 1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -4117,6 +3896,8 @@ end;
|
||||||
procedure DataFrame.AssertSchemaConsistent;
|
procedure DataFrame.AssertSchemaConsistent;
|
||||||
begin
|
begin
|
||||||
{$IFNDEF Test}
|
{$IFNDEF Test}
|
||||||
|
|
||||||
|
if columns.Count = 0 then exit;
|
||||||
|
|
||||||
// --- 1. одинаковая RowCount у всех столбцов ---
|
// --- 1. одинаковая RowCount у всех столбцов ---
|
||||||
var rc := columns[0].RowCount;
|
var rc := columns[0].RowCount;
|
||||||
|
|
@ -4136,7 +3917,7 @@ begin
|
||||||
if not fSchema.HasColumn(name) then
|
if not fSchema.HasColumn(name) then
|
||||||
Error(ER_SCHEMA_COLUMN_MISSING, name);
|
Error(ER_SCHEMA_COLUMN_MISSING, name);
|
||||||
|
|
||||||
var idx := GetColumnIndex(name);
|
var idx := ColumnIndex(name);
|
||||||
if idx <> i then
|
if idx <> i then
|
||||||
Error(ER_SCHEMA_COLUMN_INDEX_INCONSISTENT, name, idx, i);
|
Error(ER_SCHEMA_COLUMN_INDEX_INCONSISTENT, name, idx, i);
|
||||||
end;
|
end;
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ type
|
||||||
Column = abstract class
|
Column = abstract class
|
||||||
Info: ColumnInfo;
|
Info: ColumnInfo;
|
||||||
public
|
public
|
||||||
|
IsValid: array of boolean; // Флаги валидности (может быть nil)
|
||||||
/// Пытается извлечь i-тое данное из столбца как числовое если это возможно
|
/// Пытается извлечь i-тое данное из столбца как числовое если это возможно
|
||||||
function TryGetNumericValue(i: integer; var value: real): boolean; virtual; abstract;
|
function TryGetNumericValue(i: integer; var value: real): boolean; virtual; abstract;
|
||||||
/// Возвращает количество строк в столбце
|
/// Возвращает количество строк в столбце
|
||||||
|
|
@ -108,7 +109,6 @@ type
|
||||||
IntColumn = class(Column)
|
IntColumn = class(Column)
|
||||||
// Data и IsValid считаются immutable после создания
|
// Data и IsValid считаются immutable после создания
|
||||||
Data: array of integer; // Данные столбца
|
Data: array of integer; // Данные столбца
|
||||||
IsValid: array of boolean; // Флаги валидности (может быть nil)
|
|
||||||
public
|
public
|
||||||
constructor Create; begin end;
|
constructor Create; begin end;
|
||||||
constructor Create(name: string);
|
constructor Create(name: string);
|
||||||
|
|
@ -121,7 +121,6 @@ type
|
||||||
/// Столбец вещественных чисел
|
/// Столбец вещественных чисел
|
||||||
FloatColumn = class(Column)
|
FloatColumn = class(Column)
|
||||||
Data: array of real; // Данные столбца
|
Data: array of real; // Данные столбца
|
||||||
IsValid: array of boolean; // Флаги валидности
|
|
||||||
public
|
public
|
||||||
constructor Create; begin end;
|
constructor Create; begin end;
|
||||||
constructor Create(name: string);
|
constructor Create(name: string);
|
||||||
|
|
@ -135,7 +134,6 @@ type
|
||||||
/// Столбец строк
|
/// Столбец строк
|
||||||
StrColumn = class(Column)
|
StrColumn = class(Column)
|
||||||
Data: array of string; // Данные столбца
|
Data: array of string; // Данные столбца
|
||||||
IsValid: array of boolean; // Флаги валидности
|
|
||||||
public
|
public
|
||||||
constructor Create; begin end;
|
constructor Create; begin end;
|
||||||
constructor Create(name: string);
|
constructor Create(name: string);
|
||||||
|
|
@ -153,7 +151,6 @@ type
|
||||||
/// Столбец булевых значений
|
/// Столбец булевых значений
|
||||||
BoolColumn = class(Column)
|
BoolColumn = class(Column)
|
||||||
Data: array of boolean; // Данные столбца
|
Data: array of boolean; // Данные столбца
|
||||||
IsValid: array of boolean; // Флаги валидности
|
|
||||||
public
|
public
|
||||||
constructor Create; begin end;
|
constructor Create; begin end;
|
||||||
constructor Create(name: string);
|
constructor Create(name: string);
|
||||||
|
|
@ -177,9 +174,9 @@ type
|
||||||
ColTypes: array of ColumnType;
|
ColTypes: array of ColumnType;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Нет Floats - по ним нельзя Join!!!
|
||||||
JoinKey = record
|
JoinKey = record
|
||||||
Ints: array of integer;
|
Ints: array of integer;
|
||||||
Floats: array of real;
|
|
||||||
Strs: array of string;
|
Strs: array of string;
|
||||||
Bools: array of boolean;
|
Bools: array of boolean;
|
||||||
function Equals(oth: object): boolean; override;
|
function Equals(oth: object): boolean; override;
|
||||||
|
|
@ -731,16 +728,12 @@ begin
|
||||||
var other := JoinKey(oth);
|
var other := JoinKey(oth);
|
||||||
|
|
||||||
if Ints.Length <> other.Ints.Length then exit(false);
|
if Ints.Length <> other.Ints.Length then exit(false);
|
||||||
if Floats.Length <> other.Floats.Length then exit(false);
|
|
||||||
if Strs.Length <> other.Strs.Length then exit(false);
|
if Strs.Length <> other.Strs.Length then exit(false);
|
||||||
if Bools.Length <> other.Bools.Length then exit(false);
|
if Bools.Length <> other.Bools.Length then exit(false);
|
||||||
|
|
||||||
for var i := 0 to Ints.Length - 1 do
|
for var i := 0 to Ints.Length - 1 do
|
||||||
if Ints[i] <> other.Ints[i] then exit(false);
|
if Ints[i] <> other.Ints[i] then exit(false);
|
||||||
|
|
||||||
for var i := 0 to Floats.Length - 1 do
|
|
||||||
if Floats[i] <> other.Floats[i] then exit(false);
|
|
||||||
|
|
||||||
for var i := 0 to Strs.Length - 1 do
|
for var i := 0 to Strs.Length - 1 do
|
||||||
if Strs[i] <> other.Strs[i] then exit(false);
|
if Strs[i] <> other.Strs[i] then exit(false);
|
||||||
|
|
||||||
|
|
@ -757,9 +750,6 @@ begin
|
||||||
foreach var v in Ints do
|
foreach var v in Ints do
|
||||||
h := h * 31 + v.GetHashCode;
|
h := h * 31 + v.GetHashCode;
|
||||||
|
|
||||||
foreach var v in Floats do
|
|
||||||
h := h * 31 + v.GetHashCode;
|
|
||||||
|
|
||||||
foreach var v in Strs do
|
foreach var v in Strs do
|
||||||
h := h * 31 + (if v = nil then 0 else v.GetHashCode);
|
h := h * 31 + (if v = nil then 0 else v.GetHashCode);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
// =============================================================
|
/// Основной модуль библиотеки машинного обучения.
|
||||||
|
/// Объединяет модели, метрики, валидацию и вспомогательные компоненты.
|
||||||
|
unit MLABC;
|
||||||
|
|
||||||
|
// =============================================================
|
||||||
// СТАТИСТИЧЕСКАЯ ПОЛИТИКА БИБЛИОТЕКИ ML PascalABC.NET
|
// СТАТИСТИЧЕСКАЯ ПОЛИТИКА БИБЛИОТЕКИ ML PascalABC.NET
|
||||||
//
|
//
|
||||||
// В библиотеке используются следующие соглашения:
|
// В библиотеке используются следующие соглашения:
|
||||||
|
|
@ -17,9 +21,20 @@
|
||||||
// • алгоритмы ML — дисперсия генеральной совокупности
|
// • алгоритмы ML — дисперсия генеральной совокупности
|
||||||
// =============================================================
|
// =============================================================
|
||||||
|
|
||||||
/// Основной модуль библиотеки машинного обучения.
|
// =============================================================
|
||||||
/// Объединяет модели, метрики, валидацию и вспомогательные компоненты.
|
// PIPELINES
|
||||||
unit MLABC;
|
//
|
||||||
|
// DataFrame-based:
|
||||||
|
// DataPipeline
|
||||||
|
// UDataPipeline
|
||||||
|
//
|
||||||
|
// Matrix/Vector-based:
|
||||||
|
// MatrixPipeline
|
||||||
|
// UMatrixPipeline
|
||||||
|
//
|
||||||
|
// Оба варианта являются равноправными и используются
|
||||||
|
// в зависимости от представления данных.
|
||||||
|
// =============================================================
|
||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
|
|
@ -74,7 +89,7 @@ type
|
||||||
NormType = MLModelsABC.NormType;
|
NormType = MLModelsABC.NormType;
|
||||||
|
|
||||||
Activations = MLModelsABC.Activations;
|
Activations = MLModelsABC.Activations;
|
||||||
Pipeline = MLModelsABC.Pipeline;
|
Pipeline = MLModelsABC.MatrixPipeline;
|
||||||
|
|
||||||
LinearRegression = MLModelsABC.LinearRegression;
|
LinearRegression = MLModelsABC.LinearRegression;
|
||||||
LogisticRegression = MLModelsABC.LogisticRegression;
|
LogisticRegression = MLModelsABC.LogisticRegression;
|
||||||
|
|
@ -113,7 +128,7 @@ type
|
||||||
IModel = MLCoreABC.IModel;
|
IModel = MLCoreABC.IModel;
|
||||||
ISupervisedModel = MLCoreABC.ISupervisedModel;
|
ISupervisedModel = MLCoreABC.ISupervisedModel;
|
||||||
IUnsupervisedModel = MLCoreABC.IUnsupervisedModel;
|
IUnsupervisedModel = MLCoreABC.IUnsupervisedModel;
|
||||||
UPipeline = MLModelsABC.UPipeline;
|
UPipeline = MLModelsABC.UMatrixPipeline;
|
||||||
UDataPipeline = MLPipelineABC.UDataPipeline;
|
UDataPipeline = MLPipelineABC.UDataPipeline;
|
||||||
TaskKind = MLPipelineABC.TaskKind;
|
TaskKind = MLPipelineABC.TaskKind;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -355,7 +355,9 @@ type
|
||||||
fCheckConvergence: boolean;
|
fCheckConvergence: boolean;
|
||||||
fMinImprovement: real;
|
fMinImprovement: real;
|
||||||
|
|
||||||
fClassLabels: array of string; // В каждой модели классификации
|
fClassLabels: array of string;
|
||||||
|
|
||||||
|
fUseFastExp: boolean;
|
||||||
|
|
||||||
function GetWeights: Matrix;
|
function GetWeights: Matrix;
|
||||||
function GetIntercept: Vector;
|
function GetIntercept: Vector;
|
||||||
|
|
@ -369,7 +371,8 @@ type
|
||||||
epochs: integer := 1000;
|
epochs: integer := 1000;
|
||||||
tol: real := 1e-6;
|
tol: real := 1e-6;
|
||||||
checkConvergence: boolean := true;
|
checkConvergence: boolean := true;
|
||||||
minImprovement: real := 1e-8
|
minImprovement: real := 1e-8;
|
||||||
|
useFastExp: boolean := True
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Обучает модель логистической регрессии.
|
/// Обучает модель логистической регрессии.
|
||||||
|
|
@ -380,7 +383,7 @@ type
|
||||||
/// Примечание:
|
/// Примечание:
|
||||||
/// • обученное состояние модели НЕ копируется методом Clone
|
/// • обученное состояние модели НЕ копируется методом Clone
|
||||||
function Fit(X: Matrix; y: Vector): ISupervisedModel;
|
function Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||||
|
|
||||||
/// Возвращает матрицу вероятностей (m x k).
|
/// Возвращает матрицу вероятностей (m x k).
|
||||||
function PredictProba(X: Matrix): Matrix;
|
function PredictProba(X: Matrix): Matrix;
|
||||||
|
|
||||||
|
|
@ -1571,9 +1574,6 @@ type
|
||||||
fTouched: array of integer;
|
fTouched: array of integer;
|
||||||
fEpoch: integer;
|
fEpoch: integer;
|
||||||
|
|
||||||
fSampleIdx: array of integer;
|
|
||||||
fSampleSize: integer;
|
|
||||||
|
|
||||||
public
|
public
|
||||||
/// Создаёт классификатор kNN.
|
/// Создаёт классификатор kNN.
|
||||||
/// k — число ближайших соседей (k > 0).
|
/// k — число ближайших соседей (k > 0).
|
||||||
|
|
@ -1589,14 +1589,22 @@ type
|
||||||
/// • обученное состояние модели (обучающая выборка) НЕ копируется методом Clone
|
/// • обученное состояние модели (обучающая выборка) НЕ копируется методом Clone
|
||||||
function Fit(X: Matrix; y: Vector): ISupervisedModel; override;
|
function Fit(X: Matrix; y: Vector): ISupervisedModel; override;
|
||||||
|
|
||||||
/// Выполняет предсказание меток классов для объектов X.
|
/// Выполняет предсказание меток классов для объектов X.
|
||||||
/// Возвращает вектор предсказанных меток
|
/// Возвращает вектор предсказанных меток.
|
||||||
|
/// Не является потокобезопасным: не вызывать одновременно из нескольких потоков
|
||||||
|
/// для одного экземпляра модели.
|
||||||
function Predict(X: Matrix): Vector; override;
|
function Predict(X: Matrix): Vector; override;
|
||||||
|
|
||||||
|
/// Выполняет предсказание меток классов для объектов X.
|
||||||
|
/// Возвращает массив индексов классов.
|
||||||
|
/// Не является потокобезопасным: не вызывать одновременно из нескольких потоков
|
||||||
|
/// для одного экземпляра модели.
|
||||||
function PredictLabels(X: Matrix): array of integer;
|
function PredictLabels(X: Matrix): array of integer;
|
||||||
|
|
||||||
/// Возвращает матрицу вероятностей размера (nSamples × nClasses).
|
/// Возвращает матрицу вероятностей размера (nSamples × nClasses).
|
||||||
/// Столбцы соответствуют классам в порядке, возвращаемом GetClasses()
|
/// Столбцы соответствуют классам в порядке, возвращаемом GetClasses().
|
||||||
|
/// Не является потокобезопасным: не вызывать одновременно из нескольких потоков
|
||||||
|
/// для одного экземпляра модели.
|
||||||
function PredictProba(X: Matrix): Matrix;
|
function PredictProba(X: Matrix): Matrix;
|
||||||
|
|
||||||
/// Возвращает массив меток классов в порядке столбцов PredictProba
|
/// Возвращает массив меток классов в порядке столбцов PredictProba
|
||||||
|
|
@ -1839,7 +1847,7 @@ type
|
||||||
|
|
||||||
{$endregion Models}
|
{$endregion Models}
|
||||||
|
|
||||||
{$region Pipeline}
|
{$region MatrixPipeline}
|
||||||
/// Последовательный конвейер машинного обучения (supervised).
|
/// Последовательный конвейер машинного обучения (supervised).
|
||||||
/// Гарантирует строгий порядок выполнения шагов:
|
/// Гарантирует строгий порядок выполнения шагов:
|
||||||
/// [преобразователи] → [модель].
|
/// [преобразователи] → [модель].
|
||||||
|
|
@ -1854,7 +1862,10 @@ type
|
||||||
///
|
///
|
||||||
/// Обеспечивает единый интерфейс Fit(X, y) / Predict(X)
|
/// Обеспечивает единый интерфейс Fit(X, y) / Predict(X)
|
||||||
/// и воспроизводимость полного процесса обучения
|
/// и воспроизводимость полного процесса обучения
|
||||||
Pipeline = class(ISupervisedModel)
|
///
|
||||||
|
/// MatrixPipeline используется, когда данные уже представлены
|
||||||
|
/// в виде Matrix X и Vector y
|
||||||
|
MatrixPipeline = class(ISupervisedModel)
|
||||||
private
|
private
|
||||||
fTransformers: List<ITransformer>;
|
fTransformers: List<ITransformer>;
|
||||||
fModel: ISupervisedModel;
|
fModel: ISupervisedModel;
|
||||||
|
|
@ -1870,7 +1881,7 @@ type
|
||||||
constructor Create;
|
constructor Create;
|
||||||
|
|
||||||
{
|
{
|
||||||
// Pipeline.Build используется, когда данные уже представлены
|
// MatrixPipeline.Build используется, когда данные уже представлены
|
||||||
// в виде числовой матрицы признаков X и вектора целевой переменной y.
|
// в виде числовой матрицы признаков X и вектора целевой переменной y.
|
||||||
// В этом случае DataFrame и препроцессоры уровня таблицы не требуются.
|
// В этом случае DataFrame и препроцессоры уровня таблицы не требуются.
|
||||||
//
|
//
|
||||||
|
|
@ -1881,7 +1892,7 @@ type
|
||||||
// • подбор гиперпараметров
|
// • подбор гиперпараметров
|
||||||
// • тестирование моделей
|
// • тестирование моделей
|
||||||
//
|
//
|
||||||
// Pipeline объединяет несколько матричных преобразований (ITransformer)
|
// MatrixPipeline объединяет несколько матричных преобразований (ITransformer)
|
||||||
// и модель (IModel) в единый объект, который можно обучать и использовать
|
// и модель (IModel) в единый объект, который можно обучать и использовать
|
||||||
// для предсказаний.
|
// для предсказаний.
|
||||||
//
|
//
|
||||||
|
|
@ -1892,13 +1903,13 @@ type
|
||||||
// перед преобразованием данных в Matrix/Vector.
|
// перед преобразованием данных в Matrix/Vector.
|
||||||
|
|
||||||
var pipe1 :=
|
var pipe1 :=
|
||||||
Pipeline.Build(
|
MatrixPipeline.Build(
|
||||||
new StandardScaler,
|
new StandardScaler,
|
||||||
new LogisticRegression
|
new LogisticRegression
|
||||||
);
|
);
|
||||||
|
|
||||||
var pipe2 :=
|
var pipe2 :=
|
||||||
Pipeline.Build(
|
MatrixPipeline.Build(
|
||||||
new StandardScaler,
|
new StandardScaler,
|
||||||
new PCATransformer(2),
|
new PCATransformer(2),
|
||||||
new LogisticRegression
|
new LogisticRegression
|
||||||
|
|
@ -1931,13 +1942,13 @@ type
|
||||||
/// сначала преобразователи, затем модель.
|
/// сначала преобразователи, затем модель.
|
||||||
/// Последний шаг обязан быть моделью (IModel).
|
/// Последний шаг обязан быть моделью (IModel).
|
||||||
/// Возвращает сконструированный конвейер.
|
/// Возвращает сконструированный конвейер.
|
||||||
static function Build(params steps: array of IPipelineStep): Pipeline;
|
static function Build(params steps: array of IPipelineStep): MatrixPipeline;
|
||||||
|
|
||||||
/// Устанавливает или заменяет модель.
|
/// Устанавливает или заменяет модель.
|
||||||
function SetModel(m: ISupervisedModel): Pipeline;
|
function SetModel(m: ISupervisedModel): MatrixPipeline;
|
||||||
|
|
||||||
/// Добавляет преобразование в конец пайплайна
|
/// Добавляет преобразование в конец пайплайна
|
||||||
function Add(t: ITransformer): Pipeline;
|
function Add(t: ITransformer): MatrixPipeline;
|
||||||
|
|
||||||
/// Обучает пайплайн на данных.
|
/// Обучает пайплайн на данных.
|
||||||
/// X — матрица m × n (m объектов, n признаков).
|
/// X — матрица m × n (m объектов, n признаков).
|
||||||
|
|
@ -1962,6 +1973,7 @@ type
|
||||||
/// Показывает, был ли пайплайн обучен (вызван метод Fit).
|
/// Показывает, был ли пайплайн обучен (вызван метод Fit).
|
||||||
property IsFitted: boolean read fFitted;
|
property IsFitted: boolean read fFitted;
|
||||||
|
|
||||||
|
/// Возвращает строковое представление пайплайна.
|
||||||
function ToString: string; override;
|
function ToString: string; override;
|
||||||
|
|
||||||
/// Создаёт копию пайплайна с теми же шагами и моделью.
|
/// Создаёт копию пайплайна с теми же шагами и моделью.
|
||||||
|
|
@ -1979,19 +1991,48 @@ type
|
||||||
function Name: string := Self.GetType.Name;
|
function Name: string := Self.GetType.Name;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
UPipeline = class(IUnsupervisedModel)
|
/// Последовательный конвейер машинного обучения (unsupervised).
|
||||||
|
/// Гарантирует строгий порядок выполнения шагов:
|
||||||
|
/// [преобразователи].
|
||||||
|
///
|
||||||
|
/// Поддерживает:
|
||||||
|
/// • преобразователи без учёта целевой переменной (unsupervised).
|
||||||
|
///
|
||||||
|
/// Все преобразователи применяются последовательно к признакам X.
|
||||||
|
///
|
||||||
|
/// Обеспечивает единый интерфейс Fit(X) / Transform(X)
|
||||||
|
/// и воспроизводимость полного процесса преобразования данных.
|
||||||
|
///
|
||||||
|
/// UMatrixPipeline используется, когда данные уже представлены
|
||||||
|
/// в виде Matrix X и отсутствует целевая переменная
|
||||||
|
UMatrixPipeline = class(IUnsupervisedModel)
|
||||||
private
|
private
|
||||||
fTransformers: List<ITransformer>;
|
fTransformers: List<ITransformer>;
|
||||||
fModel: IModel;
|
fModel: IModel;
|
||||||
fFitted: boolean;
|
fFitted: boolean;
|
||||||
public
|
public
|
||||||
|
/// Создаёт конвейер машинного обучения для заданной модели:
|
||||||
|
/// • model — модель, которая будет применена
|
||||||
|
/// после последовательного применения всех преобразователей.
|
||||||
|
/// Модель должна реализовывать интерфейс IModel (без учёта целевой переменной)
|
||||||
constructor Create(model: IModel);
|
constructor Create(model: IModel);
|
||||||
|
|
||||||
|
/// Создаёт пустой пайплайн (конвейер машинного обучения).
|
||||||
|
/// Модель должна быть установлена через SetModel
|
||||||
constructor Create;
|
constructor Create;
|
||||||
|
|
||||||
static function Build(params steps: array of IPipelineStep): UPipeline;
|
/// Строит конвейер машинного обучения из последовательности шагов.
|
||||||
|
/// Шаги указываются в порядке выполнения:
|
||||||
|
/// сначала преобразователи, затем модель.
|
||||||
|
/// Последний шаг обязан быть моделью (IModel).
|
||||||
|
/// Возвращает сконструированный конвейер.
|
||||||
|
static function Build(params steps: array of IPipelineStep): UMatrixPipeline;
|
||||||
|
|
||||||
function SetModel(m: IModel): UPipeline;
|
/// Устанавливает или заменяет модель.
|
||||||
function Add(t: ITransformer): UPipeline;
|
function SetModel(m: IModel): UMatrixPipeline;
|
||||||
|
|
||||||
|
/// Добавляет преобразование в конец пайплайна
|
||||||
|
function Add(t: ITransformer): UMatrixPipeline;
|
||||||
|
|
||||||
/// Обучает пайплайн на данных.
|
/// Обучает пайплайн на данных.
|
||||||
/// X — матрица m × n (m объектов, n признаков).
|
/// X — матрица m × n (m объектов, n признаков).
|
||||||
|
|
@ -2001,11 +2042,30 @@ type
|
||||||
/// Примечание:
|
/// Примечание:
|
||||||
/// • обученное состояние пайплайна НЕ копируется методом Clone
|
/// • обученное состояние пайплайна НЕ копируется методом Clone
|
||||||
function Fit(X: Matrix): IUnsupervisedModel;
|
function Fit(X: Matrix): IUnsupervisedModel;
|
||||||
|
|
||||||
|
/// Применяет последовательность преобразований к данным.
|
||||||
|
/// X — матрица m × n (m объектов, n признаков).
|
||||||
|
/// Последовательно применяет все трансформеры к данным,
|
||||||
|
/// после чего возвращает преобразованную матрицу.
|
||||||
|
///
|
||||||
|
/// Примечание:
|
||||||
|
/// • обученное состояние пайплайна НЕ копируется методом Clone
|
||||||
function Transform(X: Matrix): Matrix;
|
function Transform(X: Matrix): Matrix;
|
||||||
|
|
||||||
|
/// Применяет пайплайн и возвращает результат модели.
|
||||||
|
/// X — матрица m × n (m объектов, n признаков).
|
||||||
|
/// После применения всех преобразований вызывает модель,
|
||||||
|
/// реализующую интерфейс IModel.
|
||||||
|
///
|
||||||
|
/// Примечание:
|
||||||
|
/// • семантика результата зависит от конкретной модели
|
||||||
|
/// (например, кластерные метки, оценки и т.д.)
|
||||||
function Predict(X: Matrix): Vector;
|
function Predict(X: Matrix): Vector;
|
||||||
|
|
||||||
|
/// Показывает, был ли пайплайн обучен (вызван метод Fit у шагов пайплайна).
|
||||||
property IsFitted: boolean read fFitted;
|
property IsFitted: boolean read fFitted;
|
||||||
|
|
||||||
|
/// Возвращает строковое представление пайплайна.
|
||||||
function ToString: string; override;
|
function ToString: string; override;
|
||||||
|
|
||||||
/// Создаёт копию пайплайна с теми же шагами и моделью.
|
/// Создаёт копию пайплайна с теми же шагами и моделью.
|
||||||
|
|
@ -2016,13 +2076,13 @@ type
|
||||||
/// • Все шаги и модель клонируются через их Clone (config-only)
|
/// • Все шаги и модель клонируются через их Clone (config-only)
|
||||||
///
|
///
|
||||||
/// Назначение:
|
/// Назначение:
|
||||||
/// • использование в CrossValidate и Pipeline
|
/// • использование в CrossValidate и MatrixPipeline
|
||||||
/// • обеспечивает независимое переобучение пайплайна
|
/// • обеспечивает независимое переобучение пайплайна
|
||||||
function Clone: IModel;
|
function Clone: IModel;
|
||||||
function Name: string := Self.GetType.Name;
|
function Name: string := Self.GetType.Name;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{$endregion Pipeline}
|
{$endregion MatrixPipeline}
|
||||||
|
|
||||||
{$region Transformers}
|
{$region Transformers}
|
||||||
|
|
||||||
|
|
@ -2078,7 +2138,7 @@ type
|
||||||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||||
///
|
///
|
||||||
/// Назначение:
|
/// Назначение:
|
||||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||||
/// • гарантирует независимое переобучение трансформеров
|
/// • гарантирует независимое переобучение трансформеров
|
||||||
///
|
///
|
||||||
/// Примечание:
|
/// Примечание:
|
||||||
|
|
@ -2136,7 +2196,7 @@ type
|
||||||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||||
///
|
///
|
||||||
/// Назначение:
|
/// Назначение:
|
||||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||||
/// • гарантирует независимое переобучение трансформеров
|
/// • гарантирует независимое переобучение трансформеров
|
||||||
///
|
///
|
||||||
/// Примечание:
|
/// Примечание:
|
||||||
|
|
@ -2190,7 +2250,7 @@ type
|
||||||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||||
///
|
///
|
||||||
/// Назначение:
|
/// Назначение:
|
||||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||||
/// • гарантирует независимое переобучение трансформеров
|
/// • гарантирует независимое переобучение трансформеров
|
||||||
///
|
///
|
||||||
/// Примечание:
|
/// Примечание:
|
||||||
|
|
@ -2243,7 +2303,7 @@ type
|
||||||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||||
///
|
///
|
||||||
/// Назначение:
|
/// Назначение:
|
||||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||||
/// • гарантирует независимое переобучение трансформеров
|
/// • гарантирует независимое переобучение трансформеров
|
||||||
///
|
///
|
||||||
/// Примечание:
|
/// Примечание:
|
||||||
|
|
@ -2340,7 +2400,7 @@ type
|
||||||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||||
///
|
///
|
||||||
/// Назначение:
|
/// Назначение:
|
||||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||||
/// • гарантирует независимое переобучение трансформеров
|
/// • гарантирует независимое переобучение трансформеров
|
||||||
///
|
///
|
||||||
/// Примечание:
|
/// Примечание:
|
||||||
|
|
@ -2398,7 +2458,7 @@ type
|
||||||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||||
///
|
///
|
||||||
/// Назначение:
|
/// Назначение:
|
||||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||||
/// • гарантирует независимое переобучение трансформеров
|
/// • гарантирует независимое переобучение трансформеров
|
||||||
///
|
///
|
||||||
/// Примечание:
|
/// Примечание:
|
||||||
|
|
@ -3100,7 +3160,7 @@ end;
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
||||||
constructor LogisticRegression.Create(lambda: real; learningRate: real; epochs: integer;
|
constructor LogisticRegression.Create(lambda: real; learningRate: real; epochs: integer;
|
||||||
tol: real; checkConvergence: boolean; minImprovement: real);
|
tol: real; checkConvergence: boolean; minImprovement: real; useFastExp: boolean);
|
||||||
begin
|
begin
|
||||||
fLambda := lambda;
|
fLambda := lambda;
|
||||||
fLearningRate := learningRate;
|
fLearningRate := learningRate;
|
||||||
|
|
@ -3109,8 +3169,19 @@ begin
|
||||||
fTol := tol;
|
fTol := tol;
|
||||||
fCheckConvergence := checkConvergence;
|
fCheckConvergence := checkConvergence;
|
||||||
fMinImprovement := minImprovement;
|
fMinImprovement := minImprovement;
|
||||||
|
fUseFastExp := useFastExp
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function FastExp(x: real): real;
|
||||||
|
begin
|
||||||
|
if x < -5 then exit(0.0);
|
||||||
|
if x > 5 then x := 5;
|
||||||
|
|
||||||
|
Result := 1.0 + x + 0.5*x*x + (1.0/6.0)*x*x*x;
|
||||||
|
end;
|
||||||
|
|
||||||
|
type RealArr = array of real;
|
||||||
|
|
||||||
function LogisticRegression.Fit(X: Matrix; y: Vector): ISupervisedModel;
|
function LogisticRegression.Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||||
begin
|
begin
|
||||||
if X = nil then
|
if X = nil then
|
||||||
|
|
@ -3179,6 +3250,228 @@ begin
|
||||||
fIndexToClass[i] := unique[i];
|
fIndexToClass[i] := unique[i];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// --- init
|
||||||
|
fW := new Matrix(p, fClassCount);
|
||||||
|
var scale := 0.01;
|
||||||
|
for var j := 0 to p - 1 do
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
fW.Data[j,k] := (Random - 0.5) * 2 * scale;
|
||||||
|
|
||||||
|
fIntercept := new Vector(fClassCount);
|
||||||
|
|
||||||
|
var prevLoss := real.PositiveInfinity;
|
||||||
|
|
||||||
|
var xRows := X.Data.Rows;
|
||||||
|
var gradW := new RealArr[fClassCount];
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
gradW[k] := new real[p];
|
||||||
|
|
||||||
|
var gradB := new real[fClassCount];
|
||||||
|
|
||||||
|
var zi := new real[fClassCount];
|
||||||
|
|
||||||
|
for var epoch := 1 to fEpochs do
|
||||||
|
begin
|
||||||
|
var WCols := fW.Data.Cols;
|
||||||
|
//var WRows := fW.Data.Rows;
|
||||||
|
|
||||||
|
var loss := 0.0;
|
||||||
|
|
||||||
|
// --- zero gradients
|
||||||
|
&Array.Clear(gradB, 0, fClassCount);
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
&Array.Clear(gradW[k], 0, p);
|
||||||
|
|
||||||
|
// --- one pass: logits -> softmax -> loss -> gradient
|
||||||
|
for var i := 0 to m - 1 do
|
||||||
|
begin
|
||||||
|
var xi := XRows[i];
|
||||||
|
var yi := yEncoded[i];
|
||||||
|
|
||||||
|
// --- logits
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
begin
|
||||||
|
var wk := WCols[k];
|
||||||
|
var s := fIntercept.Data[k];
|
||||||
|
|
||||||
|
for var j := 0 to p - 1 do
|
||||||
|
s += xi[j] * wk[j];
|
||||||
|
|
||||||
|
zi[k] := s;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- stable softmax
|
||||||
|
var maxVal := zi[0];
|
||||||
|
for var k := 1 to fClassCount - 1 do
|
||||||
|
if zi[k] > maxVal then
|
||||||
|
maxVal := zi[k];
|
||||||
|
|
||||||
|
var sumExp := 0.0;
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
begin
|
||||||
|
var v: real;
|
||||||
|
if fUseFastExp then
|
||||||
|
v := FastExp(zi[k] - maxVal)
|
||||||
|
else
|
||||||
|
v := Exp(zi[k] - maxVal);
|
||||||
|
zi[k] := v;
|
||||||
|
sumExp += v;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if sumExp <= 0 then
|
||||||
|
begin
|
||||||
|
var uniformProb := 1.0 / fClassCount;
|
||||||
|
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
zi[k] := uniformProb;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
var invSum := 1.0 / sumExp;
|
||||||
|
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
zi[k] *= invSum;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- loss
|
||||||
|
var prob := zi[yi];
|
||||||
|
if prob < 1e-300 then
|
||||||
|
prob := 1e-300;
|
||||||
|
|
||||||
|
loss -= Ln(prob);
|
||||||
|
|
||||||
|
// --- gradient
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
begin
|
||||||
|
var diff := zi[k] - Ord(k = yi);
|
||||||
|
|
||||||
|
gradB[k] += diff;
|
||||||
|
|
||||||
|
var gwk := gradW[k];
|
||||||
|
for var j := 0 to p - 1 do
|
||||||
|
gwk[j] += xi[j] * diff;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
loss /= m;
|
||||||
|
|
||||||
|
// --- L2 penalty
|
||||||
|
if fLambda <> 0 then
|
||||||
|
begin
|
||||||
|
var l2 := 0.0;
|
||||||
|
for var j := 0 to p - 1 do
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
l2 += fW.Data[j,k] * fW.Data[j,k];
|
||||||
|
|
||||||
|
loss += 0.5 * fLambda * l2;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- divergence check
|
||||||
|
if double.IsNaN(loss) or double.IsInfinity(loss) then
|
||||||
|
ArgumentError(ER_LOGISTIC_INVALID_LOSS);
|
||||||
|
|
||||||
|
// --- convergence check
|
||||||
|
if fCheckConvergence then
|
||||||
|
begin
|
||||||
|
if Abs(prevLoss - loss) < Max(fMinImprovement, fTol * Max(1.0, Abs(prevLoss))) then
|
||||||
|
break;
|
||||||
|
|
||||||
|
prevLoss := loss;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- update
|
||||||
|
var invM := 1.0 / m;
|
||||||
|
|
||||||
|
for var k := 0 to fClassCount - 1 do
|
||||||
|
begin
|
||||||
|
gradB[k] *= invM;
|
||||||
|
fIntercept.Data[k] -= fLearningRate * gradB[k];
|
||||||
|
|
||||||
|
for var j := 0 to p - 1 do
|
||||||
|
begin
|
||||||
|
var g := gradW[k][j] * invM;
|
||||||
|
|
||||||
|
if fLambda <> 0 then
|
||||||
|
g += fLambda * fW.Data[j,k];
|
||||||
|
|
||||||
|
fW.Data[j,k] -= fLearningRate * g;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
fFitted := true;
|
||||||
|
Result := Self;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{function LogisticRegression.FitOld(X: Matrix; y: Vector): ISupervisedModel;
|
||||||
|
begin
|
||||||
|
if X = nil then
|
||||||
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
|
if y = nil then
|
||||||
|
ArgumentNullError(ER_Y_NULL);
|
||||||
|
|
||||||
|
if MLConfig.ValidateFiniteInputs then
|
||||||
|
begin
|
||||||
|
CheckXForFit(X);
|
||||||
|
CheckYForFit(y);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if X.RowCount = 0 then
|
||||||
|
ArgumentError(ER_EMPTY_DATASET);
|
||||||
|
|
||||||
|
if X.RowCount <> y.Length then
|
||||||
|
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
|
||||||
|
|
||||||
|
if fLearningRate <= 0 then
|
||||||
|
ArgumentOutOfRangeError(ER_LEARNING_RATE_INVALID, fLearningRate);
|
||||||
|
|
||||||
|
if fEpochs <= 0 then
|
||||||
|
ArgumentOutOfRangeError(ER_EPOCHS_INVALID, fEpochs);
|
||||||
|
|
||||||
|
if fLambda < 0 then
|
||||||
|
ArgumentOutOfRangeError(ER_LAMBDA_NEGATIVE, fLambda);
|
||||||
|
|
||||||
|
if fCheckConvergence and (fTol <= 0) then
|
||||||
|
ArgumentOutOfRangeError(ER_TOL_INVALID, fTol);
|
||||||
|
|
||||||
|
var m := X.RowCount;
|
||||||
|
var p := X.ColCount;
|
||||||
|
|
||||||
|
// --- convert to integer labels
|
||||||
|
var yInt := new integer[m];
|
||||||
|
|
||||||
|
for var i := 0 to m - 1 do
|
||||||
|
begin
|
||||||
|
var r := y[i];
|
||||||
|
var ir := Round(r);
|
||||||
|
|
||||||
|
if Abs(r - ir) > 1e-12 then
|
||||||
|
ArgumentError(ER_LABELS_NOT_INTEGER);
|
||||||
|
|
||||||
|
yInt[i] := ir;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- encode (порядок первого появления)
|
||||||
|
var unique: array of integer;
|
||||||
|
var yEncoded := EncodeLabelsInt(yInt, unique);
|
||||||
|
|
||||||
|
fClassCount := unique.Length;
|
||||||
|
|
||||||
|
if fClassCount < 2 then
|
||||||
|
ArgumentError(ER_LOGISTIC_NEED_AT_LEAST_TWO_CLASSES);
|
||||||
|
|
||||||
|
// --- build mappings
|
||||||
|
fClassToIndex := new Dictionary<integer, integer>;
|
||||||
|
SetLength(fIndexToClass, fClassCount);
|
||||||
|
|
||||||
|
for var i := 0 to fClassCount - 1 do
|
||||||
|
begin
|
||||||
|
fClassToIndex[unique[i]] := i;
|
||||||
|
fIndexToClass[i] := unique[i];
|
||||||
|
end;
|
||||||
|
|
||||||
// --- init
|
// --- init
|
||||||
fW := new Matrix(p, fClassCount);
|
fW := new Matrix(p, fClassCount);
|
||||||
fIntercept := new Vector(fClassCount);
|
fIntercept := new Vector(fClassCount);
|
||||||
|
|
@ -3301,7 +3594,7 @@ begin
|
||||||
|
|
||||||
fFitted := true;
|
fFitted := true;
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;}
|
||||||
|
|
||||||
function LogisticRegression.PredictProba(X: Matrix): Matrix;
|
function LogisticRegression.PredictProba(X: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
|
|
@ -3394,7 +3687,8 @@ begin
|
||||||
fEpochs,
|
fEpochs,
|
||||||
fTol,
|
fTol,
|
||||||
fCheckConvergence,
|
fCheckConvergence,
|
||||||
fMinImprovement
|
fMinImprovement,
|
||||||
|
fUseFastExp
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -4050,6 +4344,7 @@ begin
|
||||||
fMinSamplesSplit,
|
fMinSamplesSplit,
|
||||||
fMinSamplesLeaf,
|
fMinSamplesLeaf,
|
||||||
fCriterion,
|
fCriterion,
|
||||||
|
fClassCount,
|
||||||
fMaxFeatures,
|
fMaxFeatures,
|
||||||
fRandomSeed
|
fRandomSeed
|
||||||
);
|
);
|
||||||
|
|
@ -7256,15 +7551,6 @@ begin
|
||||||
// --- copy train data
|
// --- copy train data
|
||||||
fXTrain := X.Clone;
|
fXTrain := X.Clone;
|
||||||
|
|
||||||
fSampleSize := Min(3000, fXTrain.RowCount); // попробуй 2000–4000
|
|
||||||
|
|
||||||
SetLength(fSampleIdx, fSampleSize);
|
|
||||||
|
|
||||||
var rnd := new System.Random(42);
|
|
||||||
|
|
||||||
for var i := 0 to fSampleSize - 1 do
|
|
||||||
fSampleIdx[i] := rnd.Next(fXTrain.RowCount);
|
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// ЕДИНЫЙ ENCODING
|
// ЕДИНЫЙ ENCODING
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
|
@ -8269,17 +8555,17 @@ end;
|
||||||
function DBSCAN.ClustersCount: integer := fClusterCount;
|
function DBSCAN.ClustersCount: integer := fClusterCount;
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// Pipeline
|
// MatrixPipeline
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
||||||
constructor Pipeline.Create;
|
constructor MatrixPipeline.Create;
|
||||||
begin
|
begin
|
||||||
fTransformers := new List<ITransformer>;
|
fTransformers := new List<ITransformer>;
|
||||||
fModel := nil;
|
fModel := nil;
|
||||||
fFitted := false;
|
fFitted := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
constructor Pipeline.Create(model: ISupervisedModel);
|
constructor MatrixPipeline.Create(model: ISupervisedModel);
|
||||||
begin
|
begin
|
||||||
Create;
|
Create;
|
||||||
if model = nil then
|
if model = nil then
|
||||||
|
|
@ -8287,7 +8573,7 @@ begin
|
||||||
fModel := model;
|
fModel := model;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function Pipeline.Build(params steps: array of IPipelineStep): Pipeline;
|
class function MatrixPipeline.Build(params steps: array of IPipelineStep): MatrixPipeline;
|
||||||
begin
|
begin
|
||||||
if (steps = nil) or (Length(steps) = 0) then
|
if (steps = nil) or (Length(steps) = 0) then
|
||||||
ArgumentError(ER_PIPELINE_NO_STEPS);
|
ArgumentError(ER_PIPELINE_NO_STEPS);
|
||||||
|
|
@ -8301,7 +8587,7 @@ begin
|
||||||
if not (last is ISupervisedModel) then
|
if not (last is ISupervisedModel) then
|
||||||
ArgumentError(ER_PIPELINE_LAST_NOT_SUPERVISED_MODEL);
|
ArgumentError(ER_PIPELINE_LAST_NOT_SUPERVISED_MODEL);
|
||||||
|
|
||||||
var pipe := new Pipeline(last as ISupervisedModel);
|
var pipe := new MatrixPipeline(last as ISupervisedModel);
|
||||||
|
|
||||||
// все шаги кроме последнего — трансформеры
|
// все шаги кроме последнего — трансформеры
|
||||||
for var i := 0 to High(steps) - 1 do
|
for var i := 0 to High(steps) - 1 do
|
||||||
|
|
@ -8320,7 +8606,7 @@ begin
|
||||||
Result := pipe;
|
Result := pipe;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Pipeline.Add(t: ITransformer): Pipeline;
|
function MatrixPipeline.Add(t: ITransformer): MatrixPipeline;
|
||||||
begin
|
begin
|
||||||
if t = nil then
|
if t = nil then
|
||||||
ArgumentError(ER_TRANSFORMER_NULL);
|
ArgumentError(ER_TRANSFORMER_NULL);
|
||||||
|
|
@ -8329,7 +8615,7 @@ begin
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Pipeline.SetModel(m: ISupervisedModel): Pipeline;
|
function MatrixPipeline.SetModel(m: ISupervisedModel): MatrixPipeline;
|
||||||
begin
|
begin
|
||||||
if m = nil then
|
if m = nil then
|
||||||
ArgumentError(ER_MODEL_NULL);
|
ArgumentError(ER_MODEL_NULL);
|
||||||
|
|
@ -8338,7 +8624,7 @@ begin
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Pipeline.Fit(X: Matrix; y: Vector): ISupervisedModel;
|
function MatrixPipeline.Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||||
begin
|
begin
|
||||||
if fModel = nil then
|
if fModel = nil then
|
||||||
ArgumentError(ER_MODEL_NULL);
|
ArgumentError(ER_MODEL_NULL);
|
||||||
|
|
@ -8386,7 +8672,7 @@ begin
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Pipeline.Transform(X: Matrix): Matrix;
|
function MatrixPipeline.Transform(X: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
if not fFitted then
|
if not fFitted then
|
||||||
NotFittedError(ER_FIT_NOT_CALLED);
|
NotFittedError(ER_FIT_NOT_CALLED);
|
||||||
|
|
@ -8410,7 +8696,7 @@ begin
|
||||||
Result := Xt;
|
Result := Xt;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Pipeline.Predict(X: Matrix): Vector;
|
function MatrixPipeline.Predict(X: Matrix): Vector;
|
||||||
begin
|
begin
|
||||||
if not fFitted then
|
if not fFitted then
|
||||||
NotFittedError(ER_FIT_NOT_CALLED);
|
NotFittedError(ER_FIT_NOT_CALLED);
|
||||||
|
|
@ -8422,7 +8708,7 @@ begin
|
||||||
Result := fModel.Predict(Xt);
|
Result := fModel.Predict(Xt);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Pipeline.PredictProba(X: Matrix): Matrix;
|
function MatrixPipeline.PredictProba(X: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
if not fFitted then
|
if not fFitted then
|
||||||
NotFittedError(ER_FIT_NOT_CALLED);
|
NotFittedError(ER_FIT_NOT_CALLED);
|
||||||
|
|
@ -8439,9 +8725,9 @@ begin
|
||||||
.PredictProba(Xt);
|
.PredictProba(Xt);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Pipeline.ToString: string;
|
function MatrixPipeline.ToString: string;
|
||||||
begin
|
begin
|
||||||
var sb := 'Pipeline (' +
|
var sb := 'MatrixPipeline (' +
|
||||||
(if fFitted then 'trained' else 'not trained') + '):' + NewLine;
|
(if fFitted then 'trained' else 'not trained') + '):' + NewLine;
|
||||||
|
|
||||||
var idx := 1;
|
var idx := 1;
|
||||||
|
|
@ -8458,12 +8744,12 @@ begin
|
||||||
Result := sb;
|
Result := sb;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Pipeline.Clone: IModel;
|
function MatrixPipeline.Clone: IModel;
|
||||||
begin
|
begin
|
||||||
if fModel = nil then
|
if fModel = nil then
|
||||||
ArgumentError(ER_MODEL_NULL);
|
ArgumentError(ER_MODEL_NULL);
|
||||||
|
|
||||||
var p := new Pipeline;
|
var p := new MatrixPipeline;
|
||||||
|
|
||||||
foreach var t in fTransformers do
|
foreach var t in fTransformers do
|
||||||
p.Add(t.Clone);
|
p.Add(t.Clone);
|
||||||
|
|
@ -8479,16 +8765,16 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// UPipeline
|
// UMatrixPipeline
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
constructor UPipeline.Create;
|
constructor UMatrixPipeline.Create;
|
||||||
begin
|
begin
|
||||||
fTransformers := new List<ITransformer>;
|
fTransformers := new List<ITransformer>;
|
||||||
fModel := nil;
|
fModel := nil;
|
||||||
fFitted := false;
|
fFitted := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
constructor UPipeline.Create(model: IModel);
|
constructor UMatrixPipeline.Create(model: IModel);
|
||||||
begin
|
begin
|
||||||
Create;
|
Create;
|
||||||
if model = nil then
|
if model = nil then
|
||||||
|
|
@ -8496,7 +8782,7 @@ begin
|
||||||
fModel := model;
|
fModel := model;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function UPipeline.Build(params steps: array of IPipelineStep): UPipeline;
|
class function UMatrixPipeline.Build(params steps: array of IPipelineStep): UMatrixPipeline;
|
||||||
begin
|
begin
|
||||||
if (steps = nil) or (Length(steps) = 0) then
|
if (steps = nil) or (Length(steps) = 0) then
|
||||||
ArgumentError(ER_PIPELINE_NO_STEPS);
|
ArgumentError(ER_PIPELINE_NO_STEPS);
|
||||||
|
|
@ -8509,7 +8795,7 @@ begin
|
||||||
if not (last is IModel) then
|
if not (last is IModel) then
|
||||||
ArgumentError(ER_PIPELINE_LAST_NOT_MODEL);
|
ArgumentError(ER_PIPELINE_LAST_NOT_MODEL);
|
||||||
|
|
||||||
var pipe := new UPipeline(last as IModel);
|
var pipe := new UMatrixPipeline(last as IModel);
|
||||||
|
|
||||||
for var i := 0 to High(steps) - 1 do
|
for var i := 0 to High(steps) - 1 do
|
||||||
begin
|
begin
|
||||||
|
|
@ -8527,7 +8813,7 @@ begin
|
||||||
Result := pipe;
|
Result := pipe;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function UPipeline.Add(t: ITransformer): UPipeline;
|
function UMatrixPipeline.Add(t: ITransformer): UMatrixPipeline;
|
||||||
begin
|
begin
|
||||||
if t = nil then
|
if t = nil then
|
||||||
ArgumentError(ER_TRANSFORMER_NULL);
|
ArgumentError(ER_TRANSFORMER_NULL);
|
||||||
|
|
@ -8536,7 +8822,7 @@ begin
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function UPipeline.SetModel(m: IModel): UPipeline;
|
function UMatrixPipeline.SetModel(m: IModel): UMatrixPipeline;
|
||||||
begin
|
begin
|
||||||
if m = nil then
|
if m = nil then
|
||||||
ArgumentError(ER_MODEL_NULL);
|
ArgumentError(ER_MODEL_NULL);
|
||||||
|
|
@ -8545,7 +8831,7 @@ begin
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function UPipeline.Fit(X: Matrix): IUnsupervisedModel;
|
function UMatrixPipeline.Fit(X: Matrix): IUnsupervisedModel;
|
||||||
begin
|
begin
|
||||||
if fModel = nil then
|
if fModel = nil then
|
||||||
ArgumentError(ER_MODEL_NULL);
|
ArgumentError(ER_MODEL_NULL);
|
||||||
|
|
@ -8585,7 +8871,7 @@ begin
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function UPipeline.Transform(X: Matrix): Matrix;
|
function UMatrixPipeline.Transform(X: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
if not fFitted then
|
if not fFitted then
|
||||||
NotFittedError(ER_FIT_NOT_CALLED);
|
NotFittedError(ER_FIT_NOT_CALLED);
|
||||||
|
|
@ -8609,7 +8895,7 @@ begin
|
||||||
Result := Xt;
|
Result := Xt;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function UPipeline.Predict(X: Matrix): Vector;
|
function UMatrixPipeline.Predict(X: Matrix): Vector;
|
||||||
begin
|
begin
|
||||||
if not fFitted then
|
if not fFitted then
|
||||||
NotFittedError(ER_FIT_NOT_CALLED);
|
NotFittedError(ER_FIT_NOT_CALLED);
|
||||||
|
|
@ -8625,9 +8911,9 @@ begin
|
||||||
Result := (fModel as IPredictiveModel).Predict(Xt);
|
Result := (fModel as IPredictiveModel).Predict(Xt);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function UPipeline.ToString: string;
|
function UMatrixPipeline.ToString: string;
|
||||||
begin
|
begin
|
||||||
var sb := 'UPipeline (' +
|
var sb := 'UMatrixPipeline (' +
|
||||||
(if fFitted then 'trained' else 'not trained') + '):' + NewLine;
|
(if fFitted then 'trained' else 'not trained') + '):' + NewLine;
|
||||||
|
|
||||||
var idx := 1;
|
var idx := 1;
|
||||||
|
|
@ -8644,12 +8930,12 @@ begin
|
||||||
Result := sb;
|
Result := sb;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function UPipeline.Clone: IModel;
|
function UMatrixPipeline.Clone: IModel;
|
||||||
begin
|
begin
|
||||||
if fModel = nil then
|
if fModel = nil then
|
||||||
ArgumentError(ER_MODEL_NULL);
|
ArgumentError(ER_MODEL_NULL);
|
||||||
|
|
||||||
var p := new UPipeline;
|
var p := new UMatrixPipeline;
|
||||||
|
|
||||||
foreach var t in fTransformers do
|
foreach var t in fTransformers do
|
||||||
p.Add(t.Clone);
|
p.Add(t.Clone);
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,8 @@ const
|
||||||
'Model is not a classifier';
|
'Model is not a classifier';
|
||||||
ER_MODEL_CLONE_TYPE =
|
ER_MODEL_CLONE_TYPE =
|
||||||
'Clone модели вернул неподдерживаемый тип!!Model Clone returned unsupported type';
|
'Clone модели вернул неподдерживаемый тип!!Model Clone returned unsupported type';
|
||||||
|
ER_INVALID_MODEL_TYPE =
|
||||||
|
'Clone модели вернул неподдерживаемый тип (ожидается {0})!!Model Clone returned unsupported type (expected {0})';
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// DataPipeline
|
// DataPipeline
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
@ -1143,7 +1144,14 @@ begin
|
||||||
|
|
||||||
// --- модель
|
// --- модель
|
||||||
if fModel <> nil then
|
if fModel <> nil then
|
||||||
p.fModel := fModel.Clone as IUnsupervisedModel;
|
begin
|
||||||
|
var m := fModel.Clone;
|
||||||
|
|
||||||
|
if not (m is IUnsupervisedModel) then
|
||||||
|
Error(ER_INVALID_MODEL_TYPE, 'IUnsupervisedModel');
|
||||||
|
|
||||||
|
p.fModel := m as IUnsupervisedModel;
|
||||||
|
end;
|
||||||
|
|
||||||
// --- состояние НЕ копируем
|
// --- состояние НЕ копируем
|
||||||
p.fFinalFeatures := nil;
|
p.fFinalFeatures := nil;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ function IntsToLabels(a: array of integer): Vector;
|
||||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||||
/// Используется при обучении моделей и визуализации.
|
/// Используется при обучении моделей и визуализации.
|
||||||
|
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||||
function EncodeLabels(labels: array of string): array of integer;
|
function EncodeLabels(labels: array of string): array of integer;
|
||||||
|
|
||||||
/// Кодирует строковые метки классов в целочисленные индексы.
|
/// Кодирует строковые метки классов в целочисленные индексы.
|
||||||
|
|
@ -47,6 +48,7 @@ function EncodeLabels(labels: array of string): array of integer;
|
||||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||||
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
||||||
/// Используется при обучении моделей и визуализации
|
/// Используется при обучении моделей и визуализации
|
||||||
|
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||||
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
|
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
|
||||||
|
|
||||||
/// Кодирует целые метки классов в целочисленные индексы.
|
/// Кодирует целые метки классов в целочисленные индексы.
|
||||||
|
|
@ -54,6 +56,7 @@ function EncodeLabels(labels: array of string; var classes: array of string): ar
|
||||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||||
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
||||||
/// Используется при обучении моделей и визуализации
|
/// Используется при обучении моделей и визуализации
|
||||||
|
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||||
function EncodeLabelsInt(labels: array of integer; var classes: array of integer): array of integer;
|
function EncodeLabelsInt(labels: array of integer; var classes: array of integer): array of integer;
|
||||||
|
|
||||||
/// Преобразует строковые метки классов в целочисленные индексы
|
/// Преобразует строковые метки классов в целочисленные индексы
|
||||||
|
|
@ -61,6 +64,7 @@ function EncodeLabelsInt(labels: array of integer; var classes: array of integer
|
||||||
/// classes должен быть получен из EncodeLabels.
|
/// classes должен быть получен из EncodeLabels.
|
||||||
/// Если встречается неизвестная метка — выбрасывается исключение.
|
/// Если встречается неизвестная метка — выбрасывается исключение.
|
||||||
/// Используется для применения кодирования к тестовым данным (Transform).
|
/// Используется для применения кодирования к тестовым данным (Transform).
|
||||||
|
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||||
function TransformLabels(labels: array of string; classes: array of string): array of integer;
|
function TransformLabels(labels: array of string; classes: array of string): array of integer;
|
||||||
|
|
||||||
/// Преобразует целочисленные метки классов в индексы (0,1,2,...)
|
/// Преобразует целочисленные метки классов в индексы (0,1,2,...)
|
||||||
|
|
@ -68,11 +72,13 @@ function TransformLabels(labels: array of string; classes: array of string): arr
|
||||||
/// classes должен быть получен из EncodeLabelsInt.
|
/// classes должен быть получен из EncodeLabelsInt.
|
||||||
/// Если встречается неизвестное значение — выбрасывается исключение.
|
/// Если встречается неизвестное значение — выбрасывается исключение.
|
||||||
/// Используется для применения кодирования к тестовым данным (Transform).
|
/// Используется для применения кодирования к тестовым данным (Transform).
|
||||||
|
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||||
function TransformLabelsInt(labels: array of integer; classes: array of integer): array of integer;
|
function TransformLabelsInt(labels: array of integer; classes: array of integer): array of integer;
|
||||||
|
|
||||||
/// Преобразует целочисленные индексы классов обратно в строковые метки.
|
/// Преобразует целочисленные индексы классов обратно в строковые метки.
|
||||||
/// Массив classes задаёт соответствие: classes[i] — имя класса с индексом i.
|
/// Массив classes задаёт соответствие: classes[i] — имя класса с индексом i.
|
||||||
/// Используется для получения текстовых предсказаний моделей.
|
/// Используется для получения текстовых предсказаний моделей.
|
||||||
|
/// Предполагается, что входные данные уже корректны.
|
||||||
function DecodeLabels(y: array of integer; classes: array of string): array of string;
|
function DecodeLabels(y: array of integer; classes: array of string): array of string;
|
||||||
|
|
||||||
/// Возвращает список уникальных меток классов.
|
/// Возвращает список уникальных меток классов.
|
||||||
|
|
@ -266,7 +272,17 @@ end;
|
||||||
|
|
||||||
function UniqueLabels(labels: array of string): array of string;
|
function UniqueLabels(labels: array of string): array of string;
|
||||||
begin
|
begin
|
||||||
Result := labels.Distinct.ToArray;
|
var seen := new HashSet<string>;
|
||||||
|
var res := new List<string>;
|
||||||
|
|
||||||
|
for var i := 0 to labels.Length - 1 do
|
||||||
|
if not seen.Contains(labels[i]) then
|
||||||
|
begin
|
||||||
|
seen.Add(labels[i]);
|
||||||
|
res.Add(labels[i]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := res.ToArray;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function CloneOrNil(v: Vector): Vector;
|
function CloneOrNil(v: Vector): Vector;
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,9 @@ type
|
||||||
/// Ожидается, что yTrue и yPred содержат закодированные метки классов
|
/// Ожидается, что yTrue и yPred содержат закодированные метки классов
|
||||||
/// (целые значения 0..K-1, допускается хранение в Vector типа real).
|
/// (целые значения 0..K-1, допускается хранение в Vector типа real).
|
||||||
/// Для строковых меток необходимо предварительно применить EncodeLabels
|
/// Для строковых меток необходимо предварительно применить EncodeLabels
|
||||||
|
///
|
||||||
|
/// Метки классов упорядочиваются по возрастанию для обеспечения
|
||||||
|
/// стабильного и предсказуемого представления результатов
|
||||||
ConfusionMatrix = class
|
ConfusionMatrix = class
|
||||||
private
|
private
|
||||||
fMatrix: array[,] of integer;
|
fMatrix: array[,] of integer;
|
||||||
|
|
|
||||||
|
|
@ -11641,103 +11641,128 @@ end;
|
||||||
// Partition, ZipTuple, UnZipTuple, Interleave, Numerate, Tabulate, Pairwise, Batch
|
// Partition, ZipTuple, UnZipTuple, Interleave, Numerate, Tabulate, Pairwise, Batch
|
||||||
// Дополнения 2024: Zip - синоним ZipTuple
|
// Дополнения 2024: Zip - синоним ZipTuple
|
||||||
|
|
||||||
|
/// Возвращает первый элемент последовательности с минимальным значением ключа
|
||||||
/// Возвращает первый элемент последовательности с минимальным значением ключа
|
/// Возвращает первый элемент последовательности с минимальным значением ключа
|
||||||
function MinBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
function MinBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
||||||
begin
|
begin
|
||||||
var enumerator := Self.GetEnumerator();
|
|
||||||
if not enumerator.MoveNext() then
|
|
||||||
raise new System.ArgumentException(GetTranslation(SEQUENCE_CANNOT_BE_EMPTY));
|
|
||||||
|
|
||||||
var minElement := enumerator.Current;
|
|
||||||
var minKey := keySelector(minElement);
|
|
||||||
|
|
||||||
var comp := Comparer&<TKey>.Default;
|
var comp := Comparer&<TKey>.Default;
|
||||||
while enumerator.MoveNext() do
|
var minElement: T;
|
||||||
begin
|
var minKey: TKey;
|
||||||
var currentElement := enumerator.Current;
|
var hasValue := False;
|
||||||
var currentKey := keySelector(currentElement);
|
|
||||||
if comp.Compare(currentKey,minKey) < 0 then
|
|
||||||
begin
|
|
||||||
minKey := currentKey;
|
|
||||||
minElement := currentElement;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
foreach var current in Self do
|
||||||
|
if not hasValue then
|
||||||
|
begin
|
||||||
|
minElement := current;
|
||||||
|
minKey := keySelector(current);
|
||||||
|
hasValue := True;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
var currentKey := keySelector(current);
|
||||||
|
if comp.Compare(currentKey, minKey) < 0 then // только строго меньше
|
||||||
|
begin
|
||||||
|
minKey := currentKey;
|
||||||
|
minElement := current;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not hasValue then
|
||||||
|
raise new ArgumentException(GetTranslation(SEQUENCE_CANNOT_BE_EMPTY));
|
||||||
|
|
||||||
Result := minElement;
|
Result := minElement;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
/// Возвращает первый элемент последовательности с максимальным значением ключа
|
/// Возвращает первый элемент последовательности с максимальным значением ключа
|
||||||
function MaxBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
function MaxBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
||||||
begin
|
begin
|
||||||
var enumerator := Self.GetEnumerator();
|
|
||||||
if not enumerator.MoveNext() then
|
|
||||||
raise new System.ArgumentException(GetTranslation(SEQUENCE_CANNOT_BE_EMPTY));
|
|
||||||
|
|
||||||
var maxElement := enumerator.Current;
|
|
||||||
var maxKey := keySelector(maxElement);
|
|
||||||
|
|
||||||
var comp := Comparer&<TKey>.Default;
|
var comp := Comparer&<TKey>.Default;
|
||||||
while enumerator.MoveNext() do
|
var maxElement: T;
|
||||||
begin
|
var maxKey: TKey;
|
||||||
var currentElement := enumerator.Current;
|
var hasValue := False;
|
||||||
var currentKey := keySelector(currentElement);
|
|
||||||
if comp.Compare(currentKey,maxKey) > 0 then
|
|
||||||
begin
|
|
||||||
maxKey := currentKey;
|
|
||||||
maxElement := currentElement;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
foreach var current in Self do
|
||||||
|
if not hasValue then
|
||||||
|
begin
|
||||||
|
maxElement := current;
|
||||||
|
maxKey := keySelector(current);
|
||||||
|
hasValue := True;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
var currentKey := keySelector(current);
|
||||||
|
if comp.Compare(currentKey, maxKey) > 0 then
|
||||||
|
begin
|
||||||
|
maxKey := currentKey;
|
||||||
|
maxElement := current;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not hasValue then
|
||||||
|
raise new ArgumentException(GetTranslation(SEQUENCE_CANNOT_BE_EMPTY));
|
||||||
|
|
||||||
Result := maxElement;
|
Result := maxElement;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
/// Возвращает последний элемент последовательности с минимальным значением ключа
|
/// Возвращает последний элемент последовательности с минимальным значением ключа
|
||||||
function LastMinBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
function LastMinBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
||||||
begin
|
begin
|
||||||
var enumerator := Self.GetEnumerator();
|
|
||||||
if not enumerator.MoveNext() then
|
|
||||||
raise new System.ArgumentException(GetTranslation(SEQUENCE_CANNOT_BE_EMPTY));
|
|
||||||
|
|
||||||
var minElement := enumerator.Current;
|
|
||||||
var minKey := keySelector(minElement);
|
|
||||||
|
|
||||||
var comp := Comparer&<TKey>.Default;
|
var comp := Comparer&<TKey>.Default;
|
||||||
while enumerator.MoveNext() do
|
var minElement: T;
|
||||||
begin
|
var minKey: TKey;
|
||||||
var currentElement := enumerator.Current;
|
var hasValue := False;
|
||||||
var currentKey := keySelector(currentElement);
|
|
||||||
if comp.Compare(currentKey,minKey) <= 0 then
|
|
||||||
begin
|
|
||||||
minKey := currentKey;
|
|
||||||
minElement := currentElement;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
foreach var current in Self do
|
||||||
|
if not hasValue then
|
||||||
|
begin
|
||||||
|
minElement := current;
|
||||||
|
minKey := keySelector(current);
|
||||||
|
hasValue := True;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
var currentKey := keySelector(current);
|
||||||
|
if comp.Compare(currentKey, minKey) <= 0 then // <= для последнего минимума
|
||||||
|
begin
|
||||||
|
minKey := currentKey;
|
||||||
|
minElement := current;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not hasValue then
|
||||||
|
raise new ArgumentException(GetTranslation(SEQUENCE_CANNOT_BE_EMPTY));
|
||||||
|
|
||||||
Result := minElement;
|
Result := minElement;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
/// Возвращает последний элемент последовательности с максимальным значением ключа
|
/// Возвращает последний элемент последовательности с максимальным значением ключа
|
||||||
function LastMaxBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
function LastMaxBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
||||||
begin
|
begin
|
||||||
var enumerator := Self.GetEnumerator();
|
|
||||||
if not enumerator.MoveNext() then
|
|
||||||
raise new System.ArgumentException(GetTranslation(SEQUENCE_CANNOT_BE_EMPTY));
|
|
||||||
|
|
||||||
var maxElement := enumerator.Current;
|
|
||||||
var maxKey := keySelector(maxElement);
|
|
||||||
|
|
||||||
var comp := Comparer&<TKey>.Default;
|
var comp := Comparer&<TKey>.Default;
|
||||||
while enumerator.MoveNext() do
|
var maxElement: T;
|
||||||
begin
|
var maxKey: TKey;
|
||||||
var currentElement := enumerator.Current;
|
var hasValue := False;
|
||||||
var currentKey := keySelector(currentElement);
|
|
||||||
if comp.Compare(currentKey,maxKey) > 0 then
|
|
||||||
begin
|
|
||||||
maxKey := currentKey;
|
|
||||||
maxElement := currentElement;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
foreach var current in Self do
|
||||||
|
if not hasValue then
|
||||||
|
begin
|
||||||
|
maxElement := current;
|
||||||
|
maxKey := keySelector(current);
|
||||||
|
hasValue := True;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
var currentKey := keySelector(current);
|
||||||
|
if comp.Compare(currentKey, maxKey) >= 0 then
|
||||||
|
begin
|
||||||
|
maxKey := currentKey;
|
||||||
|
maxElement := current;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not hasValue then
|
||||||
|
raise new ArgumentException(GetTranslation(SEQUENCE_CANNOT_BE_EMPTY));
|
||||||
|
|
||||||
Result := maxElement;
|
Result := maxElement;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -323,11 +323,11 @@ begin
|
||||||
var cnt := pair.Value.Count;
|
var cnt := pair.Value.Count;
|
||||||
|
|
||||||
// Класс может иметь меньше объектов, чем число фолдов.
|
// Класс может иметь меньше объектов, чем число фолдов.
|
||||||
// В этом случае он не будет представлен во всех фолдах.
|
// В библиотеке принята строгая политика: такие случаи считаются ошибкой,
|
||||||
// Это допустимо, но может привести к ошибкам при обучении модели.
|
// так как не гарантируется присутствие класса во всех train-fold.
|
||||||
|
// Поэтому выполняется fail-fast проверка (см. ниже).
|
||||||
//if cnt < k then
|
if cnt < k then
|
||||||
// ArgumentError(ER_STRATIFIED_CLASS_TOO_SMALL, cls, cnt, k);
|
ArgumentError(ER_STRATIFIED_CLASS_TOO_SMALL, cls, cnt, k);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- 2. Контейнеры фолдов
|
// --- 2. Контейнеры фолдов
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue