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 Minor = "11";
|
||||
public const string Build = "1";
|
||||
public const string Revision = "3808";
|
||||
public const string Revision = "3810";
|
||||
|
||||
public const string MainVersion = Major + "." + Minor;
|
||||
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%COREVERSION%=1
|
||||
%REVISION%=3808
|
||||
%REVISION%=3810
|
||||
%MINOR%=11
|
||||
%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';
|
||||
ER_ENCODELABELS_UNSUPPORTED_TYPE =
|
||||
'Неподдерживаемый тип столбца "{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;
|
||||
begin
|
||||
|
|
@ -89,6 +91,11 @@ begin
|
|||
|
||||
if not Self.IsCategorical(target) then
|
||||
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
|
||||
begin
|
||||
|
|
@ -130,15 +137,18 @@ begin
|
|||
|
||||
if not Self.IsCategorical(target) then
|
||||
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
|
||||
|
||||
ColumnType.ctStr:
|
||||
begin
|
||||
var labels := Self.GetStrColumn(target).ToArray;
|
||||
Result := EncodeLabels(labels, classes);
|
||||
end;
|
||||
|
||||
ColumnType.ctInt:
|
||||
begin
|
||||
var labels := Self.GetIntColumn(target).ToArray;
|
||||
|
|
@ -149,7 +159,6 @@ begin
|
|||
// если API требует string classes:
|
||||
classes := intClasses.Select(x -> x.ToString).ToArray;
|
||||
end;
|
||||
|
||||
else
|
||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||
end;
|
||||
|
|
@ -176,15 +185,18 @@ begin
|
|||
|
||||
if not Self.IsCategorical(target) then
|
||||
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
|
||||
|
||||
ColumnType.ctStr:
|
||||
begin
|
||||
var data := Self.GetStrColumn(target).ToArray;
|
||||
Result := TransformLabels(data, classes);
|
||||
end;
|
||||
|
||||
ColumnType.ctInt:
|
||||
begin
|
||||
var data := Self.GetIntColumn(target).ToArray;
|
||||
|
|
@ -195,7 +207,6 @@ begin
|
|||
|
||||
Result := TransformLabels(strData, classes);
|
||||
end;
|
||||
|
||||
else
|
||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||
end;
|
||||
|
|
@ -224,6 +235,11 @@ begin
|
|||
|
||||
if Self.GetColumnType(target) <> ColumnType.ctInt then
|
||||
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;
|
||||
|
||||
|
|
@ -253,6 +269,11 @@ begin
|
|||
if Self.GetColumnType(target) <> ColumnType.ctInt then
|
||||
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;
|
||||
|
||||
Result := EncodeLabelsInt(labels, classes);
|
||||
|
|
|
|||
|
|
@ -49,24 +49,14 @@ type
|
|||
|
||||
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 методы
|
||||
{function DataFrame.JoinInnerSingleKey(other: DataFrame; leftKey, rightKey: integer;
|
||||
resultSchema: DataFrameSchema): DataFrame;}
|
||||
function JoinInnerSingleKey(other: DataFrame; key: string): 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 JoinInnerSingleKeyBool(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||
|
||||
function LeftJoinSingleKey(other: DataFrame; key: string): 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 LeftJoinSingleKeyBool(other: DataFrame; leftKey, rightKey: integer): DataFrame;
|
||||
|
||||
|
|
@ -96,7 +86,6 @@ type
|
|||
|
||||
function CreateEmptyBySchema(schema: DataFrameSchema): DataFrame;
|
||||
|
||||
function GetColumnIndex(name: string): integer;
|
||||
function GetColumn(name: string): Column;
|
||||
|
||||
function CloneWithCopiedColumns: DataFrame;
|
||||
|
|
@ -713,43 +702,6 @@ begin
|
|||
res.AddIntColumn(name, data, valid);
|
||||
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(
|
||||
res: DataFrame;
|
||||
name: string;
|
||||
|
|
@ -1032,23 +984,21 @@ function DataFrame.BuildJoinKey(cur: DataFrameCursor; layout: JoinKeyLayout; var
|
|||
begin
|
||||
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
|
||||
case layout.ColTypes[i] of
|
||||
ctInt: inc(ic);
|
||||
ctFloat: inc(fc);
|
||||
ctStr: inc(sc);
|
||||
ctBool: inc(bc);
|
||||
end;
|
||||
|
||||
Result.Ints := new integer[ic];
|
||||
Result.Floats := new real[fc];
|
||||
Result.Strs := new string[sc];
|
||||
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
|
||||
begin
|
||||
|
|
@ -1066,10 +1016,7 @@ begin
|
|||
inc(ic);
|
||||
end;
|
||||
ctFloat:
|
||||
begin
|
||||
Result.Floats[fc] := cur.Float(col);
|
||||
inc(fc);
|
||||
end;
|
||||
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||
ctStr:
|
||||
begin
|
||||
Result.Strs[sc] := cur.Str(col);
|
||||
|
|
@ -1080,6 +1027,8 @@ begin
|
|||
Result.Bools[bc] := cur.Bool(col);
|
||||
inc(bc);
|
||||
end;
|
||||
else
|
||||
Error(ER_UNSUPPORTED_COLUMN_TYPE, layout.ColTypes[i]);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
|
@ -1104,23 +1053,24 @@ end;
|
|||
|
||||
function DataFrame.LeftJoinSingleKey(other: DataFrame; key: string): DataFrame;
|
||||
begin
|
||||
// 1. индексы ключей — через Schema
|
||||
var leftKey := fSchema.IndexOf(key);
|
||||
var rightKey := other.fSchema.IndexOf(key);
|
||||
|
||||
// 2. проверка типов ключей — через Schema
|
||||
var lt := fSchema.ColumnTypeAt(leftKey);
|
||||
var rt := other.fSchema.ColumnTypeAt(rightKey);
|
||||
|
||||
if lt <> rt then
|
||||
Error(ER_JOIN_KEY_TYPE_MISMATCH);
|
||||
|
||||
// 3. типоспецифичный алгоритм (КАК РАНЬШЕ)
|
||||
if lt = ctFloat then
|
||||
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||
|
||||
case lt of
|
||||
ctInt: Result := LeftJoinSingleKeyInt(other, leftKey, rightKey);
|
||||
ctFloat: Result := LeftJoinSingleKeyFloat(other, leftKey, rightKey);
|
||||
ctStr: Result := LeftJoinSingleKeyStr(other, leftKey, rightKey);
|
||||
ctBool: Result := LeftJoinSingleKeyBool(other, leftKey, rightKey);
|
||||
else
|
||||
Error(ER_UNSUPPORTED_COLUMN_TYPE, lt);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -1177,62 +1127,6 @@ begin
|
|||
Result := BuildLeftJoinResult(Self, other, leftIdx, rightIdx, leftKey, rightKey);
|
||||
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;
|
||||
begin
|
||||
var index := new Dictionary<string, List<integer>>;
|
||||
|
|
@ -1540,14 +1434,7 @@ begin
|
|||
);
|
||||
|
||||
ctFloat:
|
||||
BuildMergedFloatKeyColumnFromFullJoin(
|
||||
res,
|
||||
name,
|
||||
FloatColumn(col),
|
||||
FloatColumn(other.columns[ri]),
|
||||
leftArr,
|
||||
rightArr
|
||||
);
|
||||
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||
|
||||
ctStr:
|
||||
BuildMergedStrKeyColumnFromFullJoin(
|
||||
|
|
@ -1685,15 +1572,7 @@ begin
|
|||
IntColumn(other.columns[ri]),
|
||||
leftArr, rightArr
|
||||
);
|
||||
|
||||
ctFloat:
|
||||
BuildMergedFloatKeyColumnFromFullJoin(
|
||||
res, name,
|
||||
FloatColumn(col),
|
||||
FloatColumn(other.columns[ri]),
|
||||
leftArr, rightArr
|
||||
);
|
||||
|
||||
|
||||
ctStr:
|
||||
BuildMergedStrKeyColumnFromFullJoin(
|
||||
res, name,
|
||||
|
|
@ -1740,11 +1619,15 @@ begin
|
|||
if lt <> rt then
|
||||
Error(ER_JOIN_KEY_TYPE_MISMATCH);
|
||||
|
||||
if lt = ctFloat then
|
||||
Error(ER_JOIN_FLOAT_KEY_NOT_SUPPORTED);
|
||||
|
||||
case lt of
|
||||
ctInt: Result := JoinInnerSingleKeyInt(other, leftKey, rightKey);
|
||||
ctFloat: Result := JoinInnerSingleKeyFloat(other, leftKey, rightKey);
|
||||
ctStr: Result := JoinInnerSingleKeyStr(other, leftKey, rightKey);
|
||||
ctBool: Result := JoinInnerSingleKeyBool(other, leftKey, rightKey);
|
||||
else
|
||||
Error(ER_UNSUPPORTED_COLUMN_TYPE, lt);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -1823,49 +1706,6 @@ begin
|
|||
Result := BuildJoinResult(Self, other, leftIdx, rightIdx, leftKey, rightKey);
|
||||
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;
|
||||
begin
|
||||
var index := new Dictionary<string, List<integer>>;
|
||||
|
|
@ -2028,9 +1868,17 @@ end;
|
|||
function DataFrame.BuildJoinKeyLayout(keyIndices: array of integer): JoinKeyLayout;
|
||||
begin
|
||||
Result.ColIndices := keyIndices;
|
||||
Result.ColTypes := ArrGen(keyIndices.Length,
|
||||
i -> columns[keyIndices[i]].Info.ColType
|
||||
);
|
||||
Result.ColTypes := new ColumnType[keyIndices.Length];
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
begin
|
||||
for var i := 0 to columns.Count - 1 do
|
||||
if columns[i].Info.Name = name then
|
||||
exit(i);
|
||||
Error(ER_COLUMN_NOT_FOUND, name);
|
||||
Result := fSchema.IndexOf(name);
|
||||
end;
|
||||
|
||||
function DataFrame.HasColumn(name: string): boolean;
|
||||
|
|
@ -3121,12 +2966,7 @@ begin
|
|||
rightIdx[i] := right.fSchema.IndexOf(rightKeys[i]);
|
||||
end;
|
||||
|
||||
Result := BuildJoinSchema(
|
||||
right,
|
||||
leftIdx,
|
||||
rightIdx,
|
||||
'right_'
|
||||
);
|
||||
Result := BuildJoinSchema(right, leftIdx, rightIdx, 'right_');
|
||||
end;
|
||||
|
||||
function DataFrame.CreateEmptyBySchema(schema: DataFrameSchema): DataFrame;
|
||||
|
|
@ -3150,14 +2990,9 @@ begin
|
|||
Result := new DataFrame(cols, schema);
|
||||
end;
|
||||
|
||||
function DataFrame.GetColumnIndex(name: string): integer;
|
||||
begin
|
||||
Result := fschema.IndexOf(name); // если у вас есть такой метод
|
||||
end;
|
||||
|
||||
function DataFrame.GetColumn(name: string): Column;
|
||||
begin
|
||||
var idx := GetColumnIndex(name);
|
||||
var idx := ColumnIndex(name);
|
||||
Result := columns[idx];
|
||||
end;
|
||||
|
||||
|
|
@ -3212,20 +3047,8 @@ begin
|
|||
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
try
|
||||
data[i] := f(cur);
|
||||
except
|
||||
on e: Exception do
|
||||
begin
|
||||
data[i] := 0;
|
||||
valid[i] := False;
|
||||
i += 1;
|
||||
continue;
|
||||
end;
|
||||
end;
|
||||
|
||||
data[i] := f(cur);
|
||||
valid[i] := True;
|
||||
|
||||
i += 1;
|
||||
end;
|
||||
|
||||
|
|
@ -3251,20 +3074,8 @@ begin
|
|||
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
try
|
||||
data[i] := f(cur);
|
||||
except
|
||||
on e: Exception do
|
||||
begin
|
||||
data[i] := 0.0;
|
||||
valid[i] := False;
|
||||
i += 1;
|
||||
continue;
|
||||
end;
|
||||
end;
|
||||
|
||||
data[i] := f(cur);
|
||||
valid[i] := True;
|
||||
|
||||
i += 1;
|
||||
end;
|
||||
|
||||
|
|
@ -3290,20 +3101,8 @@ begin
|
|||
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
try
|
||||
data[i] := f(cur);
|
||||
except
|
||||
on e: Exception do
|
||||
begin
|
||||
data[i] := nil;
|
||||
valid[i] := False;
|
||||
i += 1;
|
||||
continue;
|
||||
end;
|
||||
end;
|
||||
|
||||
data[i] := f(cur);
|
||||
valid[i] := True;
|
||||
|
||||
i += 1;
|
||||
end;
|
||||
|
||||
|
|
@ -3329,20 +3128,8 @@ begin
|
|||
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
try
|
||||
data[i] := f(cur);
|
||||
except
|
||||
on e: Exception do
|
||||
begin
|
||||
data[i] := False;
|
||||
valid[i] := False;
|
||||
i += 1;
|
||||
continue;
|
||||
end;
|
||||
end;
|
||||
|
||||
data[i] := f(cur);
|
||||
valid[i] := True;
|
||||
|
||||
i += 1;
|
||||
end;
|
||||
|
||||
|
|
@ -3403,20 +3190,8 @@ begin
|
|||
end;
|
||||
|
||||
// --- вычисление
|
||||
try
|
||||
data[row] := f(cur);
|
||||
except
|
||||
on e: Exception do
|
||||
begin
|
||||
data[row] := 0.0;
|
||||
valid[row] := False;
|
||||
row += 1;
|
||||
continue;
|
||||
end;
|
||||
end;
|
||||
|
||||
data[row] := f(cur);
|
||||
valid[row] := True;
|
||||
|
||||
row += 1;
|
||||
end;
|
||||
|
||||
|
|
@ -3455,18 +3230,22 @@ begin
|
|||
|
||||
var cur := GetCursor;
|
||||
var row := 0;
|
||||
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
try
|
||||
data[row] := f(cur);
|
||||
valid[row] := True;
|
||||
except
|
||||
on e: Exception do
|
||||
begin
|
||||
data[row] := 0;
|
||||
valid[row] := False;
|
||||
end;
|
||||
// NA из исходных данных
|
||||
if not cur.IsValid(colIndex) then
|
||||
begin
|
||||
data[row] := 0;
|
||||
valid[row] := false;
|
||||
row += 1;
|
||||
continue;
|
||||
end;
|
||||
|
||||
// строгий расчёт
|
||||
data[row] := f(cur);
|
||||
valid[row] := True;
|
||||
|
||||
row += 1;
|
||||
end;
|
||||
|
||||
|
|
@ -4117,6 +3896,8 @@ end;
|
|||
procedure DataFrame.AssertSchemaConsistent;
|
||||
begin
|
||||
{$IFNDEF Test}
|
||||
|
||||
if columns.Count = 0 then exit;
|
||||
|
||||
// --- 1. одинаковая RowCount у всех столбцов ---
|
||||
var rc := columns[0].RowCount;
|
||||
|
|
@ -4136,7 +3917,7 @@ begin
|
|||
if not fSchema.HasColumn(name) then
|
||||
Error(ER_SCHEMA_COLUMN_MISSING, name);
|
||||
|
||||
var idx := GetColumnIndex(name);
|
||||
var idx := ColumnIndex(name);
|
||||
if idx <> i then
|
||||
Error(ER_SCHEMA_COLUMN_INDEX_INCONSISTENT, name, idx, i);
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ type
|
|||
Column = abstract class
|
||||
Info: ColumnInfo;
|
||||
public
|
||||
IsValid: array of boolean; // Флаги валидности (может быть nil)
|
||||
/// Пытается извлечь i-тое данное из столбца как числовое если это возможно
|
||||
function TryGetNumericValue(i: integer; var value: real): boolean; virtual; abstract;
|
||||
/// Возвращает количество строк в столбце
|
||||
|
|
@ -108,7 +109,6 @@ type
|
|||
IntColumn = class(Column)
|
||||
// Data и IsValid считаются immutable после создания
|
||||
Data: array of integer; // Данные столбца
|
||||
IsValid: array of boolean; // Флаги валидности (может быть nil)
|
||||
public
|
||||
constructor Create; begin end;
|
||||
constructor Create(name: string);
|
||||
|
|
@ -121,7 +121,6 @@ type
|
|||
/// Столбец вещественных чисел
|
||||
FloatColumn = class(Column)
|
||||
Data: array of real; // Данные столбца
|
||||
IsValid: array of boolean; // Флаги валидности
|
||||
public
|
||||
constructor Create; begin end;
|
||||
constructor Create(name: string);
|
||||
|
|
@ -135,7 +134,6 @@ type
|
|||
/// Столбец строк
|
||||
StrColumn = class(Column)
|
||||
Data: array of string; // Данные столбца
|
||||
IsValid: array of boolean; // Флаги валидности
|
||||
public
|
||||
constructor Create; begin end;
|
||||
constructor Create(name: string);
|
||||
|
|
@ -153,7 +151,6 @@ type
|
|||
/// Столбец булевых значений
|
||||
BoolColumn = class(Column)
|
||||
Data: array of boolean; // Данные столбца
|
||||
IsValid: array of boolean; // Флаги валидности
|
||||
public
|
||||
constructor Create; begin end;
|
||||
constructor Create(name: string);
|
||||
|
|
@ -177,9 +174,9 @@ type
|
|||
ColTypes: array of ColumnType;
|
||||
end;
|
||||
|
||||
// Нет Floats - по ним нельзя Join!!!
|
||||
JoinKey = record
|
||||
Ints: array of integer;
|
||||
Floats: array of real;
|
||||
Strs: array of string;
|
||||
Bools: array of boolean;
|
||||
function Equals(oth: object): boolean; override;
|
||||
|
|
@ -731,16 +728,12 @@ begin
|
|||
var other := JoinKey(oth);
|
||||
|
||||
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 Bools.Length <> other.Bools.Length then exit(false);
|
||||
|
||||
for var i := 0 to Ints.Length - 1 do
|
||||
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
|
||||
if Strs[i] <> other.Strs[i] then exit(false);
|
||||
|
||||
|
|
@ -757,9 +750,6 @@ begin
|
|||
foreach var v in Ints do
|
||||
h := h * 31 + v.GetHashCode;
|
||||
|
||||
foreach var v in Floats do
|
||||
h := h * 31 + v.GetHashCode;
|
||||
|
||||
foreach var v in Strs do
|
||||
h := h * 31 + (if v = nil then 0 else v.GetHashCode);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
// =============================================================
|
||||
/// Основной модуль библиотеки машинного обучения.
|
||||
/// Объединяет модели, метрики, валидацию и вспомогательные компоненты.
|
||||
unit MLABC;
|
||||
|
||||
// =============================================================
|
||||
// СТАТИСТИЧЕСКАЯ ПОЛИТИКА БИБЛИОТЕКИ ML PascalABC.NET
|
||||
//
|
||||
// В библиотеке используются следующие соглашения:
|
||||
|
|
@ -17,9 +21,20 @@
|
|||
// • алгоритмы ML — дисперсия генеральной совокупности
|
||||
// =============================================================
|
||||
|
||||
/// Основной модуль библиотеки машинного обучения.
|
||||
/// Объединяет модели, метрики, валидацию и вспомогательные компоненты.
|
||||
unit MLABC;
|
||||
// =============================================================
|
||||
// PIPELINES
|
||||
//
|
||||
// DataFrame-based:
|
||||
// DataPipeline
|
||||
// UDataPipeline
|
||||
//
|
||||
// Matrix/Vector-based:
|
||||
// MatrixPipeline
|
||||
// UMatrixPipeline
|
||||
//
|
||||
// Оба варианта являются равноправными и используются
|
||||
// в зависимости от представления данных.
|
||||
// =============================================================
|
||||
|
||||
interface
|
||||
|
||||
|
|
@ -74,7 +89,7 @@ type
|
|||
NormType = MLModelsABC.NormType;
|
||||
|
||||
Activations = MLModelsABC.Activations;
|
||||
Pipeline = MLModelsABC.Pipeline;
|
||||
Pipeline = MLModelsABC.MatrixPipeline;
|
||||
|
||||
LinearRegression = MLModelsABC.LinearRegression;
|
||||
LogisticRegression = MLModelsABC.LogisticRegression;
|
||||
|
|
@ -113,7 +128,7 @@ type
|
|||
IModel = MLCoreABC.IModel;
|
||||
ISupervisedModel = MLCoreABC.ISupervisedModel;
|
||||
IUnsupervisedModel = MLCoreABC.IUnsupervisedModel;
|
||||
UPipeline = MLModelsABC.UPipeline;
|
||||
UPipeline = MLModelsABC.UMatrixPipeline;
|
||||
UDataPipeline = MLPipelineABC.UDataPipeline;
|
||||
TaskKind = MLPipelineABC.TaskKind;
|
||||
|
||||
|
|
|
|||
|
|
@ -355,7 +355,9 @@ type
|
|||
fCheckConvergence: boolean;
|
||||
fMinImprovement: real;
|
||||
|
||||
fClassLabels: array of string; // В каждой модели классификации
|
||||
fClassLabels: array of string;
|
||||
|
||||
fUseFastExp: boolean;
|
||||
|
||||
function GetWeights: Matrix;
|
||||
function GetIntercept: Vector;
|
||||
|
|
@ -369,7 +371,8 @@ type
|
|||
epochs: integer := 1000;
|
||||
tol: real := 1e-6;
|
||||
checkConvergence: boolean := true;
|
||||
minImprovement: real := 1e-8
|
||||
minImprovement: real := 1e-8;
|
||||
useFastExp: boolean := True
|
||||
);
|
||||
|
||||
/// Обучает модель логистической регрессии.
|
||||
|
|
@ -380,7 +383,7 @@ type
|
|||
/// Примечание:
|
||||
/// • обученное состояние модели НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||
|
||||
|
||||
/// Возвращает матрицу вероятностей (m x k).
|
||||
function PredictProba(X: Matrix): Matrix;
|
||||
|
||||
|
|
@ -1571,9 +1574,6 @@ type
|
|||
fTouched: array of integer;
|
||||
fEpoch: integer;
|
||||
|
||||
fSampleIdx: array of integer;
|
||||
fSampleSize: integer;
|
||||
|
||||
public
|
||||
/// Создаёт классификатор kNN.
|
||||
/// k — число ближайших соседей (k > 0).
|
||||
|
|
@ -1589,14 +1589,22 @@ type
|
|||
/// • обученное состояние модели (обучающая выборка) НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): ISupervisedModel; override;
|
||||
|
||||
/// Выполняет предсказание меток классов для объектов X.
|
||||
/// Возвращает вектор предсказанных меток
|
||||
/// Выполняет предсказание меток классов для объектов X.
|
||||
/// Возвращает вектор предсказанных меток.
|
||||
/// Не является потокобезопасным: не вызывать одновременно из нескольких потоков
|
||||
/// для одного экземпляра модели.
|
||||
function Predict(X: Matrix): Vector; override;
|
||||
|
||||
/// Выполняет предсказание меток классов для объектов X.
|
||||
/// Возвращает массив индексов классов.
|
||||
/// Не является потокобезопасным: не вызывать одновременно из нескольких потоков
|
||||
/// для одного экземпляра модели.
|
||||
function PredictLabels(X: Matrix): array of integer;
|
||||
|
||||
/// Возвращает матрицу вероятностей размера (nSamples × nClasses).
|
||||
/// Столбцы соответствуют классам в порядке, возвращаемом GetClasses()
|
||||
/// Столбцы соответствуют классам в порядке, возвращаемом GetClasses().
|
||||
/// Не является потокобезопасным: не вызывать одновременно из нескольких потоков
|
||||
/// для одного экземпляра модели.
|
||||
function PredictProba(X: Matrix): Matrix;
|
||||
|
||||
/// Возвращает массив меток классов в порядке столбцов PredictProba
|
||||
|
|
@ -1839,7 +1847,7 @@ type
|
|||
|
||||
{$endregion Models}
|
||||
|
||||
{$region Pipeline}
|
||||
{$region MatrixPipeline}
|
||||
/// Последовательный конвейер машинного обучения (supervised).
|
||||
/// Гарантирует строгий порядок выполнения шагов:
|
||||
/// [преобразователи] → [модель].
|
||||
|
|
@ -1854,7 +1862,10 @@ type
|
|||
///
|
||||
/// Обеспечивает единый интерфейс Fit(X, y) / Predict(X)
|
||||
/// и воспроизводимость полного процесса обучения
|
||||
Pipeline = class(ISupervisedModel)
|
||||
///
|
||||
/// MatrixPipeline используется, когда данные уже представлены
|
||||
/// в виде Matrix X и Vector y
|
||||
MatrixPipeline = class(ISupervisedModel)
|
||||
private
|
||||
fTransformers: List<ITransformer>;
|
||||
fModel: ISupervisedModel;
|
||||
|
|
@ -1870,7 +1881,7 @@ type
|
|||
constructor Create;
|
||||
|
||||
{
|
||||
// Pipeline.Build используется, когда данные уже представлены
|
||||
// MatrixPipeline.Build используется, когда данные уже представлены
|
||||
// в виде числовой матрицы признаков X и вектора целевой переменной y.
|
||||
// В этом случае DataFrame и препроцессоры уровня таблицы не требуются.
|
||||
//
|
||||
|
|
@ -1881,7 +1892,7 @@ type
|
|||
// • подбор гиперпараметров
|
||||
// • тестирование моделей
|
||||
//
|
||||
// Pipeline объединяет несколько матричных преобразований (ITransformer)
|
||||
// MatrixPipeline объединяет несколько матричных преобразований (ITransformer)
|
||||
// и модель (IModel) в единый объект, который можно обучать и использовать
|
||||
// для предсказаний.
|
||||
//
|
||||
|
|
@ -1892,13 +1903,13 @@ type
|
|||
// перед преобразованием данных в Matrix/Vector.
|
||||
|
||||
var pipe1 :=
|
||||
Pipeline.Build(
|
||||
MatrixPipeline.Build(
|
||||
new StandardScaler,
|
||||
new LogisticRegression
|
||||
);
|
||||
|
||||
var pipe2 :=
|
||||
Pipeline.Build(
|
||||
MatrixPipeline.Build(
|
||||
new StandardScaler,
|
||||
new PCATransformer(2),
|
||||
new LogisticRegression
|
||||
|
|
@ -1931,13 +1942,13 @@ type
|
|||
/// сначала преобразователи, затем модель.
|
||||
/// Последний шаг обязан быть моделью (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 признаков).
|
||||
|
|
@ -1962,6 +1973,7 @@ type
|
|||
/// Показывает, был ли пайплайн обучен (вызван метод Fit).
|
||||
property IsFitted: boolean read fFitted;
|
||||
|
||||
/// Возвращает строковое представление пайплайна.
|
||||
function ToString: string; override;
|
||||
|
||||
/// Создаёт копию пайплайна с теми же шагами и моделью.
|
||||
|
|
@ -1979,19 +1991,48 @@ type
|
|||
function Name: string := Self.GetType.Name;
|
||||
end;
|
||||
|
||||
UPipeline = class(IUnsupervisedModel)
|
||||
/// Последовательный конвейер машинного обучения (unsupervised).
|
||||
/// Гарантирует строгий порядок выполнения шагов:
|
||||
/// [преобразователи].
|
||||
///
|
||||
/// Поддерживает:
|
||||
/// • преобразователи без учёта целевой переменной (unsupervised).
|
||||
///
|
||||
/// Все преобразователи применяются последовательно к признакам X.
|
||||
///
|
||||
/// Обеспечивает единый интерфейс Fit(X) / Transform(X)
|
||||
/// и воспроизводимость полного процесса преобразования данных.
|
||||
///
|
||||
/// UMatrixPipeline используется, когда данные уже представлены
|
||||
/// в виде Matrix X и отсутствует целевая переменная
|
||||
UMatrixPipeline = class(IUnsupervisedModel)
|
||||
private
|
||||
fTransformers: List<ITransformer>;
|
||||
fModel: IModel;
|
||||
fFitted: boolean;
|
||||
public
|
||||
/// Создаёт конвейер машинного обучения для заданной модели:
|
||||
/// • model — модель, которая будет применена
|
||||
/// после последовательного применения всех преобразователей.
|
||||
/// Модель должна реализовывать интерфейс IModel (без учёта целевой переменной)
|
||||
constructor Create(model: IModel);
|
||||
|
||||
/// Создаёт пустой пайплайн (конвейер машинного обучения).
|
||||
/// Модель должна быть установлена через SetModel
|
||||
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 признаков).
|
||||
|
|
@ -2001,11 +2042,30 @@ type
|
|||
/// Примечание:
|
||||
/// • обученное состояние пайплайна НЕ копируется методом Clone
|
||||
function Fit(X: Matrix): IUnsupervisedModel;
|
||||
|
||||
/// Применяет последовательность преобразований к данным.
|
||||
/// X — матрица m × n (m объектов, n признаков).
|
||||
/// Последовательно применяет все трансформеры к данным,
|
||||
/// после чего возвращает преобразованную матрицу.
|
||||
///
|
||||
/// Примечание:
|
||||
/// • обученное состояние пайплайна НЕ копируется методом Clone
|
||||
function Transform(X: Matrix): Matrix;
|
||||
|
||||
/// Применяет пайплайн и возвращает результат модели.
|
||||
/// X — матрица m × n (m объектов, n признаков).
|
||||
/// После применения всех преобразований вызывает модель,
|
||||
/// реализующую интерфейс IModel.
|
||||
///
|
||||
/// Примечание:
|
||||
/// • семантика результата зависит от конкретной модели
|
||||
/// (например, кластерные метки, оценки и т.д.)
|
||||
function Predict(X: Matrix): Vector;
|
||||
|
||||
/// Показывает, был ли пайплайн обучен (вызван метод Fit у шагов пайплайна).
|
||||
property IsFitted: boolean read fFitted;
|
||||
|
||||
/// Возвращает строковое представление пайплайна.
|
||||
function ToString: string; override;
|
||||
|
||||
/// Создаёт копию пайплайна с теми же шагами и моделью.
|
||||
|
|
@ -2016,13 +2076,13 @@ type
|
|||
/// • Все шаги и модель клонируются через их Clone (config-only)
|
||||
///
|
||||
/// Назначение:
|
||||
/// • использование в CrossValidate и Pipeline
|
||||
/// • использование в CrossValidate и MatrixPipeline
|
||||
/// • обеспечивает независимое переобучение пайплайна
|
||||
function Clone: IModel;
|
||||
function Name: string := Self.GetType.Name;
|
||||
end;
|
||||
|
||||
{$endregion Pipeline}
|
||||
{$endregion MatrixPipeline}
|
||||
|
||||
{$region Transformers}
|
||||
|
||||
|
|
@ -2078,7 +2138,7 @@ type
|
|||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||
///
|
||||
/// Назначение:
|
||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
||||
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||
/// • гарантирует независимое переобучение трансформеров
|
||||
///
|
||||
/// Примечание:
|
||||
|
|
@ -2136,7 +2196,7 @@ type
|
|||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||
///
|
||||
/// Назначение:
|
||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
||||
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||
/// • гарантирует независимое переобучение трансформеров
|
||||
///
|
||||
/// Примечание:
|
||||
|
|
@ -2190,7 +2250,7 @@ type
|
|||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||
///
|
||||
/// Назначение:
|
||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
||||
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||
/// • гарантирует независимое переобучение трансформеров
|
||||
///
|
||||
/// Примечание:
|
||||
|
|
@ -2243,7 +2303,7 @@ type
|
|||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||
///
|
||||
/// Назначение:
|
||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
||||
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||
/// • гарантирует независимое переобучение трансформеров
|
||||
///
|
||||
/// Примечание:
|
||||
|
|
@ -2340,7 +2400,7 @@ type
|
|||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||
///
|
||||
/// Назначение:
|
||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
||||
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||
/// • гарантирует независимое переобучение трансформеров
|
||||
///
|
||||
/// Примечание:
|
||||
|
|
@ -2398,7 +2458,7 @@ type
|
|||
/// • Возвращаемый объект эквивалентен новому экземпляру после Create(...)
|
||||
///
|
||||
/// Назначение:
|
||||
/// • использование в CrossValidate, GridSearch и Pipeline
|
||||
/// • использование в CrossValidate, GridSearch и MatrixPipeline
|
||||
/// • гарантирует независимое переобучение трансформеров
|
||||
///
|
||||
/// Примечание:
|
||||
|
|
@ -3100,7 +3160,7 @@ end;
|
|||
//-----------------------------
|
||||
|
||||
constructor LogisticRegression.Create(lambda: real; learningRate: real; epochs: integer;
|
||||
tol: real; checkConvergence: boolean; minImprovement: real);
|
||||
tol: real; checkConvergence: boolean; minImprovement: real; useFastExp: boolean);
|
||||
begin
|
||||
fLambda := lambda;
|
||||
fLearningRate := learningRate;
|
||||
|
|
@ -3109,8 +3169,19 @@ begin
|
|||
fTol := tol;
|
||||
fCheckConvergence := checkConvergence;
|
||||
fMinImprovement := minImprovement;
|
||||
fUseFastExp := useFastExp
|
||||
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;
|
||||
begin
|
||||
if X = nil then
|
||||
|
|
@ -3179,6 +3250,228 @@ begin
|
|||
fIndexToClass[i] := unique[i];
|
||||
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
|
||||
fW := new Matrix(p, fClassCount);
|
||||
fIntercept := new Vector(fClassCount);
|
||||
|
|
@ -3301,7 +3594,7 @@ begin
|
|||
|
||||
fFitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
end;}
|
||||
|
||||
function LogisticRegression.PredictProba(X: Matrix): Matrix;
|
||||
begin
|
||||
|
|
@ -3394,7 +3687,8 @@ begin
|
|||
fEpochs,
|
||||
fTol,
|
||||
fCheckConvergence,
|
||||
fMinImprovement
|
||||
fMinImprovement,
|
||||
fUseFastExp
|
||||
);
|
||||
end;
|
||||
|
||||
|
|
@ -4050,6 +4344,7 @@ begin
|
|||
fMinSamplesSplit,
|
||||
fMinSamplesLeaf,
|
||||
fCriterion,
|
||||
fClassCount,
|
||||
fMaxFeatures,
|
||||
fRandomSeed
|
||||
);
|
||||
|
|
@ -7256,15 +7551,6 @@ begin
|
|||
// --- copy train data
|
||||
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
|
||||
// =========================================================
|
||||
|
|
@ -8269,17 +8555,17 @@ end;
|
|||
function DBSCAN.ClustersCount: integer := fClusterCount;
|
||||
|
||||
//-----------------------------
|
||||
// Pipeline
|
||||
// MatrixPipeline
|
||||
//-----------------------------
|
||||
|
||||
constructor Pipeline.Create;
|
||||
constructor MatrixPipeline.Create;
|
||||
begin
|
||||
fTransformers := new List<ITransformer>;
|
||||
fModel := nil;
|
||||
fFitted := false;
|
||||
end;
|
||||
|
||||
constructor Pipeline.Create(model: ISupervisedModel);
|
||||
constructor MatrixPipeline.Create(model: ISupervisedModel);
|
||||
begin
|
||||
Create;
|
||||
if model = nil then
|
||||
|
|
@ -8287,7 +8573,7 @@ begin
|
|||
fModel := model;
|
||||
end;
|
||||
|
||||
class function Pipeline.Build(params steps: array of IPipelineStep): Pipeline;
|
||||
class function MatrixPipeline.Build(params steps: array of IPipelineStep): MatrixPipeline;
|
||||
begin
|
||||
if (steps = nil) or (Length(steps) = 0) then
|
||||
ArgumentError(ER_PIPELINE_NO_STEPS);
|
||||
|
|
@ -8301,7 +8587,7 @@ begin
|
|||
if not (last is ISupervisedModel) then
|
||||
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
|
||||
|
|
@ -8320,7 +8606,7 @@ begin
|
|||
Result := pipe;
|
||||
end;
|
||||
|
||||
function Pipeline.Add(t: ITransformer): Pipeline;
|
||||
function MatrixPipeline.Add(t: ITransformer): MatrixPipeline;
|
||||
begin
|
||||
if t = nil then
|
||||
ArgumentError(ER_TRANSFORMER_NULL);
|
||||
|
|
@ -8329,7 +8615,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function Pipeline.SetModel(m: ISupervisedModel): Pipeline;
|
||||
function MatrixPipeline.SetModel(m: ISupervisedModel): MatrixPipeline;
|
||||
begin
|
||||
if m = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
|
@ -8338,7 +8624,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function Pipeline.Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||
function MatrixPipeline.Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||
begin
|
||||
if fModel = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
|
@ -8386,7 +8672,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function Pipeline.Transform(X: Matrix): Matrix;
|
||||
function MatrixPipeline.Transform(X: Matrix): Matrix;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
|
@ -8410,7 +8696,7 @@ begin
|
|||
Result := Xt;
|
||||
end;
|
||||
|
||||
function Pipeline.Predict(X: Matrix): Vector;
|
||||
function MatrixPipeline.Predict(X: Matrix): Vector;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
|
@ -8422,7 +8708,7 @@ begin
|
|||
Result := fModel.Predict(Xt);
|
||||
end;
|
||||
|
||||
function Pipeline.PredictProba(X: Matrix): Matrix;
|
||||
function MatrixPipeline.PredictProba(X: Matrix): Matrix;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
|
@ -8439,9 +8725,9 @@ begin
|
|||
.PredictProba(Xt);
|
||||
end;
|
||||
|
||||
function Pipeline.ToString: string;
|
||||
function MatrixPipeline.ToString: string;
|
||||
begin
|
||||
var sb := 'Pipeline (' +
|
||||
var sb := 'MatrixPipeline (' +
|
||||
(if fFitted then 'trained' else 'not trained') + '):' + NewLine;
|
||||
|
||||
var idx := 1;
|
||||
|
|
@ -8458,12 +8744,12 @@ begin
|
|||
Result := sb;
|
||||
end;
|
||||
|
||||
function Pipeline.Clone: IModel;
|
||||
function MatrixPipeline.Clone: IModel;
|
||||
begin
|
||||
if fModel = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
||||
var p := new Pipeline;
|
||||
var p := new MatrixPipeline;
|
||||
|
||||
foreach var t in fTransformers do
|
||||
p.Add(t.Clone);
|
||||
|
|
@ -8479,16 +8765,16 @@ begin
|
|||
end;
|
||||
|
||||
//-----------------------------
|
||||
// UPipeline
|
||||
// UMatrixPipeline
|
||||
//-----------------------------
|
||||
constructor UPipeline.Create;
|
||||
constructor UMatrixPipeline.Create;
|
||||
begin
|
||||
fTransformers := new List<ITransformer>;
|
||||
fModel := nil;
|
||||
fFitted := false;
|
||||
end;
|
||||
|
||||
constructor UPipeline.Create(model: IModel);
|
||||
constructor UMatrixPipeline.Create(model: IModel);
|
||||
begin
|
||||
Create;
|
||||
if model = nil then
|
||||
|
|
@ -8496,7 +8782,7 @@ begin
|
|||
fModel := model;
|
||||
end;
|
||||
|
||||
class function UPipeline.Build(params steps: array of IPipelineStep): UPipeline;
|
||||
class function UMatrixPipeline.Build(params steps: array of IPipelineStep): UMatrixPipeline;
|
||||
begin
|
||||
if (steps = nil) or (Length(steps) = 0) then
|
||||
ArgumentError(ER_PIPELINE_NO_STEPS);
|
||||
|
|
@ -8509,7 +8795,7 @@ begin
|
|||
if not (last is IModel) then
|
||||
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
|
||||
begin
|
||||
|
|
@ -8527,7 +8813,7 @@ begin
|
|||
Result := pipe;
|
||||
end;
|
||||
|
||||
function UPipeline.Add(t: ITransformer): UPipeline;
|
||||
function UMatrixPipeline.Add(t: ITransformer): UMatrixPipeline;
|
||||
begin
|
||||
if t = nil then
|
||||
ArgumentError(ER_TRANSFORMER_NULL);
|
||||
|
|
@ -8536,7 +8822,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function UPipeline.SetModel(m: IModel): UPipeline;
|
||||
function UMatrixPipeline.SetModel(m: IModel): UMatrixPipeline;
|
||||
begin
|
||||
if m = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
|
@ -8545,7 +8831,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function UPipeline.Fit(X: Matrix): IUnsupervisedModel;
|
||||
function UMatrixPipeline.Fit(X: Matrix): IUnsupervisedModel;
|
||||
begin
|
||||
if fModel = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
|
@ -8585,7 +8871,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function UPipeline.Transform(X: Matrix): Matrix;
|
||||
function UMatrixPipeline.Transform(X: Matrix): Matrix;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
|
@ -8609,7 +8895,7 @@ begin
|
|||
Result := Xt;
|
||||
end;
|
||||
|
||||
function UPipeline.Predict(X: Matrix): Vector;
|
||||
function UMatrixPipeline.Predict(X: Matrix): Vector;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
|
@ -8625,9 +8911,9 @@ begin
|
|||
Result := (fModel as IPredictiveModel).Predict(Xt);
|
||||
end;
|
||||
|
||||
function UPipeline.ToString: string;
|
||||
function UMatrixPipeline.ToString: string;
|
||||
begin
|
||||
var sb := 'UPipeline (' +
|
||||
var sb := 'UMatrixPipeline (' +
|
||||
(if fFitted then 'trained' else 'not trained') + '):' + NewLine;
|
||||
|
||||
var idx := 1;
|
||||
|
|
@ -8644,12 +8930,12 @@ begin
|
|||
Result := sb;
|
||||
end;
|
||||
|
||||
function UPipeline.Clone: IModel;
|
||||
function UMatrixPipeline.Clone: IModel;
|
||||
begin
|
||||
if fModel = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
||||
var p := new UPipeline;
|
||||
var p := new UMatrixPipeline;
|
||||
|
||||
foreach var t in fTransformers do
|
||||
p.Add(t.Clone);
|
||||
|
|
|
|||
|
|
@ -298,7 +298,8 @@ const
|
|||
'Model is not a classifier';
|
||||
ER_MODEL_CLONE_TYPE =
|
||||
'Clone модели вернул неподдерживаемый тип!!Model Clone returned unsupported type';
|
||||
|
||||
ER_INVALID_MODEL_TYPE =
|
||||
'Clone модели вернул неподдерживаемый тип (ожидается {0})!!Model Clone returned unsupported type (expected {0})';
|
||||
//-----------------------------
|
||||
// DataPipeline
|
||||
//-----------------------------
|
||||
|
|
@ -1143,7 +1144,14 @@ begin
|
|||
|
||||
// --- модель
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ function IntsToLabels(a: array of integer): Vector;
|
|||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
/// Используется при обучении моделей и визуализации.
|
||||
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||
function EncodeLabels(labels: array of string): array of integer;
|
||||
|
||||
/// Кодирует строковые метки классов в целочисленные индексы.
|
||||
|
|
@ -47,6 +48,7 @@ function EncodeLabels(labels: array of string): array of integer;
|
|||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
||||
/// Используется при обучении моделей и визуализации
|
||||
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||
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 возвращается массив уникальных значений в порядке кодирования.
|
||||
/// Используется при обучении моделей и визуализации
|
||||
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||
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.
|
||||
/// Если встречается неизвестная метка — выбрасывается исключение.
|
||||
/// Используется для применения кодирования к тестовым данным (Transform).
|
||||
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||
function TransformLabels(labels: array of string; classes: array of string): array of integer;
|
||||
|
||||
/// Преобразует целочисленные метки классов в индексы (0,1,2,...)
|
||||
|
|
@ -68,11 +72,13 @@ function TransformLabels(labels: array of string; classes: array of string): arr
|
|||
/// classes должен быть получен из EncodeLabelsInt.
|
||||
/// Если встречается неизвестное значение — выбрасывается исключение.
|
||||
/// Используется для применения кодирования к тестовым данным (Transform).
|
||||
/// Предполагается, что входные данные уже очищены от пропущенных значений.
|
||||
function TransformLabelsInt(labels: array of integer; classes: array of integer): array of integer;
|
||||
|
||||
/// Преобразует целочисленные индексы классов обратно в строковые метки.
|
||||
/// Массив classes задаёт соответствие: classes[i] — имя класса с индексом i.
|
||||
/// Используется для получения текстовых предсказаний моделей.
|
||||
/// Предполагается, что входные данные уже корректны.
|
||||
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;
|
||||
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;
|
||||
|
||||
function CloneOrNil(v: Vector): Vector;
|
||||
|
|
|
|||
|
|
@ -227,6 +227,9 @@ type
|
|||
/// Ожидается, что yTrue и yPred содержат закодированные метки классов
|
||||
/// (целые значения 0..K-1, допускается хранение в Vector типа real).
|
||||
/// Для строковых меток необходимо предварительно применить EncodeLabels
|
||||
///
|
||||
/// Метки классов упорядочиваются по возрастанию для обеспечения
|
||||
/// стабильного и предсказуемого представления результатов
|
||||
ConfusionMatrix = class
|
||||
private
|
||||
fMatrix: array[,] of integer;
|
||||
|
|
|
|||
|
|
@ -11641,103 +11641,128 @@ end;
|
|||
// Partition, ZipTuple, UnZipTuple, Interleave, Numerate, Tabulate, Pairwise, Batch
|
||||
// Дополнения 2024: Zip - синоним ZipTuple
|
||||
|
||||
/// Возвращает первый элемент последовательности с минимальным значением ключа
|
||||
/// Возвращает первый элемент последовательности с минимальным значением ключа
|
||||
function MinBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
||||
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;
|
||||
while enumerator.MoveNext() do
|
||||
begin
|
||||
var currentElement := enumerator.Current;
|
||||
var currentKey := keySelector(currentElement);
|
||||
if comp.Compare(currentKey,minKey) < 0 then
|
||||
begin
|
||||
minKey := currentKey;
|
||||
minElement := currentElement;
|
||||
end;
|
||||
end;
|
||||
var minElement: T;
|
||||
var minKey: TKey;
|
||||
var hasValue := False;
|
||||
|
||||
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;
|
||||
end;
|
||||
|
||||
/// Возвращает первый элемент последовательности с максимальным значением ключа
|
||||
function MaxBy<T, TKey>(Self: sequence of T; keySelector: T -> TKey): T; extensionmethod;
|
||||
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;
|
||||
while enumerator.MoveNext() do
|
||||
begin
|
||||
var currentElement := enumerator.Current;
|
||||
var currentKey := keySelector(currentElement);
|
||||
if comp.Compare(currentKey,maxKey) > 0 then
|
||||
begin
|
||||
maxKey := currentKey;
|
||||
maxElement := currentElement;
|
||||
end;
|
||||
end;
|
||||
var maxElement: T;
|
||||
var maxKey: TKey;
|
||||
var hasValue := False;
|
||||
|
||||
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;
|
||||
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
|
||||
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;
|
||||
while enumerator.MoveNext() do
|
||||
begin
|
||||
var currentElement := enumerator.Current;
|
||||
var currentKey := keySelector(currentElement);
|
||||
if comp.Compare(currentKey,minKey) <= 0 then
|
||||
begin
|
||||
minKey := currentKey;
|
||||
minElement := currentElement;
|
||||
end;
|
||||
end;
|
||||
var minElement: T;
|
||||
var minKey: TKey;
|
||||
var hasValue := False;
|
||||
|
||||
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;
|
||||
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
|
||||
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;
|
||||
while enumerator.MoveNext() do
|
||||
begin
|
||||
var currentElement := enumerator.Current;
|
||||
var currentKey := keySelector(currentElement);
|
||||
if comp.Compare(currentKey,maxKey) > 0 then
|
||||
begin
|
||||
maxKey := currentKey;
|
||||
maxElement := currentElement;
|
||||
end;
|
||||
end;
|
||||
var maxElement: T;
|
||||
var maxKey: TKey;
|
||||
var hasValue := False;
|
||||
|
||||
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;
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -323,11 +323,11 @@ begin
|
|||
var cnt := pair.Value.Count;
|
||||
|
||||
// Класс может иметь меньше объектов, чем число фолдов.
|
||||
// В этом случае он не будет представлен во всех фолдах.
|
||||
// Это допустимо, но может привести к ошибкам при обучении модели.
|
||||
|
||||
//if cnt < k then
|
||||
// ArgumentError(ER_STRATIFIED_CLASS_TOO_SMALL, cls, cnt, k);
|
||||
// В библиотеке принята строгая политика: такие случаи считаются ошибкой,
|
||||
// так как не гарантируется присутствие класса во всех train-fold.
|
||||
// Поэтому выполняется fail-fast проверка (см. ниже).
|
||||
if cnt < k then
|
||||
ArgumentError(ER_STRATIFIED_CLASS_TOO_SMALL, cls, cnt, k);
|
||||
end;
|
||||
|
||||
// --- 2. Контейнеры фолдов
|
||||
|
|
|
|||
Loading…
Reference in a new issue