Поправленные исключения в модулях ML (не до конца)

This commit is contained in:
Mikhalkovich Stanislav 2026-02-18 10:38:42 +03:00
parent d09da0213e
commit 3d8ebff9ba
8 changed files with 335 additions and 173 deletions

View file

@ -347,6 +347,89 @@ type
implementation
uses MLExceptions;
const
ER_COLS_NULL =
'cols не может быть nil!!cols cannot be nil';
ER_SCHEMA_NULL =
'schema не может быть nil!!schema cannot be nil';
ER_COLS_SCHEMA_MISMATCH =
'Количество столбцов не совпадает со схемой!!Columns count and schema mismatch';
ER_JOIN_KEY_TYPE_MISMATCH =
'Типы ключей соединения не совпадают!!Join key types mismatch';
ER_JOIN_KEY_NOT_FOUND =
'Ключ соединения "{0}" не найден!!Join key "{0}" not found';
ER_JOIN_KIND_NOT_IMPLEMENTED =
'Тип соединения не реализован!!Join kind not implemented';
ER_JOIN_KEYS_LENGTH_MISMATCH =
'leftKeys и rightKeys должны иметь одинаковую длину!!leftKeys and rightKeys must have the same length';
ER_COLUMN_NOT_FOUND =
'Столбец "{0}" не найден!!Column not found: {0}';
ER_ADD_COLUMN_ROW_MISMATCH =
'Несоответствие числа строк при добавлении столбца!!Row count mismatch when adding column';
ER_COLUMN_VALID_LENGTH_MISMATCH =
'Длины data и valid не совпадают при добавлении столбца!!Data and valid length mismatch when adding column';
ER_COLUMN_INDEX_OUT_OF_RANGE =
'Индекс столбца {0} вне диапазона [0..{1})!!Column index {0} out of range [0..{1})';
ER_NO_VALID_VALUES_COLUMN =
'Нет допустимых значений в столбце {0}!!No valid values in column {0}';
ER_SORTBY_LENGTH_MISMATCH =
'SortBy: длины colIndices и descending не совпадают!!SortBy: colIndices and descending length mismatch';
ER_UNKNOWN_COLUMN_TYPE =
'Неизвестный тип столбца!!Unknown column type';
ER_COLUMN_ALREADY_EXISTS =
'Столбец "{0}" уже существует!!Column "{0}" already exists';
ER_SCHEMA_INCONSISTENT =
'Несогласованная схема: столбец "{0}" имеет RowCount={1}, ожидалось {2}!!' +
'Schema inconsistent: column "{0}" has RowCount={1}, expected {2}';
ER_SCHEMA_COLUMNCOUNT_MISMATCH =
'Несогласованная схема: ColumnCount={0}, columns.Count={1}!!' +
'Schema inconsistent: ColumnCount={0}, columns.Count={1}';
ER_SCHEMA_COLUMN_MISSING =
'Несогласованная схема: столбец "{0}" отсутствует в схеме!!' +
'Schema inconsistent: column "{0}" missing in schema';
ER_SCHEMA_COLUMN_INDEX_INCONSISTENT =
'Несогласованная схема: GetColumnIndex("{0}")={1}, ожидалось {2}!!' +
'Schema inconsistent: GetColumnIndex("{0}")={1}, expected {2}';
ER_COLUMN_NOT_NUMERIC =
'Столбец не является числовым!!Column is not numeric';
ER_GROUPBY_UNSUPPORTED_KEY_TYPE =
'Неподдерживаемый тип ключа GroupBy: {0}!!Unsupported GroupBy key type: {0}';
ER_ZERO_VARIANCE =
'Нулевая дисперсия при вычислении корреляции!!Zero variance in correlation';
ER_NO_VALID_PAIRS =
'Нет допустимых пар для вычисления корреляции!!No valid pairs for correlation';
ER_NO_NUMERIC_COLUMNS =
'Нет числовых столбцов для матрицы корреляции!!No numeric columns for correlation matrix';
ER_ZERO_STD_STANDARDIZE =
'Нулевое стандартное отклонение при вызове Standardize!!Zero standard deviation in Standardize';
ER_INVALID_VALUE_IN_COLUMN =
'Недопустимое значение в столбце "{0}"!!' +
'Invalid value in column "{0}"';
ER_ZERO_STD_COLUMN =
'Нулевое стандартное отклонение в столбце "{0}" при StandardizeAll!!' +
'Zero standard deviation in column "{0}" in StandardizeAll';
ER_ZERO_RANGE =
'Нулевой диапазон при нормализации!!Zero range in normalization';
ER_ZERO_RANGE_COLUMN =
'Нулевой диапазон в столбце "{0}"!!Zero range in column "{0}"';
ER_QUANTILE_P_INVALID =
'Параметр p должен быть в диапазоне [0,1]!!Quantile p must be in [0,1]';
ER_NO_VALID_VALUES_QUANTILE =
'Нет допустимых значений для вычисления квантиля!!No valid values for quantile';
ER_CSV_COLUMN_COUNT_MISMATCH =
'Ошибка формата CSV: ожидалось {0} столбцов, получено {1}!!' +
'CSV format error: expected {0} columns, got {1}';
ER_EMPTY_CSV =
'CSV-файл пуст!!Empty CSV';
ER_CSV_UNCLOSED_QUOTE =
'Ошибка формата CSV: незакрытая кавычка!!CSV format error: unclosed quote';
ER_CSV_INVALID_BOOL =
'Некорректное логическое значение "{0}" в столбце "{1}"!!' +
'Invalid bool "{0}" in column "{1}"';
type
/// Класс для группировки данных
GroupByContext = class(IGroupByContext)
@ -392,11 +475,11 @@ end;
constructor DataFrame.Create(cols: List<Column>; schema: DataFrameSchema);
begin
if cols = nil then
raise new System.ArgumentException('cols is nil');
ArgumentNullError(ER_COLS_NULL);
if schema = nil then
raise new System.ArgumentException('schema is nil');
ArgumentNullError(ER_SCHEMA_NULL);
if cols.Count <> schema.ColumnCount then
raise new System.ArgumentException('Columns count and schema mismatch');
ArgumentError(ER_COLS_SCHEMA_MISMATCH);
self.columns := cols;
self.fSchema := schema;
@ -573,7 +656,7 @@ begin
var rt := other.fSchema.ColumnTypeAt(rightKey);
if lt <> rt then
raise new Exception('Join key types mismatch');
Error(ER_JOIN_KEY_TYPE_MISMATCH);
// 3. типоспецифичный алгоритм (КАК РАНЬШЕ)
case lt of
@ -804,9 +887,8 @@ begin
// 2. проверка типов ключей — через Schema
for var i := 0 to n - 1 do
if fSchema.ColumnTypeAt(leftKeyIdx[i]) <>
other.fSchema.ColumnTypeAt(rightKeyIdx[i]) then
raise new Exception('Join key types mismatch');
if fSchema.ColumnTypeAt(leftKeyIdx[i]) <> other.fSchema.ColumnTypeAt(rightKeyIdx[i]) then
Error(ER_JOIN_KEY_TYPE_MISMATCH);
// 3. layout'ы (как раньше)
var leftLayout := BuildJoinKeyLayout(leftKeyIdx);
@ -881,7 +963,7 @@ begin
var ri := other.fSchema.IndexOf(key);
if (li < 0) or (ri < 0) then
raise new Exception($'Join key "{key}" not found');
Error(ER_JOIN_KEY_NOT_FOUND, key);
var leftKeyIdx := [li];
var rightKeyIdx := [ri];
@ -1032,28 +1114,6 @@ begin
Result := res;
end;
{function DataFrame.JoinInnerSingleKey(other: DataFrame; leftKey, rightKey: integer;
resultSchema: DataFrameSchema): DataFrame;
begin
// типы ключей — ТОЛЬКО из Schema
var lt := fSchema.ColumnTypeAt(leftKey);
var rt := other.fSchema.ColumnTypeAt(rightKey);
if lt <> rt then
raise new Exception('Join key types mismatch');
case lt of
ctInt:
Result := JoinInnerSingleKeyInt(other, leftKey, rightKey, resultSchema);
ctFloat:
Result := JoinInnerSingleKeyFloat(other, leftKey, rightKey, resultSchema);
ctStr:
Result := JoinInnerSingleKeyStr(other, leftKey, rightKey, resultSchema);
ctBool:
Result := JoinInnerSingleKeyBool(other, leftKey, rightKey, resultSchema);
end;
end;}
function DataFrame.JoinInnerSingleKey(other: DataFrame; key: string): DataFrame;
begin
var leftKey := fSchema.IndexOf(key);
@ -1063,7 +1123,7 @@ begin
var rt := other.fSchema.ColumnTypeAt(rightKey);
if lt <> rt then
raise new Exception('Join key types mismatch');
Error(ER_JOIN_KEY_TYPE_MISMATCH);
case lt of
ctInt: Result := JoinInnerSingleKeyInt(other, leftKey, rightKey);
@ -1264,9 +1324,8 @@ begin
// 2. проверка типов ключей — через Schema
for var i := 0 to n - 1 do
if fSchema.ColumnTypeAt(leftKeyIdx[i]) <>
other.fSchema.ColumnTypeAt(rightKeyIdx[i]) then
raise new Exception('Join key types mismatch');
if fSchema.ColumnTypeAt(leftKeyIdx[i]) <> other.fSchema.ColumnTypeAt(rightKeyIdx[i]) then
Error(ER_JOIN_KEY_TYPE_MISMATCH);
// 3. строим layout'ы
var leftLayout := BuildJoinKeyLayout(leftKeyIdx);
@ -1344,7 +1403,7 @@ begin
else
exit(FullJoinMultiKey(other, keys));
raise new Exception('Join kind not implemented');
Error(ER_JOIN_KIND_NOT_IMPLEMENTED);
end;
function DataFrame.Join(other: DataFrame; key: string; kind: JoinKind): DataFrame;
@ -1355,7 +1414,7 @@ end;
function DataFrame.Join(other: DataFrame; leftKeys, rightKeys: array of string; kind: JoinKind): DataFrame;
begin
if leftKeys.Length <> rightKeys.Length then
raise new Exception('leftKeys and rightKeys must have the same length');
ArgumentError(ER_JOIN_KEYS_LENGTH_MISMATCH);
// временно переименовываем столбцы справа
var tmp := other;
@ -1416,7 +1475,7 @@ begin
for var i := 0 to columns.Count - 1 do
if columns[i].Info.Name = name then
exit(i);
raise new Exception('Column not found: ' + name);
Error(ER_COLUMN_NOT_FOUND, name);
end;
function DataFrame.GetCursor: DataFrameCursor :=
@ -1439,7 +1498,7 @@ end;
procedure DataFrame.AddIntColumn(name: string; data: array of integer; valid: array of boolean; isCategorical: boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
raise new Exception('Row count mismatch');
DimensionError(ER_ADD_COLUMN_ROW_MISMATCH);
var c := new IntColumn;
c.Info := new ColumnInfo(name, ctInt, isCategorical);
@ -1450,7 +1509,7 @@ begin
else
begin
if valid.Length <> data.Length then
raise new Exception('AddIntColumn: data and valid length mismatch');
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);;
c.IsValid := valid;
end;
@ -1461,7 +1520,7 @@ end;
procedure DataFrame.AddFloatColumn(name: string; data: array of real; valid: array of boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
raise new Exception('Row count mismatch');
DimensionError(ER_ADD_COLUMN_ROW_MISMATCH);
var c := new FloatColumn;
c.Info := new ColumnInfo(name, ctFloat, False);
@ -1472,7 +1531,7 @@ begin
else
begin
if valid.Length <> data.Length then
raise new Exception('AddFloatColumn: data and valid length mismatch');
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
c.IsValid := valid;
end;
@ -1483,7 +1542,7 @@ end;
procedure DataFrame.AddStrColumn(name: string; data: array of string; valid: array of boolean; isCategorical: boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
raise new Exception('Row count mismatch');
DimensionError(ER_ADD_COLUMN_ROW_MISMATCH);
var c := new StrColumn;
c.Info := new ColumnInfo(name, ctStr, isCategorical);
@ -1494,7 +1553,7 @@ begin
else
begin
if valid.Length <> data.Length then
raise new Exception('AddStrColumn: data and valid length mismatch');
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
c.IsValid := valid;
end;
@ -1505,7 +1564,7 @@ end;
procedure DataFrame.AddBoolColumn(name: string; data: array of boolean; valid: array of boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
raise new Exception('Row count mismatch');
DimensionError(ER_ADD_COLUMN_ROW_MISMATCH);
var c := new BoolColumn;
c.Info := new ColumnInfo(name, ctBool, False);
@ -1516,7 +1575,7 @@ begin
else
begin
if valid.Length <> data.Length then
raise new Exception('AddBoolColumn: data and valid length mismatch');
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
c.IsValid := valid;
end;
@ -1528,7 +1587,7 @@ end;
procedure DataFrame.CheckColumnIndex(colIndex: integer);
begin
if (colIndex < 0) or (colIndex >= ColumnCount) then
raise new Exception('Column index out of range');
ArgumentOutOfRangeError(ER_COLUMN_INDEX_OUT_OF_RANGE, colIndex, ColumnCount);
end;
function DataFrame.Sum(colIndex: integer): real;
@ -1620,7 +1679,7 @@ begin
end;
if not has then
raise new Exception('Min: no valid values');
Error(ER_NO_VALID_VALUES_COLUMN, colIndex);
Result := m;
end;
@ -1650,7 +1709,7 @@ begin
end;
if not has then
raise new Exception('Max: no valid values');
Error(ER_NO_VALID_VALUES_COLUMN, colIndex);
Result := m;
end;
@ -1684,7 +1743,7 @@ begin
end;
if not has then
raise new Exception('MinMax: no valid values');
Error(ER_NO_VALID_VALUES_COLUMN, colIndex);
Result := (mn, mx);
end;
@ -1974,7 +2033,7 @@ type
function DataFrame.SortBy(colIndices: array of integer; descending: array of boolean): DataFrame;
begin
if colIndices.Length <> descending.Length then
raise new Exception('SortBy: length mismatch');
ArgumentError(ER_SORTBY_LENGTH_MISMATCH);
foreach var c in colIndices do
CheckColumnIndex(c);
@ -2172,7 +2231,7 @@ begin
res.AddBoolColumn(src.Info.Name, data, valid);
end
else
raise new Exception('Unknown column type');
Error(ER_UNKNOWN_COLUMN_TYPE);
end;
Result := res;
@ -2422,7 +2481,7 @@ end;
function DataFrame.WithColumnInt(name: string; f: DataFrameCursor -> integer): DataFrame;
begin
if fSchema.HasColumn(name) then
raise new Exception($'Column "{name}" already exists');
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := new DataFrame;
@ -2499,7 +2558,7 @@ end;
function DataFrame.WithColumnFloat(name: string; f: DataFrameCursor -> real): DataFrame;
begin
if fSchema.HasColumn(name) then
raise new Exception($'Column "{name}" already exists');
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := new DataFrame;
@ -2550,7 +2609,7 @@ end;
function DataFrame.WithColumnStr(name: string; f: DataFrameCursor -> string): DataFrame;
begin
if fSchema.HasColumn(name) then
raise new Exception($'Column "{name}" already exists');
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := new DataFrame;
@ -2601,7 +2660,7 @@ end;
function DataFrame.WithColumnBool(name: string; f: DataFrameCursor -> boolean): DataFrame;
begin
if fSchema.HasColumn(name) then
raise new Exception($'Column "{name}" already exists');
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := new DataFrame;
@ -2737,7 +2796,7 @@ function DataFrame.AddDerivedIntColumn(
): DataFrame;
begin
if Schema.HasColumn(name) then
raise new Exception($'Column "{name}" already exists');
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var rowCount := RowCount;
var data := new integer[rowCount];
@ -3028,15 +3087,11 @@ begin
var rc := columns[0].RowCount;
for var i := 1 to columns.Count - 1 do
if columns[i].RowCount <> rc then
raise new Exception(
$'Schema inconsistent: column "{columns[i].Info.Name}" has RowCount={columns[i].RowCount}, expected {rc}'
);
Error(ER_SCHEMA_INCONSISTENT, columns[i].Info.Name, columns[i].RowCount, rc);
// --- 2. fschema.ColumnCount = columns.Count ---
if fschema.ColumnCount <> columns.Count then
raise new Exception(
$'Schema inconsistent: ColumnCount={fschema.ColumnCount}, columns.Count={columns.Count}'
);
Error(ER_SCHEMA_COLUMNCOUNT_MISMATCH, fSchema.ColumnCount, columns.Count);
// --- 3. имена уникальны и корректно индексированы ---
for var i := 0 to columns.Count - 1 do
@ -3044,15 +3099,11 @@ begin
var name := columns[i].Info.Name;
if not fSchema.HasColumn(name) then
raise new Exception(
$'Schema inconsistent: column "{name}" missing in schema'
);
Error(ER_SCHEMA_COLUMN_MISSING, name);
var idx := GetColumnIndex(name);
if idx <> i then
raise new Exception(
$'Schema inconsistent: GetColumnIndex("{name}")={idx}, expected {i}'
);
Error(ER_SCHEMA_COLUMN_INDEX_INCONSISTENT, name, idx, i);
end;
{$ENDIF}
@ -3113,7 +3164,7 @@ begin
isInt := false;
end
else
raise new Exception('Column is not numeric');
Error(ER_COLUMN_NOT_NUMERIC);
end;
constructor GroupByContext.Create(df: DataFrame; keyCols: array of integer);
@ -3137,7 +3188,7 @@ begin
case df.columns[keyColumn].Info.ColType of
ctInt: key := cursor.Int(keyColumn);
ctStr: key := cursor.Str(keyColumn);
else raise new Exception('Unsupported GroupBy key type');
else Error(ER_GROUPBY_UNSUPPORTED_KEY_TYPE, df.columns[keyColumn].Info.ColType);
end;
if not groups1.ContainsKey(key) then
@ -3171,7 +3222,7 @@ begin
case df.columns[c].Info.ColType of
ctInt: key[i] := cursor.Int(c);
ctStr: key[i] := cursor.Str(c);
else raise new Exception('Unsupported GroupBy key type');
else Error(ER_GROUPBY_UNSUPPORTED_KEY_TYPE, df.columns[c].Info.ColType);
end;
end;
@ -3618,7 +3669,7 @@ begin
var sy := df.Std(iy);
if (sx = 0) or (sy = 0) then
raise new Exception('Zero variance in correlation');
Error(ER_ZERO_VARIANCE);
var cur := df.GetCursor;
var sum := 0.0;
@ -3632,7 +3683,7 @@ begin
end;
if cnt = 0 then
raise new Exception('No valid pairs for correlation');
Error(ER_NO_VALID_PAIRS);
Result := sum / (cnt * sx * sy);
end;
@ -3648,7 +3699,7 @@ begin
var n := names.Count;
if n = 0 then
raise new Exception('No numeric columns for correlation matrix');
Error(ER_NO_NUMERIC_COLUMNS);
var res := new DataFrame;
@ -3679,12 +3730,12 @@ begin
var std := df.Std(idx);
if std = 0 then
raise new Exception('Zero standard deviation');
Error(ER_ZERO_STD_STANDARDIZE);
Result := df.ReplaceColumnFloat(colName, cur ->
begin
if not cur.IsValid(idx) then
raise new Exception;
Error(ER_INVALID_VALUE_IN_COLUMN, colName);
Result := (cur.Float(idx) - mean) / std;
end);
end;
@ -3707,7 +3758,7 @@ begin
means[i] := df.Mean(i);
stds[i] := df.Std(i);
if stds[i] = 0 then
raise new Exception($'Zero std in column {df.fSchema.Names[i]}');
Error(ER_ZERO_STD_COLUMN, df.fSchema.Names[i]);
isNumeric[i] := true;
end;
end;
@ -3761,12 +3812,12 @@ begin
var (mn, mx) := df.MinMax(idx);
if mn = mx then
raise new Exception('Zero range in normalization');
Error(ER_ZERO_RANGE);
Result := df.ReplaceColumnFloat(colName, cur ->
begin
if not cur.IsValid(idx) then
raise new Exception;
Error(ER_INVALID_VALUE_IN_COLUMN, colName);
Result := (cur.Float(idx) - mn) / (mx - mn);
end);
end;
@ -3788,7 +3839,7 @@ begin
begin
var (mn, mx) := df.MinMax(i);
if mn = mx then
raise new Exception($'Zero range in column {df.fSchema.Names[i]}');
Error(ER_ZERO_RANGE_COLUMN, df.fSchema.Names[i]);
mins[i] := mn;
maxs[i] := mx;
isNumeric[i] := true;
@ -3841,7 +3892,7 @@ end;
static function Statistics.Quantile(df: DataFrame; colName: string; p: real): real;
begin
if (p < 0) or (p > 1) then
raise new Exception('Quantile p must be in [0,1]');
ArgumentError(ER_QUANTILE_P_INVALID);
var idx := df.ColumnIndex(colName);
var values := new List<real>;
@ -3852,7 +3903,7 @@ begin
values.Add(cur.Float(idx));
if values.Count = 0 then
raise new Exception('No valid values for quantile');
Error(ER_NO_VALID_VALUES_QUANTILE);
values.Sort;
@ -4320,7 +4371,7 @@ begin
if parts.Length <> colCount then
if strict then
raise new Exception($'CSV format error: expected {colCount} columns, got {parts.Length}');
Error(ER_CSV_COLUMN_COUNT_MISMATCH, colCount, parts.Length);
for var j := 0 to colCount - 1 do
begin
@ -4347,7 +4398,7 @@ begin
end;
if headers = nil then
raise new Exception('Empty CSV');
Error(ER_EMPTY_CSV);
// ---------- PASS 2: allocate ----------
var df := new DataFrame;
@ -4394,7 +4445,7 @@ begin
if unclosedQuote then
begin
if strict then
raise new Exception('CSV format error: unclosed quote');
Error(ER_CSV_UNCLOSED_QUOTE);
for var j := 0 to colCount - 1 do
valid[j][row] := false;
@ -4404,7 +4455,7 @@ begin
end;
if (actualCount <> colCount) and strict then
raise new Exception($'CSV format error: expected {colCount} columns, got {actualCount}');
Error(ER_CSV_COLUMN_COUNT_MISMATCH, colCount, actualCount);
for var j := 0 to colCount - 1 do
begin
@ -4439,8 +4490,7 @@ begin
else
begin
if strict then
raise new Exception(
$'Invalid bool "{line.Substring(starts[j]-1, lens[j])}" in column {headers[j]}');
Error(ER_CSV_INVALID_BOOL, line.Substring(starts[j]-1, lens[j]), headers[j]);
valid[j][row] := false;
end;
end

View file

@ -230,6 +230,36 @@ type
implementation
uses MLExceptions;
const
ER_COLUMN_NOT_INT =
'Столбец не является Int!!Column is not Int';
ER_COLUMN_NOT_FLOAT =
'Столбец не является Float!!Column is not Float';
ER_COLUMN_NOT_STR =
'Столбец не является Str!!Column is not Str';
ER_COLUMN_NOT_BOOL =
'Столбец не является Bool!!Column is not Bool';
ER_DUPLICATE_COLUMN_NAME =
'Повторяющееся имя столбца "{0}"!!Duplicate column name "{0}"';
ER_NAMES_NULL =
'names не может быть nil!!names is nil';
ER_TYPES_NULL =
'types не может быть nil!!types is nil';
ER_NAMES_TYPES_LENGTH_MISMATCH =
'Длины names и types не совпадают!!names and types length mismatch';
ER_ISCATEGORICAL_LENGTH_MISMATCH =
'Длина isCategorical не совпадает с names!!isCategorical length mismatch';
ER_COLUMN_NOT_EXISTS =
'Столбец "{0}" не существует!!Column "{0}" does not exist';
ER_COLUMN_ALREADY_EXISTS =
'Столбец "{0}" уже существует!!Column "{0}" already exists';
ER_INDEX_OUT_OF_RANGE =
'Индекс {0} вне диапазона [0..{1})!!Index {0} out of range [0..{1})';
ER_INDICES_NULL =
'indices не может быть nil!!indices is nil';
//-----------------------------
// Сервисные функции
//-----------------------------
@ -237,25 +267,25 @@ implementation
function NotInt(pos: integer): integer;
begin
Result := 0;
raise new Exception('Column is not Int');
Error(ER_COLUMN_NOT_INT);
end;
function NotFloat(pos: integer): real;
begin
Result := 0;
raise new Exception('Column is not Float');
Error(ER_COLUMN_NOT_FLOAT);
end;
function NotStr(pos: integer): string;
begin
Result := nil;
raise new Exception('Column is not Str');
Error(ER_COLUMN_NOT_STR);
end;
function NotBool(pos: integer): boolean;
begin
Result := False;
raise new Exception('Column is not Bool');
Error(ER_COLUMN_NOT_BOOL);
end;
//-----------------------------
@ -267,7 +297,7 @@ begin
for var i := 0 to names.Length - 1 do
begin
if Result.ContainsKey(names[i]) then
raise new System.ArgumentException($'Duplicate column name "{names[i]}"');
ArgumentError(ER_DUPLICATE_COLUMN_NAME, names[i]);
Result.Add(names[i], i);
end;
end;
@ -275,12 +305,14 @@ end;
constructor DataFrameSchema.Create(names: array of string; types: array of ColumnType;
isCategorical: array of boolean);
begin
if names = nil then raise new System.ArgumentException('names is nil');
if types = nil then raise new System.ArgumentException('types is nil');
if names = nil then
ArgumentNullError(ER_NAMES_NULL);
if types = nil then
ArgumentNullError(ER_TYPES_NULL);
if names.Length <> types.Length then
raise new System.ArgumentException('names and types length mismatch');
ArgumentError(ER_NAMES_TYPES_LENGTH_MISMATCH);
if (isCategorical <> nil) and (isCategorical.Length <> names.Length) then
raise new System.ArgumentException('isCategorical length mismatch');
ArgumentError(ER_ISCATEGORICAL_LENGTH_MISMATCH);
fNames := Copy(names);
fTypes := Copy(types);
@ -293,7 +325,7 @@ end;
function DataFrameSchema.IndexOf(name: string): integer;
begin
if not fIndexByName.ContainsKey(name) then
raise new System.ArgumentException($'Column "{name}" does not exist');
ArgumentError(ER_COLUMN_NOT_EXISTS, name);
Result := fIndexByName[name];
end;
@ -303,21 +335,21 @@ function DataFrameSchema.HasColumn(name: string): boolean :=
function DataFrameSchema.NameAt(i: integer): string;
begin
if (i < 0) or (i >= ColumnCount) then
raise new System.ArgumentOutOfRangeException('i');
ArgumentOutOfRangeError(ER_INDEX_OUT_OF_RANGE, i, ColumnCount);
Result := fNames[i];
end;
function DataFrameSchema.ColumnTypeAt(i: integer): ColumnType;
begin
if (i < 0) or (i >= ColumnCount) then
raise new System.ArgumentOutOfRangeException('i');
ArgumentOutOfRangeError(ER_INDEX_OUT_OF_RANGE, i, ColumnCount);
Result := fTypes[i];
end;
function DataFrameSchema.IsCategoricalAt(i: integer): boolean;
begin
if (i < 0) or (i >= ColumnCount) then
raise new System.ArgumentOutOfRangeException('i');
ArgumentOutOfRangeError(ER_INDEX_OUT_OF_RANGE, i, ColumnCount);
if fIsCategorical = nil then
Result := false
else
@ -326,7 +358,8 @@ end;
function DataFrameSchema.Select(indices: array of integer): DataFrameSchema;
begin
if indices = nil then raise new System.ArgumentException('indices is nil');
if indices = nil then
ArgumentNullError(ER_INDICES_NULL);
var n := indices.Length;
var names := new string[n];
@ -348,13 +381,14 @@ end;
function DataFrameSchema.Drop(indices: array of integer): DataFrameSchema;
begin
if indices = nil then raise new System.ArgumentException('indices is nil');
if indices = nil then
ArgumentNullError(ER_INDICES_NULL);
var drop := new boolean[ColumnCount];
foreach var i in indices do
begin
if (i < 0) or (i >= ColumnCount) then
raise new System.ArgumentOutOfRangeException('indices');
ArgumentOutOfRangeError(ER_INDEX_OUT_OF_RANGE, i, ColumnCount);
drop[i] := true;
end;
@ -369,9 +403,9 @@ end;
function DataFrameSchema.Rename(oldName, newName: string): DataFrameSchema;
begin
if not HasColumn(oldName) then
raise new System.ArgumentException($'Column "{oldName}" does not exist');
ArgumentError(ER_COLUMN_NOT_EXISTS, oldName);
if HasColumn(newName) then
raise new System.ArgumentException($'Column "{newName}" already exists');
ArgumentError(ER_COLUMN_ALREADY_EXISTS, newName);
var names := Copy(fNames);
names[IndexOf(oldName)] := newName;
@ -393,8 +427,10 @@ end;
class function DataFrameSchema.Merge(left, right: DataFrameSchema;
leftKeys, rightKeys: array of integer; rightPrefix: string): DataFrameSchema;
begin
if left = nil then raise new System.ArgumentException('left is nil');
if right = nil then raise new System.ArgumentException('right is nil');
if left = nil then
raise new System.ArgumentException('left is nil');
if right = nil then
raise new System.ArgumentException('right is nil');
if leftKeys.Length <> rightKeys.Length then
raise new System.ArgumentException('join keys length mismatch');

View file

@ -50,6 +50,13 @@ type
implementation
const
ER_TO_MATRIX_NO_COLUMNS =
'ToMatrix: не указаны столбцы!!ToMatrix: no columns specified';
ER_TO_VECTOR_NON_NUMERIC =
'ToVector: столбец "{0}" содержит нечисловые или NA значения!!' +
'ToVector: column "{0}" contains non-numeric or NA values';
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
begin
var df := Self;
@ -57,7 +64,7 @@ begin
var p := colNames.Length;
if p = 0 then
raise new Exception('ToMatrix: no columns specified');
ArgumentError(ER_TO_MATRIX_NO_COLUMNS);
Result := new Matrix(n, p);
@ -92,9 +99,7 @@ begin
var value: real;
if not col.TryGetNumericValue(i, value) then
raise new Exception(
'ToVector: column "' + colName +
'" contains non-numeric or NA values');
ArgumentError(ER_TO_VECTOR_NON_NUMERIC, colName);
Result[i] := value;
end;

View file

@ -2,13 +2,24 @@
interface
const
ER_DIM_MISMATCH =
'Несоответствие размерностей: {0} и {1}!!Dimension mismatch: {0} and {1}';
ER_PARAM_VALUES_EMPTY =
'Список paramValues пуст!!paramValues is empty';
ER_EMPTY_DATASET =
'Набор данных пуст!!Dataset is empty';
ER_LAMBDA_NEGATIVE =
'Параметр lambda должен быть >= 0!!Lambda parameter must be >= 0';
ER_FIT_NOT_CALLED =
'Метод Fit() не вызван!!Fit() must be called first';
ER_MODEL_NULL =
'Модель не может быть nil!!Model cannot be nil';
type
/// Базовое исключение ML-библиотеки
MLException = class(Exception);
/// Ошибка аргументов ML-методов
MLArgumentException = class(System.ArgumentException);
/// Попытка использования необученной модели/пайплайна
MLNotFittedException = class(MLException);
@ -19,6 +30,8 @@ procedure Error(msg: string; params args: array of object);
procedure ArgumentError(msg: string; params args: array of object);
procedure NotFittedError(msg: string; params args: array of object);
procedure DimensionError(msg: string; params args: array of object);
procedure ArgumentNullError(msg: string; params args: array of object);
procedure ArgumentOutOfRangeError(msg: string; params args: array of object);
implementation
@ -39,7 +52,7 @@ end;
procedure ArgumentError(msg: string; params args: array of object);
begin
raise new MLArgumentException(FormatSafe(msg, args));
raise new System.ArgumentException(FormatSafe(msg, args));
end;
procedure NotFittedError(msg: string; params args: array of object);
@ -52,4 +65,14 @@ begin
raise new MLDimensionException(FormatSafe(msg, args));
end;
procedure ArgumentNullError(msg: string; params args: array of object);
begin
raise new System.ArgumentNullException(FormatSafe(msg, args));
end;
procedure ArgumentOutOfRangeError(msg: string; params args: array of object);
begin
raise new System.ArgumentOutOfRangeException(FormatSafe(msg, args));
end;
end.

View file

@ -593,7 +593,33 @@ type
implementation
uses System;
uses MLExceptions;
const
ER_PIPELINE_NO_STEPS =
'Pipeline должен содержать хотя бы один шаг!!Pipeline requires at least one step';
ER_PIPELINE_LAST_NOT_MODEL =
'Последний шаг Pipeline должен быть моделью (IModel)!!Last step must be a model (IModel)';
ER_PIPELINE_INVALID_STEP_ORDER =
'Все шаги, кроме последнего, должны быть трансформерами!!All steps except the last must be transformers';
ER_TRANSFORMER_NULL =
'Трансформер не может быть nil!!Transformer cannot be nil';
ER_PROBA_NOT_SUPPORTED =
'Модель не поддерживает предсказание вероятностей!!Model does not support probability prediction';
ER_RANGE_INVALID =
'rangeMax должен быть больше rangeMin!!rangeMax must be greater than rangeMin';
ER_K_MUST_BE_POSITIVE =
'Параметр k должен быть > 0!!Parameter k must be > 0';
ER_K_EXCEEDS_FEATURES =
'k превышает число признаков!!k exceeds feature count';
ER_THRESHOLD_NEGATIVE =
'Порог threshold должен быть >= 0!!threshold must be >= 0';
ER_CHI_SQUARE_NEGATIVE =
'ChiSquare требует неотрицательные признаки!!ChiSquare requires non-negative features';
ER_UNKNOWN_FEATURE_SCORE =
'Неизвестный тип FeatureScore!!Unknown FeatureScore type';
ER_SELECTKBEST_FIT_INVALID =
'Для SelectKBest необходимо вызывать Fit(X, y)!!SelectKBest requires Fit(X, y)';
//-----------------------------
// LinearRegression
@ -609,9 +635,9 @@ begin
var n := X.RowCount;
if n = 0 then
raise new Exception('Empty dataset');
ArgumentError(ER_EMPTY_DATASET);
if y.Length <> n then
raise new Exception('Dimension mismatch in Fit');
DimensionError(ER_DIM_MISMATCH, y.Length, n);
// 1. Means
var meanX := X.ColumnMeans;
@ -639,10 +665,10 @@ end;
function LinearRegression.Predict(X: Matrix): Vector;
begin
if not ffitted then
raise new Exception('Model not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
if X.ColCount <> fcoef.Length then
raise new Exception('Dimension mismatch in Predict');
DimensionError(ER_DIM_MISMATCH, X.ColCount, fCoef.Length);
Result := X * fcoef + intercept;
end;
@ -696,7 +722,7 @@ begin
var p := X.ColCount;
if y.Length <> n then
raise new Exception('Dimension mismatch in Fit');
DimensionError(ER_DIM_MISMATCH, y.Length, n);
fCoef := new Vector(p);
fIntercept := 0.0;
@ -726,7 +752,7 @@ end;
function LogisticRegression.PredictProba(X: Matrix): Vector;
begin
if not fFitted then
raise new Exception('Model not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
Result := Activations.Sigmoid(X * fCoef + fIntercept);
end;
@ -767,7 +793,7 @@ end;
constructor RidgeRegression.Create(lambda: real);
begin
if lambda < 0 then
raise new ArgumentException('lambda must be >= 0');
ArgumentError(ER_LAMBDA_NEGATIVE, lambda);
fLambda := lambda;
fFitted := false;
end;
@ -775,7 +801,7 @@ end;
function RidgeRegression.Fit(X: Matrix; y: Vector): IModel;
begin
if X.RowCount <> y.Length then
raise new ArgumentException('X and y size mismatch');
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
var n := X.RowCount;
var p := X.ColCount;
@ -808,7 +834,7 @@ end;
function RidgeRegression.Predict(X: Matrix): Vector;
begin
if not fFitted then
raise new InvalidOperationException('Model is not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
Result := X * fCoef;
for var i := 0 to Result.Length - 1 do
@ -833,7 +859,7 @@ end;
constructor ElasticNet.Create(lambda1, lambda2: real; maxIter: integer; tol: real);
begin
if (lambda1 < 0) or (lambda2 < 0) then
raise new ArgumentException('lambda must be >= 0');
ArgumentError(ER_LAMBDA_NEGATIVE);
fLambda1 := lambda1;
fLambda2 := lambda2;
@ -855,7 +881,7 @@ end;
function ElasticNet.Fit(X: Matrix; y: Vector): IModel;
begin
if X.RowCount <> y.Length then
raise new ArgumentException('X and y size mismatch');
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
// Loss(β) = ||y - (Xβ + b)||² + λ1||β||1 + λ2||β||2²
@ -921,7 +947,7 @@ end;
function ElasticNet.Predict(X: Matrix): Vector;
begin
if not fFitted then
raise new InvalidOperationException('Model is not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
Result := X * fCoef;
for var i := 0 to Result.Length - 1 do
@ -1036,7 +1062,7 @@ end;
function MulticlassLogisticRegression.PredictProbaMatrix(X: Matrix): Matrix;
begin
if not fFitted then
raise new Exception('Model not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
var m := X.RowCount;
@ -1106,20 +1132,20 @@ constructor Pipeline.Create(model: IModel);
begin
Create;
if model = nil then
raise new ArgumentException('Model cannot be nil');
ArgumentError(ER_MODEL_NULL);
fModel := model;
end;
class function Pipeline.Build(params steps: array of IPipeStep): Pipeline;
begin
if (steps = nil) or (Length(steps) = 0) then
raise new ArgumentException('Pipeline requires at least one step');
ArgumentError(ER_PIPELINE_NO_STEPS);
// последний шаг должен быть моделью
var last := steps[High(steps)];
if not (last is IModel) then
raise new ArgumentException('Last step must be a model (IModel)');
ArgumentError(ER_PIPELINE_LAST_NOT_MODEL);
var pipe := new Pipeline(last as IModel);
@ -1129,7 +1155,7 @@ begin
var step := steps[i];
if not (step is ITransformer) then
raise new ArgumentException('All steps except the last must be transformers');
ArgumentError(ER_PIPELINE_INVALID_STEP_ORDER);
pipe.Add(step as ITransformer);
end;
@ -1140,7 +1166,7 @@ end;
function Pipeline.Add(t: ITransformer): Pipeline;
begin
if t = nil then
raise new ArgumentException('Transformer cannot be nil');
ArgumentError(ER_TRANSFORMER_NULL);
fTransformers.Add(t);
Result := Self;
@ -1149,7 +1175,7 @@ end;
function Pipeline.SetModel(m: IModel): Pipeline;
begin
if m = nil then
raise new ArgumentException('Model cannot be nil');
ArgumentError(ER_TRANSFORMER_NULL);
fModel := m;
Result := Self;
@ -1158,7 +1184,7 @@ end;
function Pipeline.Fit(X: Matrix; y: Vector): IModel;
begin
if fModel = nil then
raise new Exception('Model is not set');
ArgumentError(ER_MODEL_NULL);
var Xt := X;
@ -1180,7 +1206,7 @@ end;
function Pipeline.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new Exception('Pipeline not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
var Xt := X;
@ -1193,7 +1219,7 @@ end;
function Pipeline.Predict(X: Matrix): Vector;
begin
if not fFitted then
raise new Exception('Pipeline not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
var Xt := Transform(X);
Result := fModel.Predict(Xt);
@ -1202,10 +1228,10 @@ end;
function Pipeline.PredictProba(X: Matrix): Vector;
begin
if not fFitted then
raise new Exception('Pipeline not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
if not (fModel is IProbabilisticClassifier) then
raise new Exception('Model does not support probability prediction');
Error(ER_PROBA_NOT_SUPPORTED);
var Xt := Transform(X);
@ -1234,7 +1260,7 @@ end;
function Pipeline.Clone: IModel;
begin
if fModel = nil then
raise new InvalidOperationException('Pipeline has no model');
ArgumentError(ER_MODEL_NULL);
var p := new Pipeline;
@ -1262,7 +1288,7 @@ end;
function StandardScaler.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new Exception('StandardScaler not fitted');
Error(ER_PROBA_NOT_SUPPORTED);
var n := X.RowCount;
var p := X.ColCount;
@ -1301,7 +1327,7 @@ end;
constructor MinMaxScaler.Create(rangeMin: real; rangeMax: real);
begin
if rangeMax <= rangeMin then
raise new Exception('rangeMax must be greater than rangeMin');
ArgumentError(ER_RANGE_INVALID);
fRangeMin := rangeMin;
fRangeMax := rangeMax;
@ -1320,7 +1346,7 @@ end;
function MinMaxScaler.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new Exception('MinMaxScaler is not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
var n := X.RowCount;
var p := X.ColCount;
@ -1362,7 +1388,7 @@ end;
constructor PCATransformer.Create(k: integer);
begin
if k <= 0 then
raise new ArgumentException('k must be > 0');
ArgumentError(ER_K_MUST_BE_POSITIVE);
fK := k;
fFitted := false;
@ -1371,7 +1397,7 @@ end;
function PCATransformer.Fit(X: Matrix): ITransformer;
begin
if fK > X.ColCount then
raise new ArgumentException('k exceeds feature count');
ArgumentError(ER_K_EXCEEDS_FEATURES);
fMean := X.ColumnMeans;
@ -1392,7 +1418,7 @@ end;
function PCATransformer.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new InvalidOperationException('PCA not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
var Xc := X.Clone;
@ -1420,7 +1446,7 @@ end;
constructor VarianceThreshold.Create(threshold: real);
begin
if threshold < 0 then
raise new ArgumentException('threshold must be >= 0');
ArgumentError(ER_THRESHOLD_NEGATIVE);
fThreshold := threshold;
fFitted := false;
@ -1445,7 +1471,7 @@ end;
function VarianceThreshold.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new InvalidOperationException('VarianceThreshold not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
var n := X.RowCount;
var k := fSelected.Length;
@ -1476,7 +1502,7 @@ end;
constructor SelectKBest.Create(k: integer; score: FeatureScore);
begin
if k <= 0 then
raise new ArgumentException('k must be > 0');
ArgumentError(ER_K_MUST_BE_POSITIVE);
fK := k;
fScoreType := score;
@ -1487,7 +1513,7 @@ end;
constructor SelectKBest.Create(k: integer; scoreFunc: (Vector, Vector) -> real);
begin
if k <= 0 then
raise new ArgumentException('k must be > 0');
ArgumentError(ER_K_MUST_BE_POSITIVE);
fK := k;
fScoreFunc := scoreFunc;
@ -1589,7 +1615,7 @@ begin
begin
var v := feature[i];
if v < 0 then
raise new ArgumentException('ChiSquare requires non-negative features');
ArgumentError(ER_CHI_SQUARE_NEGATIVE);
total += v;
@ -1630,7 +1656,7 @@ begin
FeatureScore.AnovaF: Result := ComputeAnovaF(feature, y);
FeatureScore.ChiSquare: Result := ComputeChiSquare(feature, y);
else
raise new InvalidOperationException('Unknown FeatureScore type');
Error(ER_UNKNOWN_FEATURE_SCORE);
end;
end;
@ -1671,13 +1697,13 @@ end;
function SelectKBest.Fit(X: Matrix): ITransformer;
begin
Result := nil;
raise new InvalidOperationException('Target variable is required');
ArgumentError(ER_SELECTKBEST_FIT_INVALID);
end;
function SelectKBest.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new InvalidOperationException('SelectKBest not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
var n := X.RowCount;
var k := fSelected.Length;
@ -1731,7 +1757,7 @@ end;
function Normalizer.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new InvalidOperationException('Normalizer not fitted');
NotFittedError(ER_FIT_NOT_CALLED);
var n := X.RowCount;
var p := X.ColCount;

View file

@ -138,6 +138,8 @@ type
implementation
uses MLExceptions;
//-----------------------------
// Metrics
//-----------------------------
@ -145,7 +147,7 @@ implementation
static function Metrics.MSE(yTrue, yPred: Vector): real;
begin
if yTrue.Length <> yPred.Length then
raise new Exception('Dimension mismatch in MSE');
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
var n := yTrue.Length;
var s := 0.0;
@ -162,7 +164,7 @@ end;
static function Metrics.MAE(yTrue, yPred: Vector): real;
begin
if yTrue.Length <> yPred.Length then
raise new Exception('Dimension mismatch in MAE');
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
var n := yTrue.Length;
var s := 0.0;
@ -176,7 +178,7 @@ end;
static function Metrics.R2(yTrue, yPred: Vector): real;
begin
if yTrue.Length <> yPred.Length then
raise new Exception('Dimension mismatch in R2');
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
var n := yTrue.Length;
var meanY := yTrue.Mean;
@ -202,7 +204,7 @@ end;
static function Metrics.LogLoss(yTrue, yProb: Vector): real;
begin
if yTrue.Length <> yProb.Length then
raise new Exception('Dimension mismatch in LogLoss');
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yProb.Length);
var n := yTrue.Length;
var eps := 1e-15;
@ -224,7 +226,7 @@ end;
static function Metrics.ROC(yTrue, yProb: Vector): (Vector, Vector);
begin
if yTrue.Length <> yProb.Length then
raise new Exception('Dimension mismatch in ROC');
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yProb.Length);
var n := yTrue.Length;
@ -319,7 +321,7 @@ end;
constructor ConfusionMatrix.Create(yTrue, yPred: Vector);
begin
if yTrue.Length <> yPred.Length then
raise new Exception('Dimension mismatch in ConfusionMatrix');
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
for var i := 0 to yTrue.Length - 1 do
begin

View file

@ -173,6 +173,14 @@ type
implementation
uses MLExceptions;
const
ER_SCALER_NO_COLUMNS =
'StandardScaler: столбцы не указаны!!StandardScaler: columns not specified';
ER_SCALER_COLUMN_NOT_NUMERIC =
'StandardScaler: столбец "{0}" не является числовым!!StandardScaler: column "{0}" is not numeric';
//-----------------------------
// StandardScaler
//-----------------------------
@ -180,7 +188,7 @@ implementation
constructor DataStandardScaler.Create(params columns: array of string);
begin
if (columns = nil) or (columns.Length = 0) then
raise new ArgumentException('StandardScaler: columns not specified');
ArgumentError(ER_SCALER_NO_COLUMNS);
cols := columns;
fitted := false;
@ -199,7 +207,7 @@ begin
var ct := df.Schema.ColumnTypeAt(idx);
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
raise new Exception($'StandardScaler: column "{colName}" is not numeric');
Error(ER_SCALER_COLUMN_NOT_NUMERIC, colName);
var sum := 0.0;
var sum2 := 0.0;
@ -710,7 +718,7 @@ end;
function DataPipeline.Transform(df: DataFrame): DataFrame;
begin
if not fitted then
raise new Exception('Pipeline is not fitted');
NotFittedError(ER_FIT_NOT_CALLED);NotFittedError(ER_FIT_NOT_CALLED);
var current := df;
foreach var step in steps do

View file

@ -76,6 +76,18 @@ const
ER_DIM_MISMATCH_TRAIN_TEST =
'Несоответствие размерностей в TrainTestSplit: X.RowCount={0}, y.Length={1}!!' +
'Dimension mismatch in TrainTestSplit: X.RowCount={0}, y.Length={1}';
ER_TEST_RATIO_INVALID =
'Параметр testRatio должен быть в интервале (0,1), получено {0}!!' +
'Parameter testRatio must be in (0,1), got {0}';
ER_K_INVALID =
'Некорректное значение k в KFold: k={0}, n={1}!!' +
'Invalid k in KFold: k={0}, n={1}';
ER_K_INVALID_STRATIFIED =
'Некорректное значение k в StratifiedKFold: k={0}, n={1}!!' +
'Invalid k in StratifiedKFold: k={0}, n={1}';
ER_STRATIFIED_LABELS_INVALID =
'StratifiedKFold поддерживает только метки 0/1!!' +
'StratifiedKFold supports only 0/1 labels';
//-----------------------------
// Validation
@ -88,7 +100,7 @@ begin
DimensionError(ER_DIM_MISMATCH_TRAIN_TEST, X.RowCount, y.Length);
if (testRatio <= 0) or (testRatio >= 1) then
raise new Exception('testRatio must be in (0,1)');
ArgumentError(ER_TEST_RATIO_INVALID, testRatio);
var n := X.RowCount;
var p := X.ColCount;
@ -137,7 +149,7 @@ static function Validation.KFold(n, k: integer; seed: integer):
sequence of (array of integer, array of integer);
begin
if (k < 2) or (k > n) then
raise new Exception('Invalid k in KFold');
ArgumentError(ER_K_INVALID, k, n);
var rnd := new System.Random(seed);
var idx := (0..n-1).OrderBy(i -> rnd.Next).ToArray;
@ -163,7 +175,7 @@ static function Validation.StratifiedKFold(y: Vector; k: integer;
begin
var n := y.Length;
if (k < 2) or (k > n) then
raise new Exception('Invalid k in StratifiedKFold');
ArgumentError(ER_K_INVALID_STRATIFIED, k, n);
var rnd := new System.Random(seed);
@ -178,7 +190,7 @@ begin
.ToArray;
if idx0.Length + idx1.Length <> n then
raise new Exception('StratifiedKFold supports only 0/1 labels');
ArgumentError(ER_STRATIFIED_LABELS_INVALID);
var base0 := idx0.Length div k;
var extra0 := idx0.Length mod k;
@ -213,7 +225,7 @@ static function Validation.CrossValidate(model: IModel; X: Matrix; y: Vector;
k: integer; metric: (Vector,Vector) -> real; seed: integer): real;
begin
if X.RowCount <> y.Length then
raise new Exception('Dimension mismatch in CrossValidate');
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
var total := 0.0;
var folds := 0;
@ -260,7 +272,7 @@ static function Validation.StratifiedCrossValidate(
): real;
begin
if X.RowCount <> y.Length then
raise new Exception('Dimension mismatch in StratifiedCrossValidate');
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
var total := 0.0;
var folds := 0;
@ -314,7 +326,7 @@ class function GridSearch.Search<T>(
): (real, real, T); where T: IModel;
begin
if paramValues.Length = 0 then
raise new System.ArgumentException('paramValues is empty');
ArgumentError(ER_PARAM_VALUES_EMPTY);
var bestParam := paramValues[0];
var bestScore := -1e308;