used_cars
Мелкие дополнения ML
This commit is contained in:
parent
7c31caa0b7
commit
911142c83e
|
|
@ -22,7 +22,7 @@ begin
|
|||
var y := df.EncodeLabels(ds.Target);
|
||||
|
||||
// Делим данные на обучающую и тестовую выборки
|
||||
var (Xtrain, Xtest, ytrain, ytest) := Validation.TrainTestSplit(X, y, 0.2, 3);
|
||||
var (Xtrain, Xtest, ytrain, ytest) := Validation.TrainTestSplit(X, y, 0.2, seed := 3);
|
||||
|
||||
// Создаём модель логистической регрессии
|
||||
var model := new LogisticRegression(0.001);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
uses MLABC,PlotML;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
Вес,ВысотаВХолке,Порода
|
||||
20,33,бульдог
|
||||
22,34,бульдог
|
||||
24,35,бульдог
|
||||
25,36,бульдог
|
||||
19,35,бульдог
|
||||
21,35,бульдог
|
||||
26,34,бульдог
|
||||
20,36,бульдог
|
||||
14,39,спаниель
|
||||
15,40,спаниель
|
||||
16,41,спаниель
|
||||
18,42,спаниель
|
||||
17,43,спаниель
|
||||
15,38,спаниель
|
||||
19,40,спаниель
|
||||
16,40,спаниель
|
||||
''');
|
||||
|
||||
var X := df.ToMatrix(['Вес', 'ВысотаВХолке']);
|
||||
var target := df.EncodeTarget('Порода');
|
||||
var y := target.Labels;
|
||||
var classNames := target.ClassNames;
|
||||
|
||||
var model := new LogisticRegression;
|
||||
model.Fit(X, y);
|
||||
|
||||
var example := [20.0, 38.0];
|
||||
var pred := model.PredictOne(example);
|
||||
var prob := model.PredictProbaOne(example);
|
||||
|
||||
Println('Предсказанная порода:', classNames[pred]);
|
||||
Println('Предсказанные вероятности:');
|
||||
Println(classnames[0]:10,'-',prob[0]:0:2);
|
||||
Println(classnames[1]:10,'-',prob[1]:0:2);
|
||||
|
||||
Plot.XLabel := df.ColumnNames[0];
|
||||
Plot.YLabel := df.ColumnNames[1];
|
||||
Plot.Title := 'Бульдоги и спаниели';
|
||||
|
||||
Plot.Points(X.Col(0),X.Col(1),y);
|
||||
Plot.Points([example[0]],[example[1]],Colors.Black,size := 8);
|
||||
|
||||
var (A,B,C) := model.DecisionBoundary.Coefficients;
|
||||
Plot.Line(A,B,C);
|
||||
end.
|
||||
|
|
@ -496,7 +496,7 @@ namespace PascalABCCompiler.ParserTools
|
|||
ss[0] = ss[0].Trim();
|
||||
if (ss[0].Length > 0)
|
||||
// мы сюда точно не должны попасть. Это должна быть обычная строка!
|
||||
this.AddErrorFromResource("IMPOSSIBLE_MULTILINE_ERROR", sc);
|
||||
this.AddErrorFromResource("IMPOSSIBLE_MULTILINE_ERROR"+" " + ss[0].Length.ToString(), sc);
|
||||
|
||||
if (ss[ss.Length - 1].Trim().Length > 0)
|
||||
// There should be no non-whitespace characters before the closing quotes of the multiline string
|
||||
|
|
|
|||
10001
ReleaseGenerators/Files/Datasets/used_cars_price.csv
Normal file
10001
ReleaseGenerators/Files/Datasets/used_cars_price.csv
Normal file
File diff suppressed because it is too large
Load diff
45
ReleaseGenerators/Files/Datasets/used_cars_price.meta
Normal file
45
ReleaseGenerators/Files/Datasets/used_cars_price.meta
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
name = UsedCarsPrice
|
||||
|
||||
task = regression
|
||||
|
||||
features = model,year,transmission,mileage_km,fuelType,l_100km,engineSize,Make
|
||||
target = price_k_rub
|
||||
|
||||
description.en = Used car prices with model, year, mileage, engine, fuel and transmission features. The target variable is price in thousand rubles.
|
||||
description.ru = Цены на автомобили с пробегом по модели, году выпуска, пробегу, двигателю, топливу и типу коробки передач. Целевая переменная — цена в тыс. руб.
|
||||
modified.en = Modified version with mileage, fuel consumption and price converted to units familiar to Russian users.
|
||||
modified.ru = Модифицированная версия датасета с переводом пробега, расхода топлива и цены в привычные для российской аудитории величины.
|
||||
|
||||
source = Kaggle: Used Car Price Prediction Dataset
|
||||
url = https://www.kaggle.com/datasets/taeefnajib/used-car-price-prediction-dataset
|
||||
|
||||
feature.model = categorical
|
||||
feature.year = int
|
||||
feature.price_k_rub = float
|
||||
feature.transmission = categorical
|
||||
feature.mileage_km = int
|
||||
feature.fuelType = categorical
|
||||
feature.l_100km = float
|
||||
feature.engineSize = float
|
||||
feature.Make = categorical
|
||||
|
||||
feature.model.ru = модель
|
||||
feature.year.ru = год выпуска
|
||||
feature.price_k_rub.ru = цена в тыс. руб.
|
||||
feature.transmission.ru = коробка передач
|
||||
feature.mileage_km.ru = пробег в километрах
|
||||
feature.fuelType.ru = тип топлива
|
||||
feature.l_100km.ru = расход в литрах на 100 км
|
||||
feature.engineSize.ru = объём двигателя в литрах
|
||||
feature.Make.ru = марка
|
||||
|
||||
feature.model.en = model
|
||||
feature.year.en = year
|
||||
feature.price_k_rub.en = price in thousand rubles
|
||||
feature.transmission.en = transmission
|
||||
feature.mileage_km.en = mileage in kilometres
|
||||
feature.fuelType.en = fuel type
|
||||
feature.l_100km.en = fuel consumption in litres per 100 km
|
||||
feature.engineSize.en = engine size in litres
|
||||
feature.Make.en = make
|
||||
|
||||
|
|
@ -2,7 +2,19 @@
|
|||
|
||||
interface
|
||||
|
||||
// Здесь пока только методы расширения поэтому секция interface - пуста
|
||||
type
|
||||
/// Результат кодирования целевого столбца:
|
||||
/// целочисленные метки и соответствующие имена классов.
|
||||
EncodedTarget = record
|
||||
Labels: array of integer;
|
||||
ClassNames: array of string;
|
||||
|
||||
/// Возвращает код класса для строки с индексом rowIndex.
|
||||
function ClassIndex(rowIndex: integer): integer;
|
||||
|
||||
/// Возвращает имя класса для строки с индексом rowIndex.
|
||||
function ClassName(rowIndex: integer): string;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
|
|
@ -23,7 +35,29 @@ const
|
|||
ER_ENCODELABELS_UNSUPPORTED_TYPE =
|
||||
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
|
||||
ER_TARGET_HAS_NA =
|
||||
'Целевой столбец "{0}" содержит NA!!Target column "{0}" contains NA values';
|
||||
'Целевой столбец "{0}" содержит NA!!Target column "{0}" contains NA values';
|
||||
ER_ENCODED_TARGET_ROW_INDEX_OUT_OF_RANGE =
|
||||
'Индекс строки {0} вне диапазона [0, {1})!!Row index {0} is out of range [0, {1})';
|
||||
ER_ENCODED_TARGET_CLASS_INDEX_OUT_OF_RANGE =
|
||||
'Индекс класса {0} вне диапазона [0, {1})!!Class index {0} is out of range [0, {1})';
|
||||
|
||||
function EncodedTarget.ClassIndex(rowIndex: integer): integer;
|
||||
begin
|
||||
if (rowIndex < 0) or (rowIndex >= Labels.Length) then
|
||||
ArgumentOutOfRangeError(ER_ENCODED_TARGET_ROW_INDEX_OUT_OF_RANGE, rowIndex, Labels.Length);
|
||||
|
||||
Result := Labels[rowIndex];
|
||||
end;
|
||||
|
||||
function EncodedTarget.ClassName(rowIndex: integer): string;
|
||||
begin
|
||||
var cls := ClassIndex(rowIndex);
|
||||
|
||||
if (cls < 0) or (cls >= ClassNames.Length) then
|
||||
ArgumentOutOfRangeError(ER_ENCODED_TARGET_CLASS_INDEX_OUT_OF_RANGE, cls, ClassNames.Length);
|
||||
|
||||
Result := ClassNames[cls];
|
||||
end;
|
||||
|
||||
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
|
||||
begin
|
||||
|
|
@ -215,6 +249,16 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
/// Кодирует целевой столбец DataFrame и возвращает
|
||||
/// целочисленные метки вместе с массивом имён классов.
|
||||
/// Порядок кодирования соответствует порядку первого появления значений.
|
||||
function EncodeTarget(Self: DataFrame; target: string): EncodedTarget; extensionmethod;
|
||||
begin
|
||||
var classes: array of string;
|
||||
Result.Labels := Self.EncodeLabels(target, classes);
|
||||
Result.ClassNames := classes;
|
||||
end;
|
||||
|
||||
/// Преобразует строковые метки целевого столбца в целочисленные индексы (0,1,2,...)
|
||||
/// с использованием заданного массива classes (mapping индекс → метка).
|
||||
/// classes должен быть получен ранее с помощью EncodeLabels.
|
||||
|
|
|
|||
|
|
@ -99,11 +99,14 @@ type
|
|||
|
||||
function GetColumn(name: string): Column;
|
||||
function GetSchema: DataFrameSchema;
|
||||
procedure CheckRowIndex(rowIndex: integer);
|
||||
|
||||
function CloneWithSharedColumns: DataFrame;
|
||||
public
|
||||
/// Создает пустой DataFrame
|
||||
constructor Create;
|
||||
/// Возвращает имена столбцов
|
||||
property ColumnNames: array of string read Schema.ColumnNames;
|
||||
|
||||
/// Возвращает копию схемы DataFrame
|
||||
property Schema: DataFrameSchema read GetSchema;
|
||||
|
|
@ -220,14 +223,34 @@ type
|
|||
|
||||
/// Возвращает целочисленный массив значений столбца с данным именем.
|
||||
function Int(name: string): array of integer;
|
||||
/// Возвращает целочисленное значение ячейки по индексам строки и столбца.
|
||||
function Int(rowIndex, colIndex: integer): integer;
|
||||
/// Возвращает целочисленное значение ячейки по индексу строки и имени столбца.
|
||||
function Int(rowIndex: integer; colName: string): integer;
|
||||
/// Возвращает вещественный массив значений столбца с данным именем.
|
||||
function Float(name: string): array of real;
|
||||
/// Возвращает вещественное значение ячейки по индексам строки и столбца.
|
||||
function Float(rowIndex, colIndex: integer): real;
|
||||
/// Возвращает вещественное значение ячейки по индексу строки и имени столбца.
|
||||
function Float(rowIndex: integer; colName: string): real;
|
||||
/// Возвращает строковый массив значений столбца с данным именем.
|
||||
function Str(name: string): array of string;
|
||||
/// Возвращает строковое значение ячейки по индексам строки и столбца.
|
||||
function Str(rowIndex, colIndex: integer): string;
|
||||
/// Возвращает строковое значение ячейки по индексу строки и имени столбца.
|
||||
function Str(rowIndex: integer; colName: string): string;
|
||||
/// Возвращает логический массив значений столбца с данным именем.
|
||||
function Bool(name: string): array of boolean;
|
||||
/// Возвращает логическое значение ячейки по индексам строки и столбца.
|
||||
function Bool(rowIndex, colIndex: integer): boolean;
|
||||
/// Возвращает логическое значение ячейки по индексу строки и имени столбца.
|
||||
function Bool(rowIndex: integer; colName: string): boolean;
|
||||
/// Возвращает массив DateTime-значений столбца с данным именем.
|
||||
function DateTime(name: string): array of System.DateTime;
|
||||
/// Возвращает DateTime-значение ячейки по индексам строки и столбца.
|
||||
function DateTime(rowIndex, colIndex: integer): System.DateTime;
|
||||
/// Возвращает DateTime-значение ячейки по индексу строки и имени столбца.
|
||||
function DateTime(rowIndex: integer; colName: string): System.DateTime;
|
||||
|
||||
/// Вычисляет сумму значений столбца по индексу
|
||||
function Sum(colIndex: integer): real;
|
||||
|
|
@ -770,6 +793,8 @@ const
|
|||
'Дублирующееся имя агрегированной колонки!!Duplicate aggregated column name';
|
||||
ER_ROW_INDEX_OUT_OF_RANGE =
|
||||
'Индекс строки вне диапазона!!Row index is out of range';
|
||||
ER_VALUE_IS_NA =
|
||||
'Значение в столбце "{0}" равно NA!!Value in column "{0}" is NA';
|
||||
ER_UNSUPPORTED_COLUMN_TYPE =
|
||||
'Неподдерживаемый тип столбца!!Unsupported column type';
|
||||
ER_JOIN_FLOAT_KEY_NOT_SUPPORTED =
|
||||
|
|
@ -2473,6 +2498,12 @@ begin
|
|||
);
|
||||
end;
|
||||
|
||||
procedure DataFrame.CheckRowIndex(rowIndex: integer);
|
||||
begin
|
||||
if (rowIndex < 0) or (rowIndex >= RowCount) then
|
||||
ArgumentOutOfRangeError(ER_ROW_INDEX_OUT_OF_RANGE);
|
||||
end;
|
||||
|
||||
function DataFrame.GetCursor: DataFrameCursor :=
|
||||
new DataFrameCursor(columns.ToArray,fSchema);
|
||||
|
||||
|
|
@ -2516,26 +2547,106 @@ begin
|
|||
Result := GetIntColumn(name);
|
||||
end;
|
||||
|
||||
function DataFrame.Int(rowIndex, colIndex: integer): integer;
|
||||
begin
|
||||
CheckRowIndex(rowIndex);
|
||||
CheckColumnIndex(colIndex);
|
||||
|
||||
var c := IntColumn(columns[colIndex]);
|
||||
if not c.IsValid[rowIndex] then
|
||||
Error(ER_VALUE_IS_NA, fSchema.NameAt(colIndex));
|
||||
Result := c.Data[rowIndex];
|
||||
end;
|
||||
|
||||
function DataFrame.Int(rowIndex: integer; colName: string): integer;
|
||||
begin
|
||||
Result := Int(rowIndex, ColumnIndex(colName));
|
||||
end;
|
||||
|
||||
function DataFrame.Float(name: string): array of real;
|
||||
begin
|
||||
Result := GetFloatColumn(name);
|
||||
end;
|
||||
|
||||
function DataFrame.Float(rowIndex, colIndex: integer): real;
|
||||
begin
|
||||
CheckRowIndex(rowIndex);
|
||||
CheckColumnIndex(colIndex);
|
||||
|
||||
var c := FloatColumn(columns[colIndex]);
|
||||
if not c.IsValid[rowIndex] then
|
||||
Error(ER_VALUE_IS_NA, fSchema.NameAt(colIndex));
|
||||
Result := c.Data[rowIndex];
|
||||
end;
|
||||
|
||||
function DataFrame.Float(rowIndex: integer; colName: string): real;
|
||||
begin
|
||||
Result := Float(rowIndex, ColumnIndex(colName));
|
||||
end;
|
||||
|
||||
function DataFrame.Str(name: string): array of string;
|
||||
begin
|
||||
Result := GetStrColumn(name);
|
||||
end;
|
||||
|
||||
function DataFrame.Str(rowIndex, colIndex: integer): string;
|
||||
begin
|
||||
CheckRowIndex(rowIndex);
|
||||
CheckColumnIndex(colIndex);
|
||||
|
||||
var c := StrColumn(columns[colIndex]);
|
||||
if not c.IsValid[rowIndex] then
|
||||
Error(ER_VALUE_IS_NA, fSchema.NameAt(colIndex));
|
||||
Result := c.Data[rowIndex];
|
||||
end;
|
||||
|
||||
function DataFrame.Str(rowIndex: integer; colName: string): string;
|
||||
begin
|
||||
Result := Str(rowIndex, ColumnIndex(colName));
|
||||
end;
|
||||
|
||||
function DataFrame.Bool(name: string): array of boolean;
|
||||
begin
|
||||
Result := GetBoolColumn(name);
|
||||
end;
|
||||
|
||||
function DataFrame.Bool(rowIndex, colIndex: integer): boolean;
|
||||
begin
|
||||
CheckRowIndex(rowIndex);
|
||||
CheckColumnIndex(colIndex);
|
||||
|
||||
var c := BoolColumn(columns[colIndex]);
|
||||
if not c.IsValid[rowIndex] then
|
||||
Error(ER_VALUE_IS_NA, fSchema.NameAt(colIndex));
|
||||
Result := c.Data[rowIndex];
|
||||
end;
|
||||
|
||||
function DataFrame.Bool(rowIndex: integer; colName: string): boolean;
|
||||
begin
|
||||
Result := Bool(rowIndex, ColumnIndex(colName));
|
||||
end;
|
||||
|
||||
function DataFrame.DateTime(name: string): array of System.DateTime;
|
||||
begin
|
||||
Result := GetDateTimeColumn(name);
|
||||
end;
|
||||
|
||||
function DataFrame.DateTime(rowIndex, colIndex: integer): System.DateTime;
|
||||
begin
|
||||
CheckRowIndex(rowIndex);
|
||||
CheckColumnIndex(colIndex);
|
||||
|
||||
var c := DateTimeColumn(columns[colIndex]);
|
||||
if not c.IsValid[rowIndex] then
|
||||
Error(ER_VALUE_IS_NA, fSchema.NameAt(colIndex));
|
||||
Result := c.Data[rowIndex];
|
||||
end;
|
||||
|
||||
function DataFrame.DateTime(rowIndex: integer; colName: string): System.DateTime;
|
||||
begin
|
||||
Result := DateTime(rowIndex, ColumnIndex(colName));
|
||||
end;
|
||||
|
||||
function DataFrame.TrainTestSplit(testRatio: real; shuffle: boolean; seed: integer): (DataFrame, DataFrame);
|
||||
begin
|
||||
if Self = nil then
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ type
|
|||
public
|
||||
/// Возвращает количество столбцов в схеме
|
||||
property ColumnCount: integer read fNames.Length;
|
||||
/// Возвращает копию массива имён столбцов
|
||||
/// Возвращает имена столбцов
|
||||
property ColumnNames: array of string read GetColumnNames;
|
||||
/// Возвращает копию массива типов столбцов
|
||||
property Types: array of ColumnType read GetTypes;
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ type
|
|||
/// Создаёт матрицу из двумерного массива вещественных значений.
|
||||
constructor Create(values: array[,] of real);
|
||||
|
||||
constructor Create(values: array of Vector);
|
||||
|
||||
/// Возвращает копию данных в виде двумерного массива.
|
||||
function ToArray2D: array[,] of real;
|
||||
/// Возвращает строку матрицы в виде массива.
|
||||
|
|
@ -225,6 +227,7 @@ type
|
|||
|
||||
|
||||
static function operator implicit(a: array [,] of real): Matrix := new Matrix(a);
|
||||
static function operator implicit(a: array of Vector): Matrix := new Matrix(a);
|
||||
static function operator implicit(a: array of array of real): Matrix := new Matrix(Matr(a));
|
||||
static function operator implicit(a: array of array of integer): Matrix := new Matrix(Matr(a.ConvertAll(x -> x.ConvertAll(y -> real(y)))));
|
||||
|
||||
|
|
@ -369,8 +372,14 @@ const
|
|||
'Длина вектора должна быть неотрицательной!!Vector length must be non-negative';
|
||||
ER_VALUES_NULL =
|
||||
'values не может быть nil!!values cannot be nil';
|
||||
ER_VALUES_EMPTY =
|
||||
'values не может быть пустым!!values cannot be empty';
|
||||
ER_VECTOR_LENGTH_MISMATCH =
|
||||
'Несоответствие длины векторов: {0} и {1}!!Vector length mismatch: {0} vs {1}';
|
||||
ER_VECTOR_NULL =
|
||||
'Вектор не может быть nil!!vector cannot be nil';
|
||||
ER_MATRIX_ROWS_HAVE_DIFFERENT_LENGTHS =
|
||||
'Строки матрицы имеют разную длину!!matrix rows have different lengths';
|
||||
ER_VECTOR_EMPTY =
|
||||
'Вектор пуст!!Vector is empty';
|
||||
ER_VECTOR_DIVIDE_BY_ZERO =
|
||||
|
|
@ -659,6 +668,38 @@ begin
|
|||
fdata := Copy(values);
|
||||
end;
|
||||
|
||||
constructor Matrix.Create(values: array of Vector);
|
||||
begin
|
||||
if values = nil then
|
||||
ArgumentNullError(ER_VALUES_NULL);
|
||||
|
||||
var r := values.Length;
|
||||
if r = 0 then
|
||||
ArgumentError(ER_VALUES_EMPTY);
|
||||
|
||||
if values[0] = nil then
|
||||
ArgumentNullError(ER_VECTOR_NULL);
|
||||
|
||||
var c := values[0].Length;
|
||||
if c = 0 then
|
||||
ArgumentError(ER_VECTOR_EMPTY);
|
||||
|
||||
for var i := 1 to r - 1 do
|
||||
begin
|
||||
if values[i] = nil then
|
||||
ArgumentNullError(ER_VECTOR_NULL);
|
||||
|
||||
if values[i].Length <> c then
|
||||
ArgumentError(ER_MATRIX_ROWS_HAVE_DIFFERENT_LENGTHS);
|
||||
end;
|
||||
|
||||
fdata := new real[r, c];
|
||||
|
||||
for var i := 0 to r - 1 do
|
||||
for var j := 0 to c - 1 do
|
||||
fdata[i, j] := values[i][j];
|
||||
end;
|
||||
|
||||
function Matrix.ToArray2D: array[,] of real;
|
||||
begin
|
||||
Result := Copy(fdata);
|
||||
|
|
@ -1094,7 +1135,6 @@ begin
|
|||
end;
|
||||
|
||||
|
||||
|
||||
static function Matrix.operator +(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckSameSize(A, B);
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ type
|
|||
CsvLoader = DataFrameABC.CsvLoader;
|
||||
JoinKind = DataFrameABC.JoinKind;
|
||||
GroupView = DataFrameABC.GroupView;
|
||||
EncodedTarget = DataAdapters.EncodedTarget;
|
||||
|
||||
IProbabilisticClassifier = MLCoreABC.IProbabilisticClassifier;
|
||||
IClassifier = MLCoreABC.IClassifier;
|
||||
|
|
@ -135,6 +136,8 @@ type
|
|||
ClassificationMatrixPipeline = MLModelsABC.ClassificationMatrixPipeline;
|
||||
RegressionMatrixPipeline = MLModelsABC.RegressionMatrixPipeline;
|
||||
ClusteringMatrixPipeline = MLModelsABC.ClusteringMatrixPipeline;
|
||||
LinearDecisionBoundary = MLModelsABC.LinearDecisionBoundary;
|
||||
LinearRegressionLine = MLModelsABC.LinearRegressionLine;
|
||||
|
||||
LinearRegression = MLModelsABC.LinearRegression;
|
||||
LogisticRegression = MLModelsABC.LogisticRegression;
|
||||
|
|
@ -143,12 +146,14 @@ type
|
|||
ElasticNet = MLModelsABC.ElasticNet;
|
||||
DecisionTreeClassifier = MLModelsABC.DecisionTreeClassifier;
|
||||
DecisionTreeRegressor = MLModelsABC.DecisionTreeRegressor;
|
||||
DecisionTreeView = MLModelsABC.DecisionTreeView;
|
||||
RandomForestRegressor = MLModelsABC.RandomForestRegressor;
|
||||
RandomForestClassifier = MLModelsABC.RandomForestClassifier;
|
||||
GradientBoostingRegressor = MLModelsABC.GradientBoostingRegressor;
|
||||
GradientBoostingClassifier = MLModelsABC.GradientBoostingClassifier;
|
||||
KNNClassifier = MLModelsABC.KNNClassifier;
|
||||
KNNRegressor = MLModelsABC.KNNRegressor;
|
||||
NeighborInfo = MLModelsABC.NeighborInfo;
|
||||
KMeans = MLModelsABC.KMeans;
|
||||
DBSCAN = MLModelsABC.DBSCAN;
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,10 @@ type
|
|||
/// и целевых метках классов y.
|
||||
function Fit(X: Matrix; y: array of integer): IClassifier;
|
||||
|
||||
/// Возвращает метку класса для одного объекта X
|
||||
/// в том же виде, в каком метки были поданы модели при обучении.
|
||||
function PredictOne(X: Vector): integer;
|
||||
|
||||
/// Возвращает метки классов в том же виде, в каком они были поданы модели при обучении.
|
||||
function Predict(X: Matrix): array of integer;
|
||||
|
||||
|
|
@ -131,6 +135,10 @@ type
|
|||
/// Позволяет получать значения в диапазоне (0, 1)
|
||||
/// вместо только итогового решения.
|
||||
IProbabilisticClassifier = interface(IClassifier)
|
||||
/// Возвращает вектор вероятностей классов для одного объекта X.
|
||||
/// Порядок элементов соответствует GetClassLabels.
|
||||
function PredictProbaOne(X: Vector): Vector;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
|
|
@ -148,6 +156,9 @@ type
|
|||
/// и векторе целевых значений y.
|
||||
function Fit(X: Matrix; y: Vector): IRegressor;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
function Predict(X: Matrix): Vector;
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -105,6 +105,24 @@ type
|
|||
function GetClassLabels: array of string;
|
||||
end;
|
||||
|
||||
LinearDecisionBoundary = record
|
||||
A, B, C: real;
|
||||
function Evaluate(x, y: real): real;
|
||||
function Y(x: real): real;
|
||||
function Side(x, y: real): integer;
|
||||
function Distance(x, y: real): real;
|
||||
function ToString: string; override;
|
||||
function Coefficients: (real,real,real) := (A,B,C);
|
||||
end;
|
||||
|
||||
LinearRegressionLine = record
|
||||
K, B: real;
|
||||
function Evaluate(x: real): real;
|
||||
function Y(x: real): real;
|
||||
function ToString: string; override;
|
||||
function Coefficients: (real,real) := (K,B);
|
||||
end;
|
||||
|
||||
/// Линейная регрессионная модель (метод наименьших квадратов).
|
||||
/// Предсказывает числовое значение по линейной комбинации признаков
|
||||
/// Используется в задачах регрессии при отсутствии выраженной
|
||||
|
|
@ -125,6 +143,9 @@ type
|
|||
/// • обученное состояние модели НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): IRegressor;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
/// Предсказывает значения для матрицы признаков
|
||||
/// Возвращает вектор длины m
|
||||
function Predict(X: Matrix): Vector;
|
||||
|
|
@ -143,6 +164,10 @@ type
|
|||
/// После вызова Fit значение становится True.
|
||||
/// Используется для проверки корректности вызова Predict.
|
||||
property IsFitted: boolean read fFitted;
|
||||
|
||||
/// Возвращает уравнение регрессионной прямой y = k*x + b
|
||||
/// для модели с одним признаком.
|
||||
function RegressionLine: LinearRegressionLine;
|
||||
|
||||
function ToString: string; override;
|
||||
|
||||
|
|
@ -179,6 +204,9 @@ type
|
|||
/// • обученное состояние модели НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): IRegressor;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
/// Предсказывает непрерывные значения для объектов X.
|
||||
/// Результат — вектор длины m.
|
||||
function Predict(X: Matrix): Vector;
|
||||
|
|
@ -248,6 +276,9 @@ type
|
|||
/// • обученное состояние модели НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): IRegressor;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
/// Предсказывает непрерывные значения для объектов X.
|
||||
/// Результат — вектор длины m.
|
||||
function Predict(X: Matrix): Vector;
|
||||
|
|
@ -307,6 +338,9 @@ type
|
|||
/// • обученное состояние модели НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): IRegressor;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
/// Предсказывает значения целевой переменной для входных данных.
|
||||
/// X — матрица признаков размера [nSamples x nFeatures].
|
||||
/// Возвращает вектор предсказаний длины nSamples.
|
||||
|
|
@ -383,7 +417,19 @@ type
|
|||
/// Примечание:
|
||||
/// • обученное состояние модели НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: array of integer): IClassifier;
|
||||
|
||||
/// Возвращает метку класса для одного объекта X
|
||||
/// в том же виде, в каком метки были поданы модели при обучении.
|
||||
function PredictOne(X: Vector): integer;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
/// - nClasses — число классов модели.
|
||||
/// Элемент [i, k] содержит вероятность того, что объект i принадлежит классу k.
|
||||
/// Сумма вероятностей в каждой строке равна 1.
|
||||
function PredictProbaOne(X: Vector): Vector;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
|
|
@ -416,6 +462,10 @@ type
|
|||
|
||||
function Name: string := Self.GetType.Name;
|
||||
|
||||
/// Возвращает коэффициенты разделяющей прямой A*x + B*y + C = 0
|
||||
/// для бинарной задачи с двумя признаками.
|
||||
function DecisionBoundary: LinearDecisionBoundary;
|
||||
|
||||
procedure SetClassLabels(classes: array of string);
|
||||
|
||||
function GetClassLabels: array of string;
|
||||
|
|
@ -437,6 +487,25 @@ type
|
|||
/// Создает глубокую копию узла вместе со всеми подузлами
|
||||
function Clone: DecisionTreeNode;
|
||||
end;
|
||||
|
||||
/// Представление решающего дерева с именами признаков и классов.
|
||||
/// Используется для печати и последующей визуализации.
|
||||
DecisionTreeView = class
|
||||
private
|
||||
fRoot: DecisionTreeNode;
|
||||
fFeatureNames: array of string;
|
||||
fClassNames: array of string;
|
||||
|
||||
public
|
||||
constructor Create(
|
||||
root: DecisionTreeNode;
|
||||
featureNames: array of string;
|
||||
classNames: array of string
|
||||
);
|
||||
|
||||
/// Возвращает текстовое представление дерева.
|
||||
function ToString: string; override;
|
||||
end;
|
||||
|
||||
/// Результат поиска лучшего разбиения узла дерева
|
||||
SplitResult = record
|
||||
|
|
@ -584,6 +653,7 @@ type
|
|||
property FeatureImportances: Vector read GetFeatureImportances;
|
||||
|
||||
function PredictOne(x: Vector; node: DecisionTreeNode): integer;
|
||||
function RootNode: DecisionTreeNode;
|
||||
function Clone: DecisionTreeCore;
|
||||
end;
|
||||
|
||||
|
|
@ -629,6 +699,10 @@ type
|
|||
/// Примечание:
|
||||
/// • обученное состояние дерева НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: array of integer): IClassifier;
|
||||
|
||||
/// Возвращает метку класса для одного объекта X
|
||||
/// в том же виде, в каком метки были поданы модели при обучении.
|
||||
function PredictOne(X: Vector): integer;
|
||||
|
||||
/// Возвращает метки классов для объектов из X
|
||||
/// в том же виде, в каком они были поданы модели при обучении.
|
||||
|
|
@ -651,6 +725,11 @@ type
|
|||
procedure SetClassLabels(classes: array of string);
|
||||
|
||||
function GetClassLabels: array of string;
|
||||
|
||||
function Tree(
|
||||
featureNames: array of string;
|
||||
classNames: array of string
|
||||
): DecisionTreeView;
|
||||
|
||||
/// Возвращает true, если дерево обучено.
|
||||
/// Если false — Predict вызовет ошибку.
|
||||
|
|
@ -795,6 +874,9 @@ type
|
|||
/// • обученное состояние дерева НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): IRegressor; override;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
/// Выполняет предсказание для всех объектов X.
|
||||
/// Возвращает вектор вещественных значений.
|
||||
function Predict(X: Matrix): Vector; override;
|
||||
|
|
@ -952,6 +1034,9 @@ type
|
|||
/// • обученное состояние ансамбля НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): IRegressor;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
/// Выполняет предсказание для X.
|
||||
/// Итоговое значение — среднее предсказаний всех деревьев ансамбля.
|
||||
function Predict(X: Matrix): Vector;
|
||||
|
|
@ -1005,6 +1090,10 @@ type
|
|||
/// • обученное состояние ансамбля НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: array of integer): IClassifier;
|
||||
|
||||
/// Возвращает метку класса для одного объекта X
|
||||
/// в том же виде, в каком метки были поданы модели при обучении.
|
||||
function PredictOne(X: Vector): integer;
|
||||
|
||||
/// Возвращает метки классов для объектов из X
|
||||
/// в том же виде, в каком они были поданы модели при обучении.
|
||||
/// Для каждого объекта агрегируются предсказания всех деревьев.
|
||||
|
|
@ -1016,6 +1105,14 @@ type
|
|||
/// Требует предварительного вызова Fit.
|
||||
function PredictLabels(X: Matrix): array of string;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
/// - nClasses — число классов модели.
|
||||
/// Элемент [i, k] содержит вероятность того, что объект i принадлежит классу k.
|
||||
/// Сумма вероятностей в каждой строке равна 1.
|
||||
function PredictProbaOne(X: Vector): Vector;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
|
|
@ -1175,6 +1272,9 @@ type
|
|||
/// Примечание:
|
||||
/// • обученное состояние модели НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): IRegressor;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
/// Предсказывает значения целевой переменной.
|
||||
/// Используются все обученные деревья.
|
||||
|
|
@ -1325,6 +1425,10 @@ type
|
|||
/// Примечание:
|
||||
/// • обученное состояние модели НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: array of integer): IClassifier;
|
||||
|
||||
/// Возвращает метку класса для одного объекта X
|
||||
/// в том же виде, в каком метки были поданы модели при обучении.
|
||||
function PredictOne(X: Vector): integer;
|
||||
|
||||
/// Обучает модель градиентного бустинга с использованием валидационной выборки.
|
||||
/// Поддерживает early stopping:
|
||||
|
|
@ -1339,6 +1443,14 @@ type
|
|||
/// Возвращает исходные строковые метки классов для объектов из X.
|
||||
function PredictLabels(X: Matrix): array of string;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
/// - nClasses — число классов модели.
|
||||
/// Элемент [i, k] содержит вероятность того, что объект i принадлежит классу k.
|
||||
/// Сумма вероятностей в каждой строке равна 1.
|
||||
function PredictProbaOne(X: Vector): Vector;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
|
|
@ -1399,6 +1511,11 @@ type
|
|||
dist: double;
|
||||
idx: integer;
|
||||
end;
|
||||
|
||||
NeighborInfo = record
|
||||
Index: integer;
|
||||
Distance: real;
|
||||
end;
|
||||
|
||||
/// Режим взвешивания в алгоритме k ближайших соседей.
|
||||
/// Uniform — равномерное голосование/усреднение.
|
||||
|
|
@ -1422,6 +1539,7 @@ type
|
|||
|
||||
// ==== common methods ====
|
||||
procedure ValidatePredictInput(X: Matrix);
|
||||
procedure FillNeighborDistances(x: Vector);
|
||||
|
||||
function SquaredL2(trainRow: integer; XTest: Matrix; testRow: integer): double;
|
||||
|
||||
|
|
@ -1433,6 +1551,11 @@ type
|
|||
/// k — число ближайших соседей (k > 0).
|
||||
/// weighting — режим взвешивания соседей
|
||||
constructor Create(k: integer; weighting: KNNWeighting := KNNWeighting.Uniform);
|
||||
|
||||
/// Возвращает k ближайших обучающих объектов
|
||||
/// для одного объекта X.
|
||||
/// Индекс относится к обучающей выборке, переданной в Fit.
|
||||
function GetNearestNeighbors(X: Vector): array of NeighborInfo;
|
||||
|
||||
/// Обучает модель k ближайших соседей.
|
||||
/// X — матрица m × n (m объектов, n признаков).
|
||||
|
|
@ -1482,6 +1605,10 @@ type
|
|||
/// Примечание:
|
||||
/// • обученное состояние модели (обучающая выборка) НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: array of integer): IClassifier;
|
||||
|
||||
/// Возвращает метку класса для одного объекта X
|
||||
/// в том же виде, в каком метки были поданы модели при обучении.
|
||||
function PredictOne(X: Vector): integer;
|
||||
|
||||
/// Возвращает метки классов для объектов из X
|
||||
/// в том же виде, в каком они были поданы модели при обучении.
|
||||
|
|
@ -1494,6 +1621,14 @@ type
|
|||
/// для одного экземпляра модели.
|
||||
function PredictLabels(X: Matrix): array of string;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
/// - nClasses — число классов модели.
|
||||
/// Элемент [i, k] содержит вероятность того, что объект i принадлежит классу k.
|
||||
/// Сумма вероятностей в каждой строке равна 1.
|
||||
function PredictProbaOne(X: Vector): Vector;
|
||||
|
||||
/// Возвращает матрицу вероятностей классов для всех объектов из X.
|
||||
/// Размер результата: nSamples × nClasses, где:
|
||||
/// - nSamples — число объектов в X;
|
||||
|
|
@ -1536,6 +1671,9 @@ type
|
|||
/// Примечание:
|
||||
/// • обученное состояние модели (обучающая выборка) НЕ копируется методом Clone
|
||||
function Fit(X: Matrix; y: Vector): IRegressor;
|
||||
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
|
||||
/// Выполняет предсказание числовых значений для объектов X.
|
||||
/// Возвращает вектор предсказанных значений
|
||||
|
|
@ -1840,11 +1978,15 @@ type
|
|||
ClassificationMatrixPipeline = class(MatrixPipeline, IProbabilisticClassifier, IClassifierInternal)
|
||||
public
|
||||
function Fit(X: Matrix; y: array of integer): IClassifier;
|
||||
/// Возвращает метку класса для одного объекта X
|
||||
/// в том же виде, в каком метки были поданы модели при обучении.
|
||||
function PredictOne(X: Vector): integer;
|
||||
/// Возвращает метки классов для объектов из X
|
||||
/// в том же виде, в каком они были поданы модели при обучении.
|
||||
function Predict(X: Matrix): array of integer; reintroduce;
|
||||
/// Возвращает строковые метки классов для объектов из X.
|
||||
function PredictLabels(X: Matrix): array of string;
|
||||
function PredictProbaOne(X: Vector): Vector;
|
||||
function PredictProba(X: Matrix): Matrix; reintroduce;
|
||||
procedure SetClassLabels(classes: array of string);
|
||||
function GetClassLabels: array of string;
|
||||
|
|
@ -1853,6 +1995,8 @@ type
|
|||
RegressionMatrixPipeline = class(MatrixPipeline, IRegressor)
|
||||
public
|
||||
function Fit(X: Matrix; y: Vector): IRegressor; reintroduce;
|
||||
/// Возвращает числовое предсказание для одного объекта X.
|
||||
function PredictOne(X: Vector): real;
|
||||
end;
|
||||
|
||||
/// Общая база для матричных конвейеров без учителя.
|
||||
|
|
@ -2413,6 +2557,18 @@ const
|
|||
ER_LOGISTIC_NEED_AT_LEAST_TWO_CLASSES =
|
||||
'Для LogisticRegression нужно минимум 2 класса.!!' +
|
||||
'LogisticRegression requires at least 2 classes.';
|
||||
ER_LOGISTIC_BOUNDARY_NEEDS_BINARY =
|
||||
'DecisionBoundary доступен только для бинарной LogisticRegression.!!' +
|
||||
'DecisionBoundary is available only for binary LogisticRegression.';
|
||||
ER_LOGISTIC_BOUNDARY_NEEDS_2D =
|
||||
'DecisionBoundary требует ровно 2 признака.!!' +
|
||||
'DecisionBoundary requires exactly 2 features.';
|
||||
ER_LINEAR_REGRESSION_LINE_NEEDS_1D =
|
||||
'RegressionLine требует ровно 1 признак.!!' +
|
||||
'RegressionLine requires exactly 1 feature.';
|
||||
ER_DECISION_BOUNDARY_DEGENERATE =
|
||||
'Разделяющая прямая вырождена: A и B равны нулю.!!' +
|
||||
'Decision boundary is degenerate: A and B are zero.';
|
||||
ER_MIN_SAMPLES_SPLIT_INVALID =
|
||||
'minSamplesSplit должно быть >= 2 ({0}).!!' +
|
||||
'minSamplesSplit must be >= 2 ({0}).';
|
||||
|
|
@ -2481,6 +2637,17 @@ const
|
|||
// Проверка на NuN/Inf
|
||||
//-----------------------------
|
||||
|
||||
function SingleRowMatrix(X: Vector): Matrix;
|
||||
begin
|
||||
if X = nil then
|
||||
ArgumentNullError(ER_X_NULL);
|
||||
|
||||
Result := new Matrix(1, X.Length);
|
||||
|
||||
for var j := 0 to X.Length - 1 do
|
||||
Result[0, j] := X[j];
|
||||
end;
|
||||
|
||||
procedure ClassifierLabelHelper.SetClassValues(values: array of integer);
|
||||
begin
|
||||
fClassValues := Copy(values);
|
||||
|
|
@ -2685,6 +2852,11 @@ begin
|
|||
Result := X * fcoef + fIntercept;
|
||||
end;
|
||||
|
||||
function LinearRegression.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function LinearRegression.ToString: string;
|
||||
begin
|
||||
Result := 'LinearRegression'
|
||||
|
|
@ -2811,6 +2983,11 @@ begin
|
|||
Result[i] += fIntercept;
|
||||
end;
|
||||
|
||||
function RidgeRegression.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function RidgeRegression.ToString: string;
|
||||
begin
|
||||
Result := 'RidgeRegression(lambda=' + fLambda + ')';
|
||||
|
|
@ -2994,6 +3171,11 @@ begin
|
|||
Result[i] += fIntercept;
|
||||
end;
|
||||
|
||||
function ElasticNet.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function ElasticNet.ToString: string;
|
||||
begin
|
||||
Result :=
|
||||
|
|
@ -3033,6 +3215,11 @@ begin
|
|||
Result := fModel.Predict(X);
|
||||
end;
|
||||
|
||||
function LassoRegression.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := fModel.PredictOne(X);
|
||||
end;
|
||||
|
||||
function LassoRegression.Clone: IModel;
|
||||
begin
|
||||
Result := new LassoRegression(fAlpha, fMaxIter, fTol);
|
||||
|
|
@ -3520,6 +3707,11 @@ begin
|
|||
Result := Z;
|
||||
end;
|
||||
|
||||
function LogisticRegression.PredictProbaOne(X: Vector): Vector;
|
||||
begin
|
||||
Result := PredictProba(SingleRowMatrix(X)).GetRow(0);
|
||||
end;
|
||||
|
||||
function LogisticRegression.Predict(X: Matrix): array of integer;
|
||||
begin
|
||||
if not fFitted then
|
||||
|
|
@ -3558,6 +3750,11 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function LogisticRegression.PredictOne(X: Vector): integer;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function LogisticRegression.PredictLabels(X: Matrix): array of string;
|
||||
begin
|
||||
var pred := Predict(X);
|
||||
|
|
@ -3621,6 +3818,138 @@ begin
|
|||
Result := fIntercept;
|
||||
end;
|
||||
|
||||
function LinearDecisionBoundary.Evaluate(x, y: real): real;
|
||||
begin
|
||||
Result := A * x + B * y + C;
|
||||
end;
|
||||
|
||||
function LinearDecisionBoundary.Y(x: real): real;
|
||||
begin
|
||||
if Abs(B) < 1e-12 then
|
||||
Result := real.PositiveInfinity
|
||||
else
|
||||
Result := -(A * x + C) / B;
|
||||
end;
|
||||
|
||||
function LinearDecisionBoundary.Side(x, y: real): integer;
|
||||
begin
|
||||
var v := Evaluate(x, y);
|
||||
if Abs(v) < 1e-12 then
|
||||
Result := 0
|
||||
else if v < 0 then
|
||||
Result := -1
|
||||
else
|
||||
Result := 1;
|
||||
end;
|
||||
|
||||
function LinearDecisionBoundary.Distance(x, y: real): real;
|
||||
begin
|
||||
var denom := Sqrt(A * A + B * B);
|
||||
if denom < 1e-12 then
|
||||
Error(ER_DECISION_BOUNDARY_DEGENERATE);
|
||||
Result := Abs(Evaluate(x, y)) / denom;
|
||||
end;
|
||||
|
||||
function LinearDecisionBoundary.ToString: string;
|
||||
function FormatCoeff(v: real): string;
|
||||
begin
|
||||
if Abs(v - Round(v)) < 1e-12 then
|
||||
Result := Round(v).ToString
|
||||
else
|
||||
Result := v.ToString('0.##');
|
||||
end;
|
||||
function FmtSigned(v: real; suffix: string): string;
|
||||
begin
|
||||
if Abs(v) < 1e-12 then
|
||||
exit('');
|
||||
|
||||
var mag := FormatCoeff(Abs(v));
|
||||
if suffix <> '' then
|
||||
mag += '*' + suffix;
|
||||
|
||||
if v < 0 then
|
||||
Result := ' - ' + mag
|
||||
else
|
||||
Result := ' + ' + mag;
|
||||
end;
|
||||
begin
|
||||
Result := '';
|
||||
|
||||
if Abs(A) >= 1e-12 then
|
||||
Result := FormatCoeff(A) + '*x';
|
||||
|
||||
Result += FmtSigned(B, 'y');
|
||||
Result += FmtSigned(C, '');
|
||||
|
||||
if Result = '' then
|
||||
Result := '0';
|
||||
|
||||
if Result.StartsWith(' + ') then
|
||||
Result := Result.Substring(3);
|
||||
if Result.StartsWith(' - ') then
|
||||
Result := '-' + Result.Substring(3);
|
||||
|
||||
Result += ' = 0';
|
||||
end;
|
||||
|
||||
function LinearRegressionLine.Evaluate(x: real): real;
|
||||
begin
|
||||
Result := K * x + B;
|
||||
end;
|
||||
|
||||
function LinearRegressionLine.Y(x: real): real;
|
||||
begin
|
||||
Result := Evaluate(x);
|
||||
end;
|
||||
|
||||
function LinearRegressionLine.ToString: string;
|
||||
function FormatCoeff(v: real): string;
|
||||
begin
|
||||
if Abs(v - Round(v)) < 1e-12 then
|
||||
Result := Round(v).ToString
|
||||
else
|
||||
Result := v.ToString('0.##');
|
||||
end;
|
||||
begin
|
||||
Result := FormatCoeff(K) + '*x';
|
||||
|
||||
if B > 1e-12 then
|
||||
Result += ' + ' + FormatCoeff(B)
|
||||
else if B < -1e-12 then
|
||||
Result += ' - ' + FormatCoeff(Abs(B));
|
||||
end;
|
||||
|
||||
function LinearRegression.RegressionLine: LinearRegressionLine;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
if (fCoef = nil) or (fCoef.Length <> 1) then
|
||||
ArgumentError(ER_LINEAR_REGRESSION_LINE_NEEDS_1D);
|
||||
|
||||
Result.K := fCoef[0];
|
||||
Result.B := fIntercept;
|
||||
end;
|
||||
|
||||
function LogisticRegression.DecisionBoundary: LinearDecisionBoundary;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
if fClassCount <> 2 then
|
||||
ArgumentError(ER_LOGISTIC_BOUNDARY_NEEDS_BINARY);
|
||||
|
||||
if fW.RowCount <> 2 then
|
||||
ArgumentError(ER_LOGISTIC_BOUNDARY_NEEDS_2D);
|
||||
|
||||
Result.A := fW[0,0] - fW[0,1];
|
||||
Result.B := fW[1,0] - fW[1,1];
|
||||
Result.C := fIntercept[0] - fIntercept[1];
|
||||
|
||||
if (Abs(Result.A) < 1e-12) and (Abs(Result.B) < 1e-12) then
|
||||
Error(ER_DECISION_BOUNDARY_DEGENERATE);
|
||||
end;
|
||||
|
||||
function GiniCriterion.Impurity(y: Vector; indices: array of integer): real;
|
||||
begin
|
||||
var n := indices.Length;
|
||||
|
|
@ -3756,6 +4085,116 @@ begin
|
|||
Result := n;
|
||||
end;
|
||||
|
||||
constructor DecisionTreeView.Create(
|
||||
root: DecisionTreeNode;
|
||||
featureNames: array of string;
|
||||
classNames: array of string
|
||||
);
|
||||
begin
|
||||
if root <> nil then
|
||||
fRoot := root.Clone;
|
||||
fFeatureNames := Copy(featureNames);
|
||||
fClassNames := Copy(classNames);
|
||||
end;
|
||||
|
||||
function DecisionTreeView.ToString: string;
|
||||
function FormatReal(v: real): string;
|
||||
begin
|
||||
if Abs(v - Round(v)) < 1e-12 then
|
||||
Result := Round(v).ToString
|
||||
else
|
||||
Result := v.ToString('0.##');
|
||||
end;
|
||||
|
||||
function FeatureNameOf(idx: integer): string;
|
||||
begin
|
||||
if (idx >= 0) and (idx < fFeatureNames.Length) and (fFeatureNames[idx] <> nil) and (fFeatureNames[idx] <> '') then
|
||||
Result := fFeatureNames[idx]
|
||||
else
|
||||
Result := 'x' + idx;
|
||||
end;
|
||||
|
||||
function ClassNameOf(idx: integer): string;
|
||||
begin
|
||||
if (idx >= 0) and (idx < fClassNames.Length) then
|
||||
Result := fClassNames[idx]
|
||||
else
|
||||
Result := idx.ToString;
|
||||
end;
|
||||
|
||||
function LeafText(node: DecisionTreeNode): string;
|
||||
begin
|
||||
if node = nil then
|
||||
exit('nil');
|
||||
|
||||
if fClassNames <> nil then
|
||||
Result := ClassNameOf(Round(node.LeafValue))
|
||||
else
|
||||
Result := FormatReal(node.LeafValue);
|
||||
end;
|
||||
|
||||
procedure AppendBranch(sb: System.Text.StringBuilder; node: DecisionTreeNode;
|
||||
prefix: string; isLast: boolean; branchMark: string);
|
||||
begin
|
||||
sb.Append(prefix);
|
||||
|
||||
if isLast then
|
||||
sb.Append('└── ')
|
||||
else
|
||||
sb.Append('├── ');
|
||||
|
||||
if node = nil then
|
||||
begin
|
||||
sb.Append(branchMark + ' → nil');
|
||||
sb.AppendLine;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if node.IsLeaf then
|
||||
begin
|
||||
sb.Append(branchMark + ' → ' + LeafText(node));
|
||||
sb.AppendLine;
|
||||
exit;
|
||||
end;
|
||||
|
||||
sb.Append(branchMark + ' ');
|
||||
sb.AppendLine;
|
||||
|
||||
var childPrefix := prefix;
|
||||
if isLast then
|
||||
childPrefix += ' '
|
||||
else
|
||||
childPrefix += '│ ';
|
||||
|
||||
sb.Append(childPrefix);
|
||||
sb.Append(FeatureNameOf(node.FeatureIndex));
|
||||
sb.Append(' ≤ ');
|
||||
sb.Append(FormatReal(node.Threshold));
|
||||
sb.Append('?');
|
||||
sb.AppendLine;
|
||||
|
||||
AppendBranch(sb, node.Left, childPrefix, false, '✓');
|
||||
AppendBranch(sb, node.Right, childPrefix, true, '✗');
|
||||
end;
|
||||
begin
|
||||
if fRoot = nil then
|
||||
exit('DecisionTreeView(empty)');
|
||||
|
||||
if fRoot.IsLeaf then
|
||||
exit('→ ' + LeafText(fRoot));
|
||||
|
||||
var sb := new System.Text.StringBuilder;
|
||||
sb.Append(FeatureNameOf(fRoot.FeatureIndex));
|
||||
sb.Append(' ≤ ');
|
||||
sb.Append(FormatReal(fRoot.Threshold));
|
||||
sb.Append('?');
|
||||
sb.AppendLine;
|
||||
|
||||
AppendBranch(sb, fRoot.Left, '', false, '✓');
|
||||
AppendBranch(sb, fRoot.Right, '', true, '✗');
|
||||
Result := sb.ToString.TrimEnd;
|
||||
end;
|
||||
|
||||
const MAX_ALLOWED_TREE_DEPTH = 1000;
|
||||
|
||||
// DecisionTreeCore
|
||||
|
|
@ -3852,6 +4291,13 @@ begin
|
|||
Result := PredictOne(x, node.Right);
|
||||
end;
|
||||
|
||||
function DecisionTreeCore.RootNode: DecisionTreeNode;
|
||||
begin
|
||||
if fRoot = nil then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
Result := fRoot.Clone;
|
||||
end;
|
||||
|
||||
function DecisionTreeCore.MajorityClass(y: Vector; indices: array of integer): integer;
|
||||
begin
|
||||
var counts := new integer[fClassCount];
|
||||
|
|
@ -4816,6 +5262,11 @@ begin
|
|||
Result := fLabels.DecodePredictions(fCore.Predict(X));
|
||||
end;
|
||||
|
||||
function DecisionTreeClassifier.PredictOne(X: Vector): integer;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function DecisionTreeClassifier.PredictLabels(X: Matrix): array of string;
|
||||
begin
|
||||
if fLabels = nil then
|
||||
|
|
@ -4841,6 +5292,21 @@ begin
|
|||
Result := fLabels.GetClassLabels;
|
||||
end;
|
||||
|
||||
function DecisionTreeClassifier.Tree(
|
||||
featureNames: array of string;
|
||||
classNames: array of string
|
||||
): DecisionTreeView;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
Result := new DecisionTreeView(
|
||||
fCore.RootNode,
|
||||
featureNames,
|
||||
classNames
|
||||
);
|
||||
end;
|
||||
|
||||
function DecisionTreeClassifier.Clone: IModel;
|
||||
begin
|
||||
Result := new DecisionTreeClassifier(
|
||||
|
|
@ -5017,6 +5483,11 @@ begin
|
|||
Result[i] := PredictOne(X, i);
|
||||
end;
|
||||
|
||||
function DecisionTreeRegressor.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function DecisionTreeRegressor.Clone: IModel;
|
||||
begin
|
||||
Result := new DecisionTreeRegressor(
|
||||
|
|
@ -5278,6 +5749,11 @@ begin
|
|||
Result := resultVec;
|
||||
end;
|
||||
|
||||
function RandomForestRegressor.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function RandomForestRegressor.Clone: IModel;
|
||||
begin
|
||||
Result := new RandomForestRegressor(
|
||||
|
|
@ -5593,6 +6069,11 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function RandomForestClassifier.PredictProbaOne(X: Vector): Vector;
|
||||
begin
|
||||
Result := PredictProba(SingleRowMatrix(X)).GetRow(0);
|
||||
end;
|
||||
|
||||
function RandomForestClassifier.Clone: IModel;
|
||||
begin
|
||||
Result := new RandomForestClassifier(
|
||||
|
|
@ -6239,6 +6720,11 @@ begin
|
|||
Result := yPred;
|
||||
end;
|
||||
|
||||
function GradientBoostingRegressor.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function GradientBoostingRegressor.PredictStage(X: Matrix; m: integer): Vector;
|
||||
begin
|
||||
if not fFitted then
|
||||
|
|
@ -7062,6 +7548,11 @@ begin
|
|||
Result := probs;
|
||||
end;
|
||||
|
||||
function GradientBoostingClassifier.PredictProbaOne(X: Vector): Vector;
|
||||
begin
|
||||
Result := PredictProba(SingleRowMatrix(X)).GetRow(0);
|
||||
end;
|
||||
|
||||
function GradientBoostingClassifier.PredictStageProba(
|
||||
X: Matrix; m: integer): Matrix;
|
||||
begin
|
||||
|
|
@ -7231,6 +7722,11 @@ begin
|
|||
Result[i] := fLabels.ClassValueAt(probs.RowArgMax(i));
|
||||
end;
|
||||
|
||||
function GradientBoostingClassifier.PredictOne(X: Vector): integer;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function GradientBoostingClassifier.PredictLabels(X: Matrix): array of string;
|
||||
begin
|
||||
var pred := Predict(X);
|
||||
|
|
@ -7271,6 +7767,25 @@ begin
|
|||
fFitted := False;
|
||||
end;
|
||||
|
||||
function KNNBase.GetNearestNeighbors(X: Vector): array of NeighborInfo;
|
||||
begin
|
||||
FillNeighborDistances(X);
|
||||
QuickSelect(fK - 1);
|
||||
|
||||
var resList := new List<NeighborInfo>;
|
||||
|
||||
for var i := 0 to fK - 1 do
|
||||
begin
|
||||
var item: NeighborInfo;
|
||||
item.Index := fNeighbors[i].idx;
|
||||
item.Distance := Sqrt(fNeighbors[i].dist);
|
||||
resList.Add(item);
|
||||
end;
|
||||
|
||||
resList.Sort((a, b) -> a.Distance.CompareTo(b.Distance));
|
||||
Result := resList.ToArray;
|
||||
end;
|
||||
|
||||
procedure KNNBase.ValidatePredictInput(X: Matrix);
|
||||
begin
|
||||
if X = nil then
|
||||
|
|
@ -7283,6 +7798,37 @@ begin
|
|||
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fXTrain.ColCount);
|
||||
end;
|
||||
|
||||
procedure KNNBase.FillNeighborDistances(x: Vector);
|
||||
begin
|
||||
if x = nil then
|
||||
ArgumentNullError(ER_X_NULL);
|
||||
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
if x.Length <> fXTrain.ColCount then
|
||||
DimensionError(ER_FEATURE_COUNT_MISMATCH, x.Length, fXTrain.ColCount);
|
||||
|
||||
var n := fXTrain.RowCount;
|
||||
var p := fXTrain.ColCount;
|
||||
var trainRows := fXTrain.Data.Rows;
|
||||
|
||||
for var t := 0 to n - 1 do
|
||||
begin
|
||||
var rowTrain := trainRows[t];
|
||||
var sum := 0.0;
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var diff := rowTrain[j] - x[j];
|
||||
sum += diff * diff;
|
||||
end;
|
||||
|
||||
fNeighbors[t].dist := sum;
|
||||
fNeighbors[t].idx := t;
|
||||
end;
|
||||
end;
|
||||
|
||||
function KNNBase.SquaredL2(trainRow: integer; XTest: Matrix; testRow: integer): double;
|
||||
begin
|
||||
var sum := 0.0;
|
||||
|
|
@ -7307,16 +7853,14 @@ begin
|
|||
var left := 0;
|
||||
var right := fNeighbors.Length - 1;
|
||||
|
||||
while true do
|
||||
while left < right do
|
||||
begin
|
||||
var pivotIndex := Partition(left, right);
|
||||
var p := Partition(left, right);
|
||||
|
||||
if pivotIndex = k then
|
||||
exit
|
||||
else if pivotIndex > k then
|
||||
right := pivotIndex - 1
|
||||
if k <= p then
|
||||
right := p
|
||||
else
|
||||
left := pivotIndex + 1;
|
||||
left := p + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -7656,6 +8200,16 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function KNNClassifier.PredictOne(X: Vector): integer;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function RandomForestClassifier.PredictOne(X: Vector): integer;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function KNNClassifier.PredictLabels(X: Matrix): array of string;
|
||||
begin
|
||||
var pred := Predict(X);
|
||||
|
|
@ -7808,6 +8362,11 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function KNNClassifier.PredictProbaOne(X: Vector): Vector;
|
||||
begin
|
||||
Result := PredictProba(SingleRowMatrix(X)).GetRow(0);
|
||||
end;
|
||||
|
||||
procedure KNNClassifier.SetClassLabels(classes: array of string);
|
||||
begin
|
||||
if fLabels = nil then
|
||||
|
|
@ -7959,6 +8518,11 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function KNNRegressor.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function KNNRegressor.Clone: IModel;
|
||||
begin
|
||||
Result := new KNNRegressor(fK, fWeighting);
|
||||
|
|
@ -8722,6 +9286,11 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function RegressionMatrixPipeline.PredictOne(X: Vector): real;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function ClassificationMatrixPipeline.Predict(X: Matrix): array of integer;
|
||||
begin
|
||||
if not fFitted then
|
||||
|
|
@ -8737,6 +9306,11 @@ begin
|
|||
Result := (fModel as IClassifier).Predict(Xt);
|
||||
end;
|
||||
|
||||
function ClassificationMatrixPipeline.PredictOne(X: Vector): integer;
|
||||
begin
|
||||
Result := Predict(SingleRowMatrix(X))[0];
|
||||
end;
|
||||
|
||||
function ClassificationMatrixPipeline.PredictLabels(X: Matrix): array of string;
|
||||
begin
|
||||
if not fFitted then
|
||||
|
|
@ -8767,6 +9341,11 @@ begin
|
|||
Result := (fModel as IProbabilisticClassifier).PredictProba(Xt);
|
||||
end;
|
||||
|
||||
function ClassificationMatrixPipeline.PredictProbaOne(X: Vector): Vector;
|
||||
begin
|
||||
Result := PredictProba(SingleRowMatrix(X)).GetRow(0);
|
||||
end;
|
||||
|
||||
procedure ClassificationMatrixPipeline.SetClassLabels(classes: array of string);
|
||||
begin
|
||||
if (fModel = nil) or not (fModel is IClassifierInternal) then
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ type
|
|||
/// Доля правильных предсказаний.
|
||||
/// Показывает, какая часть объектов классифицирована верно.
|
||||
/// Удобна, когда классы примерно сбалансированы.
|
||||
/// Пример: Accuracy = 0.9 означает 90% правильных ответов.
|
||||
/// Пример: Accuracy = 0.9 означает 90% правильных ответов
|
||||
static function Accuracy(yTrue, yPred: array of integer): real;
|
||||
|
||||
/// Точность (Precision) для положительного класса (1).
|
||||
|
|
@ -228,6 +228,7 @@ type
|
|||
fClassCount: integer;
|
||||
|
||||
fLabels: array of integer;
|
||||
fClassNames: array of string;
|
||||
fLabelToIndex: Dictionary<integer, integer>;
|
||||
|
||||
procedure EnsureBinary;
|
||||
|
|
@ -254,6 +255,10 @@ type
|
|||
///
|
||||
/// Для бинарной классификации дополнительно доступны значения TP, TN, FP и FN
|
||||
constructor Create(yTrue, yPred: array of integer);
|
||||
|
||||
/// Создаёт матрицу ошибок и сохраняет отображаемые имена классов.
|
||||
/// classNames[i] соответствует классу fLabels[i].
|
||||
constructor Create(yTrue, yPred: array of integer; classNames: array of string);
|
||||
|
||||
/// Возвращает исходную метку класса по его внутреннему номеру.
|
||||
///
|
||||
|
|
@ -263,6 +268,10 @@ type
|
|||
/// Метод позволяет узнать, какой исходной метке соответствует
|
||||
/// данный внутренний номер.
|
||||
function GetLabel(c: integer): integer;
|
||||
|
||||
/// Возвращает отображаемое имя класса по его внутреннему номеру.
|
||||
/// Если строковые имена не заданы, возвращает исходную числовую метку как строку.
|
||||
function GetClassDisplayName(i: integer): string;
|
||||
|
||||
/// Возвращает внутренний номер класса по его исходной метке.
|
||||
///
|
||||
|
|
@ -400,6 +409,12 @@ type
|
|||
/// Применима для многоклассовой классификации.
|
||||
function F1ForClass(c: integer): real;
|
||||
|
||||
/// Возвращает текстовое представление матрицы ошибок.
|
||||
function ToString: string; override;
|
||||
|
||||
/// Печатает матрицу ошибок.
|
||||
procedure Println;
|
||||
|
||||
end;
|
||||
|
||||
/// Метрики для оценки качества классификационных моделей.
|
||||
|
|
@ -616,7 +631,9 @@ const
|
|||
ER_INVALID_PROBABILITY =
|
||||
'Вероятность вне диапазона [0,1]: {0}!!Probability out of range [0,1]: {0}';
|
||||
ER_ARG_OUT_OF_RANGE =
|
||||
'Аргумент {0} имеет недопустимое значение: {1}!!Argument {0} is out of range: {1}';
|
||||
'Аргумент {0} имеет недопустимое значение: {1}!!Argument {0} is out of range: {1}';
|
||||
ER_CLASS_NAMES_LENGTH_MISMATCH =
|
||||
'Число имён классов ({0}) не совпадает с числом классов ({1})!!Class names count ({0}) does not match class count ({1})';
|
||||
|
||||
//-----------------------------
|
||||
// Metrics
|
||||
|
|
@ -1718,7 +1735,7 @@ begin
|
|||
// --- собираем строки классов
|
||||
for var c := 0 to cm.ClassCount - 1 do
|
||||
begin
|
||||
var cls := cm.GetLabel(c).ToString;
|
||||
var cls := cm.GetClassDisplayName(c);
|
||||
|
||||
var p := cm.PrecisionForClass(c);
|
||||
var r := cm.RecallForClass(c);
|
||||
|
|
@ -1875,6 +1892,19 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
constructor ConfusionMatrix.Create(yTrue, yPred: array of integer; classNames: array of string);
|
||||
begin
|
||||
Create(yTrue, yPred);
|
||||
|
||||
if classNames = nil then
|
||||
exit;
|
||||
|
||||
if classNames.Length <> fClassCount then
|
||||
ArgumentError(ER_CLASS_NAMES_LENGTH_MISMATCH, classNames.Length, fClassCount);
|
||||
|
||||
fClassNames := Copy(classNames);
|
||||
end;
|
||||
|
||||
function ConfusionMatrix.GetMatrix: array[,] of integer;
|
||||
begin
|
||||
Result := new integer[fClassCount, fClassCount];
|
||||
|
|
@ -1892,6 +1922,16 @@ begin
|
|||
Result := fLabels[c];
|
||||
end;
|
||||
|
||||
function ConfusionMatrix.GetClassDisplayName(i: integer): string;
|
||||
begin
|
||||
EnsureClassIndex(i);
|
||||
|
||||
if (fClassNames <> nil) and (i < fClassNames.Length) then
|
||||
Result := fClassNames[i]
|
||||
else
|
||||
Result := fLabels[i].ToString;
|
||||
end;
|
||||
|
||||
function ConfusionMatrix.GetIndex(labl: integer): integer;
|
||||
begin
|
||||
if labl not in fLabelToIndex then
|
||||
|
|
@ -2074,4 +2114,78 @@ begin
|
|||
Result := 2 * p * r / (p + r);
|
||||
end;
|
||||
|
||||
function ConfusionMatrix.ToString: string;
|
||||
function Center(s: string; w: integer): string;
|
||||
begin
|
||||
if s.Length >= w then
|
||||
exit(s);
|
||||
|
||||
var left := (w - s.Length) div 2;
|
||||
var right := w - s.Length - left;
|
||||
Result := new string(' ', left) + s + new string(' ', right);
|
||||
end;
|
||||
begin
|
||||
var names := new string[fClassCount];
|
||||
var rowHeader := 'Реально ↓';
|
||||
var topHeader := 'Предсказано →';
|
||||
var nameWidth := rowHeader.Length;
|
||||
|
||||
for var i := 0 to fClassCount - 1 do
|
||||
begin
|
||||
names[i] := GetClassDisplayName(i);
|
||||
if names[i].Length > nameWidth then
|
||||
nameWidth := names[i].Length;
|
||||
end;
|
||||
|
||||
var cellWidth := 1;
|
||||
for var i := 0 to fClassCount - 1 do
|
||||
for var j := 0 to fClassCount - 1 do
|
||||
if fMatrix[i, j].ToString.Length > cellWidth then
|
||||
cellWidth := fMatrix[i, j].ToString.Length;
|
||||
|
||||
for var i := 0 to fClassCount - 1 do
|
||||
if names[i].Length > cellWidth then
|
||||
cellWidth := names[i].Length;
|
||||
|
||||
var leftPad := nameWidth + 3;
|
||||
var firstColStart := leftPad;
|
||||
|
||||
var sb := new System.Text.StringBuilder;
|
||||
|
||||
sb.Append(new string(' ', firstColStart));
|
||||
sb.AppendLine(topHeader);
|
||||
|
||||
sb.Append(rowHeader.PadRight(nameWidth));
|
||||
sb.Append(' ');
|
||||
for var j := 0 to fClassCount - 1 do
|
||||
begin
|
||||
if j > 0 then
|
||||
sb.Append(' ');
|
||||
sb.Append(Center(names[j], cellWidth));
|
||||
end;
|
||||
sb.AppendLine;
|
||||
|
||||
for var i := 0 to fClassCount - 1 do
|
||||
begin
|
||||
sb.Append(names[i].PadRight(nameWidth));
|
||||
sb.Append(' ');
|
||||
|
||||
for var j := 0 to fClassCount - 1 do
|
||||
begin
|
||||
if j > 0 then
|
||||
sb.Append(' ');
|
||||
sb.Append(Center(fMatrix[i, j].ToString, cellWidth));
|
||||
end;
|
||||
|
||||
sb.AppendLine;
|
||||
end;
|
||||
|
||||
Result := sb.ToString.TrimEnd;
|
||||
end;
|
||||
|
||||
procedure ConfusionMatrix.Println;
|
||||
begin
|
||||
System.Console.WriteLine(ToString);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -94,8 +94,16 @@ type
|
|||
public
|
||||
constructor Create(g: GridWPF; r,c: integer);
|
||||
|
||||
function PaletteColor(i: integer): ColorWPF;
|
||||
|
||||
procedure LineGraph(x, y: array of real; color: ColorWPF := DefaultColor;
|
||||
thickness: real := 2; legend: string := nil);
|
||||
procedure Line(k, b: real; color: ColorWPF := DefaultColor;
|
||||
thickness: real := 2; legend: string := nil);
|
||||
procedure Line(A, B, C: real; color: ColorWPF := DefaultColor;
|
||||
thickness: real := 2; legend: string := nil);
|
||||
procedure Point(x, y: real; color: ColorWPF := DefaultColor;
|
||||
size: real := 8; marker: MarkerType := MarkerType.Circle; legend: string := nil);
|
||||
procedure Points(x, y: array of real; color: ColorWPF := DefaultColor;
|
||||
size: real := 6; marker: MarkerType := MarkerType.Circle; legend: string := nil);
|
||||
procedure Points(x, y: array of real; labels: array of integer;
|
||||
|
|
@ -173,6 +181,8 @@ type
|
|||
|
||||
static procedure DrawLine(chart: ChartWPF; x, y: array of real;
|
||||
color: ColorWPF; thickness: real; legend: string);
|
||||
static procedure DrawImplicitLine(chart: ChartWPF; A, B, C: real;
|
||||
color: ColorWPF; thickness: real; legend: string);
|
||||
|
||||
static procedure DrawText(chart: ChartWPF; s: string; x, y: real);
|
||||
|
||||
|
|
@ -207,8 +217,16 @@ type
|
|||
public
|
||||
static procedure AddSeries(chart: ChartWPF; series: UIElement);
|
||||
|
||||
static function PaletteColor(i: integer): ColorWPF;
|
||||
|
||||
static procedure LineGraph(x, y: array of real;
|
||||
color: ColorWPF := DefaultColor; thickness: real := 2; legend: string := nil);
|
||||
static procedure Line(k, b: real;
|
||||
color: ColorWPF := DefaultColor; thickness: real := 2; legend: string := nil);
|
||||
static procedure Line(A, B, C: real;
|
||||
color: ColorWPF := DefaultColor; thickness: real := 2; legend: string := nil);
|
||||
static procedure Point(x, y: real;
|
||||
color: ColorWPF := DefaultColor; size: real := 8; marker: MarkerType := MarkerType.Circle; legend: string := nil);
|
||||
|
||||
static procedure Points(x, y: array of real;
|
||||
color: ColorWPF := DefaultColor; size: real := 6; marker: MarkerType := MarkerType.Circle; legend: string := nil);
|
||||
|
|
@ -335,6 +353,24 @@ type
|
|||
property MaxCount: integer read fMaxCount;
|
||||
end;
|
||||
|
||||
ImplicitLinePlot = class(PlotWPF)
|
||||
private
|
||||
fA, fB, fC: real;
|
||||
fStroke: BrushWPF;
|
||||
fStrokeThickness: real;
|
||||
fDescription: string;
|
||||
|
||||
function IsFinite(x: real): boolean;
|
||||
procedure OnPlotTransformChanged(sender: object; e: EventArgs);
|
||||
protected
|
||||
function ComputeBounds: InteractiveDataDisplay.WPF.DataRect; override;
|
||||
procedure OnRender(dc: DrawingContext); override;
|
||||
public
|
||||
constructor Create(A, B, C: real; color: ColorWPF; thickness: real);
|
||||
|
||||
property Description: string read fDescription write fDescription;
|
||||
end;
|
||||
|
||||
HorizontalBarPlot = class(PlotWPF)
|
||||
private
|
||||
fElements: List<UIElement> := new List<UIElement>;
|
||||
|
|
@ -493,6 +529,19 @@ begin
|
|||
Result := c;
|
||||
end;
|
||||
|
||||
function ColorFromPalette(p: PaletteWPF; i: integer): ColorWPF;
|
||||
begin
|
||||
if (p = nil) or (p.Colors = nil) or (p.Colors.Length = 0) then
|
||||
exit(Colors.Black);
|
||||
|
||||
var n := p.Colors.Length;
|
||||
var k := i mod n;
|
||||
if k < 0 then
|
||||
k += n;
|
||||
|
||||
Result := p.Colors[k];
|
||||
end;
|
||||
|
||||
function Clamp01(x: real): real;
|
||||
begin
|
||||
if x < 0 then
|
||||
|
|
@ -950,6 +999,11 @@ begin
|
|||
Result := c;
|
||||
end;
|
||||
|
||||
function Cell.PaletteColor(i: integer): ColorWPF;
|
||||
begin
|
||||
Result := ColorFromPalette(palette, i);
|
||||
end;
|
||||
|
||||
procedure Cell.SetPalette(p: PaletteWPF);
|
||||
begin
|
||||
if p = nil then exit;
|
||||
|
|
@ -1108,6 +1162,29 @@ begin
|
|||
end);
|
||||
end;
|
||||
|
||||
procedure Cell.Line(k, b: real; color: ColorWPF; thickness: real; legend: string);
|
||||
begin
|
||||
Line(k, -1, b, color, thickness, legend);
|
||||
end;
|
||||
|
||||
procedure Cell.Line(A, B, C: real; color: ColorWPF; thickness: real; legend: string);
|
||||
begin
|
||||
Plot.RunUI(() ->
|
||||
begin
|
||||
EnsureChart;
|
||||
|
||||
var clr := if color<>DefaultColor then color else Colors.Red;
|
||||
|
||||
Plot.DrawImplicitLine(chart, A, B, C, clr, thickness, legend);
|
||||
end);
|
||||
end;
|
||||
|
||||
procedure Cell.Point(x, y: real; color: ColorWPF; size: real; marker: MarkerType; legend: string);
|
||||
begin
|
||||
var clr := if color<>DefaultColor then color else Colors.Black;
|
||||
Points(Arr(x), Arr(y), clr, size, marker, legend);
|
||||
end;
|
||||
|
||||
procedure Cell.Points(x, y: array of real; color: ColorWPF;
|
||||
size: real; marker: MarkerType; legend: string);
|
||||
begin
|
||||
|
|
@ -1631,6 +1708,20 @@ begin
|
|||
AddSeries(chart, g);
|
||||
end;
|
||||
|
||||
static procedure Plot.DrawImplicitLine(chart: ChartWPF; A, B, C: real;
|
||||
color: ColorWPF; thickness: real; legend: string);
|
||||
begin
|
||||
var g := new ImplicitLinePlot(A, B, C, color, thickness);
|
||||
|
||||
if legend <> nil then
|
||||
begin
|
||||
g.Description := legend;
|
||||
chart.LegendVisibility := Visibility.Visible;
|
||||
end;
|
||||
|
||||
AddSeries(chart, g);
|
||||
end;
|
||||
|
||||
static procedure Plot.DrawText(chart: ChartWPF; s: string; x, y: real);
|
||||
begin
|
||||
var tb := new System.Windows.Controls.TextBlock;
|
||||
|
|
@ -1987,6 +2078,11 @@ begin
|
|||
container.Children.Add(series);
|
||||
end;
|
||||
|
||||
class function Plot.PaletteColor(i: integer): ColorWPF;
|
||||
begin
|
||||
Result := ColorFromPalette(currentPalette, i);
|
||||
end;
|
||||
|
||||
class procedure Plot.LineGraph(x, y: array of real;
|
||||
color: ColorWPF; thickness: real; legend: string);
|
||||
begin
|
||||
|
|
@ -1998,6 +2094,27 @@ begin
|
|||
end);
|
||||
end;
|
||||
|
||||
class procedure Plot.Line(k, b: real; color: ColorWPF; thickness: real; legend: string);
|
||||
begin
|
||||
Line(k, -1, b, color, thickness, legend);
|
||||
end;
|
||||
|
||||
class procedure Plot.Line(A, B, C: real; color: ColorWPF; thickness: real; legend: string);
|
||||
begin
|
||||
RunUI(() ->
|
||||
begin
|
||||
var clr := if color<>DefaultColor then color else Colors.Red;
|
||||
|
||||
DrawImplicitLine(rootChart, A, B, C, clr, thickness, legend);
|
||||
end);
|
||||
end;
|
||||
|
||||
class procedure Plot.Point(x, y: real; color: ColorWPF; size: real; marker: MarkerType; legend: string);
|
||||
begin
|
||||
var clr := if color<>DefaultColor then color else Colors.Black;
|
||||
Points(Arr(x), Arr(y), clr, size, marker, legend);
|
||||
end;
|
||||
|
||||
static procedure Plot.Points(x, y: array of real;
|
||||
color: ColorWPF; size: real; marker: MarkerType; legend: string);
|
||||
begin
|
||||
|
|
@ -2722,6 +2839,94 @@ begin
|
|||
fMaxCount := counts.Max;
|
||||
end;
|
||||
|
||||
constructor ImplicitLinePlot.Create(A, B, C: real; color: ColorWPF; thickness: real);
|
||||
begin
|
||||
fA := A;
|
||||
fB := B;
|
||||
fC := C;
|
||||
fStroke := new SolidColorBrush(color);
|
||||
fStrokeThickness := thickness;
|
||||
IsAutoFitEnabled := false;
|
||||
PlotTransformChanged += OnPlotTransformChanged;
|
||||
end;
|
||||
|
||||
function ImplicitLinePlot.IsFinite(x: real): boolean;
|
||||
begin
|
||||
Result := not real.IsNaN(x) and not real.IsInfinity(x);
|
||||
end;
|
||||
|
||||
procedure ImplicitLinePlot.OnPlotTransformChanged(sender: object; e: EventArgs);
|
||||
begin
|
||||
InvalidateVisual;
|
||||
end;
|
||||
|
||||
function ImplicitLinePlot.ComputeBounds: InteractiveDataDisplay.WPF.DataRect;
|
||||
begin
|
||||
Result := InteractiveDataDisplay.WPF.DataRect.Empty;
|
||||
end;
|
||||
|
||||
procedure ImplicitLinePlot.OnRender(dc: DrawingContext);
|
||||
begin
|
||||
inherited OnRender(dc);
|
||||
|
||||
if (Abs(fA) < 1e-12) and (Abs(fB) < 1e-12) then
|
||||
exit;
|
||||
|
||||
var r := ActualPlotRect;
|
||||
if r.IsEmpty then
|
||||
exit;
|
||||
|
||||
var xmin := r.XMin;
|
||||
var xmax := r.XMax;
|
||||
var ymin := r.YMin;
|
||||
var ymax := r.YMax;
|
||||
|
||||
var pts := new List<Point>;
|
||||
|
||||
if Abs(fB) > 1e-12 then
|
||||
begin
|
||||
var y := (-fA * xmin - fC) / fB;
|
||||
if IsFinite(y) and (y >= ymin - 1e-9) and (y <= ymax + 1e-9) then
|
||||
pts.Add(new Point(xmin, y));
|
||||
|
||||
y := (-fA * xmax - fC) / fB;
|
||||
if IsFinite(y) and (y >= ymin - 1e-9) and (y <= ymax + 1e-9) then
|
||||
pts.Add(new Point(xmax, y));
|
||||
end;
|
||||
|
||||
if Abs(fA) > 1e-12 then
|
||||
begin
|
||||
var x := (-fB * ymin - fC) / fA;
|
||||
if IsFinite(x) and (x >= xmin - 1e-9) and (x <= xmax + 1e-9) then
|
||||
pts.Add(new Point(x, ymin));
|
||||
|
||||
x := (-fB * ymax - fC) / fA;
|
||||
if IsFinite(x) and (x >= xmin - 1e-9) and (x <= xmax + 1e-9) then
|
||||
pts.Add(new Point(x, ymax));
|
||||
end;
|
||||
|
||||
if pts.Count < 2 then
|
||||
exit;
|
||||
|
||||
var p1 := pts[0];
|
||||
var p2 := pts[1];
|
||||
|
||||
for var i := 1 to pts.Count - 1 do
|
||||
if Abs(pts[i].X - p1.X) + Abs(pts[i].Y - p1.Y) > 1e-9 then
|
||||
begin
|
||||
p2 := pts[i];
|
||||
break;
|
||||
end;
|
||||
|
||||
if Abs(p2.X - p1.X) + Abs(p2.Y - p1.Y) <= 1e-9 then
|
||||
exit;
|
||||
|
||||
var s1 := new Point(LeftFromX(p1.X), TopFromY(p1.Y));
|
||||
var s2 := new Point(LeftFromX(p2.X), TopFromY(p2.Y));
|
||||
|
||||
dc.DrawLine(new Pen(fStroke, fStrokeThickness), s1, s2);
|
||||
end;
|
||||
|
||||
constructor HorizontalBarPlot.Create;
|
||||
begin
|
||||
IsAutoFitEnabled := false;
|
||||
|
|
|
|||
Loading…
Reference in a new issue