Поправленные исключения в модулях ML (не до конца)
This commit is contained in:
parent
d09da0213e
commit
3d8ebff9ba
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -593,8 +593,34 @@ 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue