ML - рефакторинг Clone, IsValid

This commit is contained in:
Mikhalkovich Stanislav 2026-04-14 13:23:08 +03:00
parent 9cf33b094c
commit 94927c73e1
7 changed files with 918 additions and 893 deletions

View file

@ -1,6 +1,11 @@
// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
// DataFrameABC v.1.1
// =============================================================
// СТАТИСТИЧЕСКОЕ СОГЛАШЕНИЕ (DataFrame)
//
// Используется выборочная дисперсия (деление на n - 1),
// как принято в описательной статистике.
//
// См. статистическую политику в модуле MLABC.
// =============================================================
/// Стандартный модуль для работы с табличными данными (датасетами)
/// !! DataFrame module for tabular data processing
@ -95,7 +100,6 @@ type
function GetColumn(name: string): Column;
function CloneWithCopiedColumns: DataFrame;
static procedure EnsureValidArray(var valid: array of boolean; rowCount, filledCount: integer);
public
/// Создает пустой DataFrame
constructor Create;
@ -692,17 +696,17 @@ begin
if li >= 0 then
begin
data[i] := leftCol.Data[li];
valid[i] := if leftCol.IsValid = nil then true else leftCol.IsValid[li];
valid[i] := leftCol.IsValid[li];
end
else if ri >= 0 then
begin
data[i] := rightCol.Data[ri];
valid[i] := if rightCol.IsValid = nil then true else rightCol.IsValid[ri];
valid[i] := rightCol.IsValid[ri];
end
else
begin
data[i] := 0;
valid[i] := false;
valid[i] := False;
end;
end;
@ -729,17 +733,17 @@ begin
if li >= 0 then
begin
data[i] := leftCol.Data[li];
valid[i] := if leftCol.IsValid = nil then true else leftCol.IsValid[li];
valid[i] := leftCol.IsValid[li];
end
else if ri >= 0 then
begin
data[i] := rightCol.Data[ri];
valid[i] := if rightCol.IsValid = nil then true else rightCol.IsValid[ri];
valid[i] := rightCol.IsValid[ri];
end
else
begin
data[i] := 0.0;
valid[i] := false;
valid[i] := False;
end;
end;
@ -766,17 +770,17 @@ begin
if li >= 0 then
begin
data[i] := leftCol.Data[li];
valid[i] := if leftCol.IsValid = nil then true else leftCol.IsValid[li];
valid[i] := leftCol.IsValid[li];
end
else if ri >= 0 then
begin
data[i] := rightCol.Data[ri];
valid[i] := if rightCol.IsValid = nil then true else rightCol.IsValid[ri];
valid[i] := rightCol.IsValid[ri];
end
else
begin
data[i] := '';
valid[i] := false;
valid[i] := False;
end;
end;
@ -803,12 +807,12 @@ begin
if li >= 0 then
begin
data[i] := leftCol.Data[li];
valid[i] := if leftCol.IsValid = nil then true else leftCol.IsValid[li];
valid[i] := leftCol.IsValid[li];
end
else if ri >= 0 then
begin
data[i] := rightCol.Data[ri];
valid[i] := if rightCol.IsValid = nil then true else rightCol.IsValid[ri];
valid[i] := rightCol.IsValid[ri];
end
else
begin
@ -838,12 +842,12 @@ begin
if j < 0 then
begin
data[i] := 0;
valid[i] := false;
valid[i] := False;
end
else
begin
data[i] := src.Data[j];
valid[i] := if src.IsValid = nil then true else src.IsValid[j];
valid[i] := src.IsValid[j];
end;
end;
@ -868,12 +872,12 @@ begin
if j < 0 then
begin
data[i] := 0.0;
valid[i] := false;
valid[i] := False;
end
else
begin
data[i] := src.Data[j];
valid[i] := if src.IsValid = nil then true else src.IsValid[j];
valid[i] := src.IsValid[j];
end;
end;
@ -898,12 +902,12 @@ begin
if j < 0 then
begin
data[i] := '';
valid[i] := false;
valid[i] := False;
end
else
begin
data[i] := src.Data[j];
valid[i] := if src.IsValid = nil then true else src.IsValid[j];
valid[i] := src.IsValid[j];
end;
end;
@ -927,13 +931,13 @@ begin
if j < 0 then
begin
data[i] := false;
valid[i] := false;
data[i] := False;
valid[i] := False;
end
else
begin
data[i] := src.Data[j];
valid[i] := if src.IsValid = nil then true else src.IsValid[j];
valid[i] := src.IsValid[j];
end;
end;
@ -2223,21 +2227,10 @@ end;
procedure DataFrame.AddIntColumn(name: string; data: array of integer; valid: array of boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
if (columns.Count > 0) and (Length(data) <> RowCount) then
DimensionError(ER_ADD_COLUMN_ROW_MISMATCH);
var c := new IntColumn;
c.Info := new ColumnInfo(name, ctInt);
c.Data := data;
if valid = nil then
c.IsValid := [True] * data.Length
else
begin
if valid.Length <> data.Length then
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
c.IsValid := valid;
end;
var c := new IntColumn(name, data, valid);
columns.Add(c);
RebuildSchema;
@ -2245,21 +2238,10 @@ end;
procedure DataFrame.AddFloatColumn(name: string; data: array of real; valid: array of boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
if (columns.Count > 0) and (Length(data) <> RowCount) then
DimensionError(ER_ADD_COLUMN_ROW_MISMATCH);
var c := new FloatColumn;
c.Info := new ColumnInfo(name, ctFloat);
c.Data := data;
if valid = nil then
c.IsValid := [True] * data.Length
else
begin
if valid.Length <> data.Length then
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
c.IsValid := valid;
end;
var c := new FloatColumn(name, data, valid);
columns.Add(c);
RebuildSchema;
@ -2267,21 +2249,10 @@ end;
procedure DataFrame.AddStrColumn(name: string; data: array of string; valid: array of boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
if (columns.Count > 0) and (Length(data) <> RowCount) then
DimensionError(ER_ADD_COLUMN_ROW_MISMATCH);
var c := new StrColumn;
c.Info := new ColumnInfo(name, ctStr);
c.Data := data;
if valid = nil then
c.IsValid := [True] * data.Length
else
begin
if valid.Length <> data.Length then
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
c.IsValid := valid;
end;
var c := new StrColumn(name, data, valid);
columns.Add(c);
RebuildSchema;
@ -2295,27 +2266,15 @@ end;
procedure DataFrame.AddBoolColumn(name: string; data: array of boolean; valid: array of boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
if (columns.Count > 0) and (Length(data) <> RowCount) then
DimensionError(ER_ADD_COLUMN_ROW_MISMATCH);
var c := new BoolColumn;
c.Info := new ColumnInfo(name, ctBool);
c.Data := data;
if valid = nil then
c.IsValid := [True] * data.Length
else
begin
if valid.Length <> data.Length then
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
c.IsValid := valid;
end;
var c := new BoolColumn(name, data, valid);
columns.Add(c);
RebuildSchema;
end;
procedure DataFrame.CheckColumnIndex(colIndex: integer);
begin
if (colIndex < 0) or (colIndex >= ColumnCount) then
@ -2917,16 +2876,12 @@ begin
var src := IntColumn(col);
var data := new integer[newCount];
var valid: array of boolean := nil;
if src.IsValid <> nil then
valid := new boolean[newCount];
var valid := new boolean[newCount];
for var k := 0 to newCount - 1 do
begin
var i := mask[k];
data[k] := src.Data[i];
if valid <> nil then
valid[k] := src.IsValid[i];
end;
@ -2939,16 +2894,12 @@ begin
var src := FloatColumn(col);
var data := new real[newCount];
var valid: array of boolean := nil;
if src.IsValid <> nil then
valid := new boolean[newCount];
var valid := new boolean[newCount];
for var k := 0 to newCount - 1 do
begin
var i := mask[k];
data[k] := src.Data[i];
if valid <> nil then
valid[k] := src.IsValid[i];
end;
@ -2961,16 +2912,12 @@ begin
var src := StrColumn(col);
var data := new string[newCount];
var valid: array of boolean := nil;
if src.IsValid <> nil then
valid := new boolean[newCount];
var valid := new boolean[newCount];
for var k := 0 to newCount - 1 do
begin
var i := mask[k];
data[k] := src.Data[i];
if valid <> nil then
valid[k] := src.IsValid[i];
end;
@ -2983,16 +2930,12 @@ begin
var src := BoolColumn(col);
var data := new boolean[newCount];
var valid: array of boolean := nil;
if src.IsValid <> nil then
valid := new boolean[newCount];
var valid := new boolean[newCount];
for var k := 0 to newCount - 1 do
begin
var i := mask[k];
data[k] := src.Data[i];
if valid <> nil then
valid[k] := src.IsValid[i];
end;
@ -3251,16 +3194,6 @@ begin
end;
end;
static procedure DataFrame.EnsureValidArray(var valid: array of boolean; rowCount, filledCount: integer);
begin
if valid = nil then
begin
valid := new boolean[rowCount];
for var j := 0 to filledCount - 1 do
valid[j] := true;
end;
end;
function DataFrame.WithColumnInt(name: string; f: DataFrameCursor -> integer): DataFrame;
begin
if fSchema.HasColumn(name) then
@ -3269,7 +3202,7 @@ begin
var res := CloneWithCopiedColumns;
var data := new integer[RowCount];
var valid: array of boolean := nil;
var valid := new boolean[RowCount];
var cur := GetCursor;
var i := 0;
@ -3282,15 +3215,13 @@ begin
on e: Exception do
begin
data[i] := 0;
EnsureValidArray(valid, RowCount, i);
valid[i] := false;
valid[i] := False;
i += 1;
continue;
end;
end;
if valid <> nil then
valid[i] := true;
valid[i] := True;
i += 1;
end;
@ -3310,7 +3241,7 @@ begin
var res := CloneWithCopiedColumns;
var data := new real[RowCount];
var valid: array of boolean := nil;
var valid := new boolean[RowCount];
var cur := GetCursor;
var i := 0;
@ -3323,15 +3254,13 @@ begin
on e: Exception do
begin
data[i] := 0.0;
EnsureValidArray(valid, RowCount, i);
valid[i] := false;
valid[i] := False;
i += 1;
continue;
end;
end;
if valid <> nil then
valid[i] := true;
valid[i] := True;
i += 1;
end;
@ -3351,7 +3280,7 @@ begin
var res := CloneWithCopiedColumns;
var data := new string[RowCount];
var valid: array of boolean := nil;
var valid := new boolean[RowCount];
var cur := GetCursor;
var i := 0;
@ -3364,15 +3293,13 @@ begin
on e: Exception do
begin
data[i] := nil;
EnsureValidArray(valid, RowCount, i);
valid[i] := false;
valid[i] := False;
i += 1;
continue;
end;
end;
if valid <> nil then
valid[i] := true;
valid[i] := True;
i += 1;
end;
@ -3392,7 +3319,7 @@ begin
var res := CloneWithCopiedColumns;
var data := new boolean[RowCount];
var valid: array of boolean := nil;
var valid := new boolean[RowCount];
var cur := GetCursor;
var i := 0;
@ -3404,16 +3331,14 @@ begin
except
on e: Exception do
begin
data[i] := false;
EnsureValidArray(valid, RowCount, i);
valid[i] := false;
data[i] := False;
valid[i] := False;
i += 1;
continue;
end;
end;
if valid <> nil then
valid[i] := true;
valid[i] := True;
i += 1;
end;
@ -3458,7 +3383,7 @@ begin
var rowCount := RowCount;
var data := new real[rowCount];
var valid: array of boolean := nil;
var valid := new boolean[rowCount];
var cur := GetCursor;
var row := 0;
@ -3469,14 +3394,6 @@ begin
if not cur.IsValid(colIndex) then
begin
data[row] := 0.0;
if valid = nil then
begin
valid := new boolean[rowCount];
for var j := 0 to row - 1 do
valid[j] := true;
end;
valid[row] := false;
row += 1;
continue;
@ -3489,22 +3406,13 @@ begin
on e: Exception do
begin
data[row] := 0.0;
if valid = nil then
begin
valid := new boolean[rowCount];
for var j := 0 to row - 1 do
valid[j] := true;
end;
valid[row] := false;
valid[row] := False;
row += 1;
continue;
end;
end;
if valid <> nil then
valid[row] := true;
valid[row] := True;
row += 1;
end;
@ -3540,7 +3448,7 @@ begin
var rowCount := RowCount;
var data := new integer[rowCount];
var valid: array of boolean := nil;
var valid := new boolean[rowCount];
var cur := GetCursor;
var row := 0;
@ -3548,17 +3456,12 @@ begin
begin
try
data[row] := f(cur);
if valid <> nil then valid[row] := true;
valid[row] := True;
except
on e: Exception do
begin
data[row] := 0;
if valid = nil then
begin
valid := new boolean[rowCount];
for var j := 0 to row - 1 do valid[j] := true;
end;
valid[row] := false;
valid[row] := False;
end;
end;
row += 1;
@ -3597,7 +3500,7 @@ begin
var rowCount := RowCount;
var data := new integer[rowCount];
var valid: array of boolean := nil;
var valid := new boolean[rowCount];
var cur := GetCursor;
var row := 0;
@ -3605,17 +3508,12 @@ begin
begin
try
data[row] := f(cur);
if valid <> nil then valid[row] := true;
valid[row] := True;
except
on e: Exception do
begin
data[row] := 0;
if valid = nil then
begin
valid := new boolean[rowCount];
for var j := 0 to row - 1 do valid[j] := true;
end;
valid[row] := false;
valid[row] := False;
end;
end;
row += 1;
@ -3863,11 +3761,7 @@ begin
begin
var src := IntColumn(col);
var data := new integer[k];
var validSrc := src.IsValid;
var validDst: array of boolean := nil;
if validSrc <> nil then
validDst := new boolean[k];
var validDst := new boolean[k];
for var j := 0 to k - 1 do
begin
@ -3876,15 +3770,8 @@ begin
if (i < 0) or (i >= RowCount) then
ArgumentError(ER_ROW_INDEX_OUT_OF_RANGE, i);
if validSrc = nil then
data[j] := src.Data[i]
else if validSrc[i] then
begin
data[j] := src.Data[i];
validDst[j] := true;
end
else
validDst[j] := false;
validDst[j] := src.IsValid[i];
end;
res.AddIntColumn(name, data, validDst);
@ -3894,11 +3781,7 @@ begin
begin
var src := FloatColumn(col);
var data := new real[k];
var validSrc := src.IsValid;
var validDst: array of boolean := nil;
if validSrc <> nil then
validDst := new boolean[k];
var validDst := new boolean[k];
for var j := 0 to k - 1 do
begin
@ -3907,15 +3790,8 @@ begin
if (i < 0) or (i >= RowCount) then
ArgumentError(ER_ROW_INDEX_OUT_OF_RANGE, i);
if validSrc = nil then
data[j] := src.Data[i]
else if validSrc[i] then
begin
data[j] := src.Data[i];
validDst[j] := true;
end
else
validDst[j] := false;
validDst[j] := src.IsValid[i];
end;
res.AddFloatColumn(name, data, validDst);
@ -3925,11 +3801,7 @@ begin
begin
var src := StrColumn(col);
var data := new string[k];
var validSrc := src.IsValid;
var validDst: array of boolean := nil;
if validSrc <> nil then
validDst := new boolean[k];
var validDst := new boolean[k];
for var j := 0 to k - 1 do
begin
@ -3938,15 +3810,8 @@ begin
if (i < 0) or (i >= RowCount) then
ArgumentError(ER_ROW_INDEX_OUT_OF_RANGE, i);
if validSrc = nil then
data[j] := src.Data[i]
else if validSrc[i] then
begin
data[j] := src.Data[i];
validDst[j] := true;
end
else
validDst[j] := false;
validDst[j] := src.IsValid[i];
end;
res.AddStrColumn(name, data, validDst);
@ -3956,11 +3821,7 @@ begin
begin
var src := BoolColumn(col);
var data := new boolean[k];
var validSrc := src.IsValid;
var validDst: array of boolean := nil;
if validSrc <> nil then
validDst := new boolean[k];
var validDst := new boolean[k];
for var j := 0 to k - 1 do
begin
@ -3969,15 +3830,8 @@ begin
if (i < 0) or (i >= RowCount) then
ArgumentError(ER_ROW_INDEX_OUT_OF_RANGE, i);
if validSrc = nil then
data[j] := src.Data[i]
else if validSrc[i] then
begin
data[j] := src.Data[i];
validDst[j] := true;
end
else
validDst[j] := false;
validDst[j] := src.IsValid[i];
end;
res.AddBoolColumn(name, data, validDst);
@ -4305,13 +4159,10 @@ begin
case src.Info.ColType of
ctInt:
AddIntColumn(src.Info.Name, IntColumn(src).Data, IntColumn(src).IsValid);
ctFloat:
AddFloatColumn(src.Info.Name, FloatColumn(src).Data, FloatColumn(src).IsValid);
ctStr:
AddStrColumn(src.Info.Name, StrColumn(src).Data, StrColumn(src).IsValid);
ctBool:
AddBoolColumn(src.Info.Name, BoolColumn(src).Data, BoolColumn(src).IsValid);
end;
@ -4559,7 +4410,7 @@ begin
begin
var i := indices[j];
if (valid <> nil) and not valid[i] then
if not valid[i] then
continue;
s += if isInt then dataInt[i] else dataFloat[i];
@ -4586,7 +4437,7 @@ begin
begin
var i := indices[j];
if (valid <> nil) and not valid[i] then
if not valid[i] then
continue;
s += if isInt then dataInt[i] else dataFloat[i];
@ -4614,7 +4465,7 @@ begin
begin
var i := indices[j];
if (valid <> nil) and not valid[i] then
if not valid[i] then
continue;
var v := if isInt then dataInt[i] else dataFloat[i];
@ -4647,7 +4498,7 @@ begin
begin
var i := indices[j];
if (valid <> nil) and not valid[i] then
if not valid[i] then
continue;
var v := if isInt then dataInt[i] else dataFloat[i];
@ -5578,7 +5429,7 @@ begin
if cur.IsValid(i) then
col.Data[row] := (cur.Float(i) - means[i]) / stds[i]
else
col.IsValid[row] := false;
col.IsValid[row] := False;
end;
row += 1;
@ -5661,20 +5512,11 @@ begin
if cur.IsValid(i) then
begin
col.Data[row] := (cur.Float(i) - mins[i]) / (maxs[i] - mins[i]);
if col.IsValid <> nil then
col.IsValid[row] := true;
col.IsValid[row] := True;
end
else
begin
// первый NA → создаём IsValid
if col.IsValid = nil then
begin
col.IsValid := new boolean[df.RowCount];
for var r := 0 to row - 1 do
col.IsValid[r] := true;
end;
col.IsValid[row] := false;
end;
col.IsValid[row] := False;
end;
row += 1;
end;

View file

@ -1,5 +1,20 @@
unit DataFrameABCCore;
// =============================================================
// ИНВАРИАНТ ВАЛИДНОСТИ СТОЛБЦОВ
//
// Для всех столбцов:
// • IsValid всегда инициализирован
// • Length(IsValid) = Length(Data)
// • nil не используется как специальное значение
//
// Пустой столбец:
// • Data имеет длину 0
// • IsValid = new boolean[0]
//
// Нарушение этого инварианта считается ошибкой.
// =============================================================
interface
type
@ -276,6 +291,8 @@ const
ER_ROW_INDEX_OUT_OF_RANGE =
'Индекс строки {0} вне диапазона [0..{1})!!' +
'Row index {0} out of range [0..{1})';
ER_INVALID_ISVALID_LENGTH =
'Длина IsValid должна совпадать с длиной Data!!IsValid length must match Data length';
//-----------------------------
// Сервисные функции
@ -547,14 +564,23 @@ end;
// Columns
//-----------------------------
constructor IntColumn.Create(name: string; data: array of integer;
valid: array of boolean);
constructor IntColumn.Create(name: string; data: array of integer; valid: array of boolean);
begin
inherited Create;
Info := new ColumnInfo(name, ctInt);
Data := data;
var n := Length(data);
Data := if n = 0 then [] else data;
if valid = nil then
IsValid := [True] * n
else
begin
if Length(valid) <> n then
Error(ER_INVALID_ISVALID_LENGTH);
IsValid := valid;
end;
end;
constructor IntColumn.Create(name: string);
@ -562,55 +588,73 @@ begin
inherited Create;
Info := new ColumnInfo(name, ctInt);
Data := new integer[0];
IsValid := nil;
Data := [];
IsValid := [];
end;
function IntColumn.TryGetNumericValue(i: integer; var value: real): boolean;
begin
if (IsValid <> nil) and not IsValid[i] then
exit(false);
if not IsValid[i] then
exit(False);
value := Data[i];
exit(true);
exit(True);
end;
constructor FloatColumn.Create(name: string; data: array of real;
valid: array of boolean);
constructor FloatColumn.Create(name: string; data: array of real; valid: array of boolean);
begin
inherited Create;
Info := new ColumnInfo(name, ctFloat);
Data := data;
var n := Length(data);
Data := if n = 0 then [] else data;
if valid = nil then
IsValid := [True] * n
else
begin
if Length(valid) <> n then
Error(ER_INVALID_ISVALID_LENGTH);
IsValid := valid;
end;
end;
constructor FloatColumn.Create(name: string);
begin
inherited Create;
Info := new ColumnInfo(name, ctFloat);
Data := new real[0];
IsValid := nil;
end;
Data := [];
IsValid := [];
end;
function FloatColumn.TryGetNumericValue(i: integer; var value: real): boolean;
begin
if (IsValid <> nil) and not IsValid[i] then
exit(false);
if not IsValid[i] then
exit(False);
value := Data[i];
exit(true);
exit(True);
end;
constructor StrColumn.Create(name: string; data: array of string;
valid: array of boolean);
constructor StrColumn.Create(name: string; data: array of string; valid: array of boolean);
begin
inherited Create;
Info := new ColumnInfo(name, ctStr);
Data := data;
var n := Length(data);
Data := if n = 0 then [] else data;
if valid = nil then
IsValid := [True] * n
else
begin
if Length(valid) <> n then
Error(ER_INVALID_ISVALID_LENGTH);
IsValid := valid;
end;
end;
constructor StrColumn.Create(name: string);
@ -618,36 +662,32 @@ begin
inherited Create;
Info := new ColumnInfo(name, ctStr);
Data := new string[0];
IsValid := nil;
Data := [];
IsValid := [];
end;
function StrColumn.TryGetNumericValue(i: integer; var value: real): boolean;
begin
exit(false);
exit(False);
end;
function BoolColumn.TryGetNumericValue(i: integer; var value: real): boolean;
begin
if (IsValid <> nil) and not IsValid[i] then
exit(false);
if Data[i] then
value := 1.0
else
value := 0.0;
exit(true);
end;
constructor BoolColumn.Create(name: string; data: array of boolean;
valid: array of boolean);
constructor BoolColumn.Create(name: string; data: array of boolean; valid: array of boolean);
begin
inherited Create;
Info := new ColumnInfo(name, ctBool);
Data := data;
var n := Length(data);
Data := if n = 0 then [] else data;
if valid = nil then
IsValid := [True] * n
else
begin
if Length(valid) <> n then
Error(ER_INVALID_ISVALID_LENGTH);
IsValid := valid;
end;
end;
constructor BoolColumn.Create(name: string);
@ -655,34 +695,24 @@ begin
inherited Create;
Info := new ColumnInfo(name, ctBool);
Data := new boolean[0];
IsValid := nil;
Data := [];
IsValid := [];
end;
{procedure BoolColumn.AppendFromCursor(cur: DataFrameCursor; colIndex: integer);
function BoolColumn.TryGetNumericValue(i: integer; var value: real): boolean;
begin
if cur.IsValid(colIndex) then
begin
Data := Data + [cur.Bool(colIndex)];
if IsValid <> nil then
IsValid := IsValid + [true];
end
else AppendInvalid;
end;}
if not IsValid[i] then
exit(False);
{procedure BoolColumn.AppendInvalid;
begin
Data := Data + [false];
if Data[i] then
value := 1.0
else
value := 0.0;
exit(True);
end;
if IsValid = nil then
begin
IsValid := new boolean[Length(Data) - 1];
for var i := 0 to IsValid.Length - 1 do
IsValid[i] := true;
end;
IsValid := IsValid + [false];
end;}
//-----------------------------
// JoinKey
@ -772,9 +802,6 @@ begin
ctInt:
begin
var c := IntColumn(col);
if c.IsValid = nil then
validAcc[i] := pos -> true
else
validAcc[i] := pos -> c.IsValid[pos];
intAcc[i] := pos -> c.Data[pos];
@ -784,9 +811,6 @@ begin
ctFloat:
begin
var c := FloatColumn(col);
if c.IsValid = nil then
validAcc[i] := pos -> true
else
validAcc[i] := pos -> c.IsValid[pos];
floatAcc[i] := pos -> c.Data[pos];
@ -796,9 +820,6 @@ begin
ctStr:
begin
var c := StrColumn(col);
if c.IsValid = nil then
validAcc[i] := pos -> true
else
validAcc[i] := pos -> c.IsValid[pos];
strAcc[i] := pos -> c.Data[pos];
@ -807,9 +828,6 @@ begin
ctBool:
begin
var c := BoolColumn(col);
if c.IsValid = nil then
validAcc[i] := pos -> true
else
validAcc[i] := pos -> c.IsValid[pos];
boolAcc[i] := pos -> c.Data[pos];

View file

@ -1,4 +1,13 @@
/// Линейная алгебра для алгоритмов машинного обучения.
// =============================================================
// СТАТИСТИЧЕСКОЕ СОГЛАШЕНИЕ (LinearAlgebraML)
//
// Используется дисперсия генеральной совокупности (деление на n),
// как принято в численных методах и алгоритмах машинного обучения.
//
// См. статистическую политику в модуле MLABC.
// =============================================================
/// Линейная алгебра для алгоритмов машинного обучения.
///
/// Содержит типы Vector и Matrix и численные методы,
/// используемые в моделях ML:

View file

@ -1,4 +1,23 @@
/// Основной модуль библиотеки машинного обучения.
// =============================================================
// СТАТИСТИЧЕСКАЯ ПОЛИТИКА БИБЛИОТЕКИ ML PascalABC.NET
//
// В библиотеке используются следующие соглашения:
//
// 1. DataFrame (описательная статистика):
// • дисперсия вычисляется с делением на (n - 1)
//
// 2. LinearAlgebra и ML:
// • дисперсия вычисляется с делением на n
//
// 3. PCA:
// • ковариационная матрица вычисляется с делением на (n - 1)
//
// Это соответствует распространённой практике:
// • описательная статистика — выборочная дисперсия
// • алгоритмы ML — дисперсия генеральной совокупности
// =============================================================
/// Основной модуль библиотеки машинного обучения.
/// Объединяет модели, метрики, валидацию и вспомогательные компоненты.
unit MLABC;

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,24 @@ interface
uses LinearAlgebraML;
/// Определяет фактический seed для генератора случайных чисел.
///
/// Если seed >= 0:
/// • используется заданное пользователем значение
/// • userProvided := true
///
/// Если seed < 0:
/// • генерируется псевдослучайный seed на основе системного времени
/// • userProvided := false
///
/// Возвращает:
/// • неотрицательный seed, используемый для инициализации RNG
///
/// Назначение:
/// • обеспечить единое поведение случайности во всех моделях
/// • сохранить воспроизводимость при явном задании seed
function ResolveRandomSeed(seed: integer; var userProvided: boolean): integer;
/// Преобразует вектор меток классов в массив целых чисел.
/// Используется при визуализации и других задачах,
/// где метки должны быть представлены как 0,1,2,...
@ -70,6 +88,20 @@ const
'Индекс метки {0} вне диапазона [0, {1})!!Label index {0} is out of range [0, {1})';
function ResolveRandomSeed(seed: integer; var userProvided: boolean): integer;
begin
if seed < 0 then
begin
userProvided := false;
Result := System.Environment.TickCount and integer.MaxValue;
end
else
begin
userProvided := true;
Result := seed;
end;
end;
function LabelsToInts(y: Vector): array of integer;
begin
if y = nil then

View file

@ -53,11 +53,18 @@ type
/// Создаёт LabelEncoder для указанного столбца
constructor Create(column: string);
/// Определяет множество категорий и сохраняет их коды
/// Определяет множество категорий столбца и сохраняет их числовое кодирование.
/// df — таблица данных.
/// Сохраняет отображение категорий в числовые коды для последующего применения.
///
/// Примечание:
/// • отображение категорий НЕ копируется методом Clone
function Fit(df: DataFrame): IPreprocessor;
/// Заменяет категории их числовыми кодами
/// Возвращает новый DataFrame
function Transform(df: DataFrame): DataFrame;
/// Выполняет Fit и Transform последовательно
function FitTransform(df: DataFrame): DataFrame;
@ -65,6 +72,15 @@ type
property ColumnName: string read col;
/// Создаёт копию препроцессора с той же конфигурацией.
///
/// ВАЖНО:
/// • Clone копирует только конфигурацию (например, имя столбца)
/// • Clone НЕ копирует обученное состояние (mapping, статистики и т.п.)
///
/// Назначение:
/// • использование в Pipeline и CrossValidate
/// • обеспечивает независимое переобучение препроцессоров
function Clone: IPreprocessor;
end;
@ -82,8 +98,14 @@ type
/// Создаёт OneHotEncoder для указанного столбца
constructor Create(column: string);
/// Определяет множество категорий столбца
/// Определяет множество категорий столбца.
/// df — таблица данных.
/// Сохраняет категории для последующего преобразования в one-hot представление.
///
/// Примечание:
/// • категории НЕ копируются методом Clone
function Fit(df: DataFrame): IPreprocessor;
/// Заменяет столбец набором бинарных столбцов
/// Возвращает новый DataFrame
function Transform(df: DataFrame): DataFrame;
@ -94,6 +116,15 @@ type
property ColumnName: string read col;
/// Создаёт копию препроцессора с той же конфигурацией.
///
/// ВАЖНО:
/// • Clone копирует только конфигурацию (например, имя столбца)
/// • Clone НЕ копирует обученное состояние (mapping, статистики и т.п.)
///
/// Назначение:
/// • использование в Pipeline и CrossValidate
/// • обеспечивает независимое переобучение препроцессоров
function Clone: IPreprocessor;
end;
@ -118,11 +149,19 @@ type
/// Создаёт Imputer с константной стратегией заполнения
constructor Create(value: object; params columns: array of string);
/// Вычисляет значения для заполнения пропусков
/// Вычисляет значения для заполнения пропусков.
/// df — таблица данных.
/// В зависимости от стратегии вычисляет параметры заполнения
/// (например, среднее, медиану или константу) для каждого столбца.
///
/// Примечание:
/// • вычисленные параметры НЕ копируются методом Clone
function Fit(df: DataFrame): IPreprocessor;
/// Заполняет пропущенные значения в DataFrame
/// Возвращает новый DataFrame
function Transform(df: DataFrame): DataFrame;
/// Выполняет Fit и Transform последовательно
function FitTransform(df: DataFrame): DataFrame;
@ -130,6 +169,15 @@ type
property Columns: array of string read cols;
/// Создаёт копию препроцессора с той же конфигурацией.
///
/// ВАЖНО:
/// • Clone копирует только конфигурацию (например, имя столбца)
/// • Clone НЕ копирует обученное состояние (mapping, статистики и т.п.)
///
/// Назначение:
/// • использование в Pipeline и CrossValidate
/// • обеспечивает независимое переобучение препроцессоров
function Clone: IPreprocessor;
end;
@ -262,7 +310,7 @@ begin
var n := df.RowCount;
var data := new integer[n];
var valid: array of boolean := nil;
var valid := new boolean[n];
var cur := df.GetCursor;
var row := 0;
@ -270,13 +318,7 @@ begin
begin
if not cur.IsValid(idx) then
begin
if valid = nil then
begin
valid := new boolean[n];
for var j := 0 to row - 1 do
valid[j] := true;
end;
valid[row] := false;
valid[row] := False;
data[row] := 0;
end
else
@ -287,8 +329,7 @@ begin
Error(ER_LABELENCODER_UNSEEN_CATEGORY, s);
data[row] := mapping[s];
if valid <> nil then
valid[row] := true;
valid[row] := True;
end;
row += 1;