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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,24 @@ interface
uses LinearAlgebraML; 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,... /// где метки должны быть представлены как 0,1,2,...
@ -70,6 +88,20 @@ const
'Индекс метки {0} вне диапазона [0, {1})!!Label index {0} is out of range [0, {1})'; 'Индекс метки {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; function LabelsToInts(y: Vector): array of integer;
begin begin
if y = nil then if y = nil then

View file

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