Новые модули ML LinearAlgebraABC и PreprocessorABC.pas
This commit is contained in:
parent
38599a310f
commit
d8c0645ac0
|
|
@ -15,7 +15,7 @@ internal static class RevisionClass
|
|||
public const string Major = "3";
|
||||
public const string Minor = "11";
|
||||
public const string Build = "1";
|
||||
public const string Revision = "3748";
|
||||
public const string Revision = "3749";
|
||||
|
||||
public const string MainVersion = Major + "." + Minor;
|
||||
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%COREVERSION%=1
|
||||
%REVISION%=3748
|
||||
%MINOR%=11
|
||||
%REVISION%=3749
|
||||
%COREVERSION%=1
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
// Загрузка и просмотр данных
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
''');
|
||||
|
||||
df.Print;
|
||||
end.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
// Загрузка данных из CSV-файла
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsv('people.csv');
|
||||
df.Print;
|
||||
end.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Выбор столбцов (Select)
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
''');
|
||||
|
||||
df.Select(['name', 'score']).Print;
|
||||
end.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Переименование столбцов (Rename)
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
''');
|
||||
|
||||
df.Rename('score', 'exam_score').Print;
|
||||
end.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Удаление столбцов (Drop)
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
''');
|
||||
|
||||
df.Drop(['score']).Print;
|
||||
end.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Простая фильтрация
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
''');
|
||||
|
||||
df.Filter(row -> row.Int('age') >= 21).Print;
|
||||
end.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Фильтрация с NA
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.Filter(row -> row.IsValid('score')).Print;
|
||||
end.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Новый числовой столбец
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.WithColumnBool('passed', row -> row.Int('score') >= 80).Print;
|
||||
end.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Обработка ошибок (NA)
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.WithColumnFloat('normalized', row -> row.Float('score') / 100).Print;
|
||||
end.
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// Простые статистики
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.Min('score').Println;
|
||||
df.Mean('score').Println;
|
||||
df.Max('score').Println;
|
||||
end.
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// Describe одного столбца
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
var d := df.Describe('score');
|
||||
Print(d.Count,d.Min,d.Max,d.Mean,d.Std);
|
||||
end.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Describe всех числовых столбцов
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.Describe.PrintLines;
|
||||
end.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// GroupBy + Count
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.GroupBy('age').Count.Print;
|
||||
end.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// GroupBy + Mean
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.GroupBy('age').Mean('score').Print;
|
||||
end.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// GroupBy + Describe
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.GroupBy('age').Describe('score').Print;
|
||||
end.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// Inner Join
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var students := DataFrame.FromCsvText('''
|
||||
id,name
|
||||
1,Alice
|
||||
2,Bob
|
||||
3,Charlie
|
||||
''');
|
||||
|
||||
var scores := DataFrame.FromCsvText('''
|
||||
id,score
|
||||
1,85
|
||||
2,90
|
||||
4,70
|
||||
''');
|
||||
|
||||
students.Join(scores, 'id').Print;
|
||||
end.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// Left Join
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var students := DataFrame.FromCsvText('''
|
||||
id,name
|
||||
1,Alice
|
||||
2,Bob
|
||||
3,Charlie
|
||||
''');
|
||||
|
||||
var scores := DataFrame.FromCsvText('''
|
||||
id,score
|
||||
1,85
|
||||
2,90
|
||||
4,70
|
||||
''');
|
||||
|
||||
students.Join(scores, 'id', jkLeft).Print;
|
||||
end.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// Full Join
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var students := DataFrame.FromCsvText('''
|
||||
id,name
|
||||
1,Alice
|
||||
2,Bob
|
||||
3,Charlie
|
||||
''');
|
||||
|
||||
var scores := DataFrame.FromCsvText('''
|
||||
id,score
|
||||
1,85
|
||||
2,90
|
||||
4,70
|
||||
''');
|
||||
|
||||
students.Join(scores, 'id', jkFull).Print;
|
||||
end.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Сортировка
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
''');
|
||||
|
||||
df.SortBy('score', descending := True).Print;
|
||||
end.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Просмотр больших данных
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.PrintPreview(3);
|
||||
end.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Полный аналитический сценарий
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df
|
||||
.Filter(r -> r.IsValid('score'))
|
||||
.SortBy('age', descending := True)
|
||||
.GroupBy('age')
|
||||
.Mean('score')
|
||||
.PrintPreview(3);
|
||||
end.
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// Сортировка по нескольким полям
|
||||
uses DataFrameABC;
|
||||
|
||||
begin
|
||||
var df := DataFrame.FromCsvText('''
|
||||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
Bob,22,90
|
||||
Clara,,78
|
||||
Kat,21,NA
|
||||
''');
|
||||
|
||||
df.Filter(r -> r.IsValid('score'))
|
||||
.SortBy(['age','score'],[True,True])
|
||||
.Println;
|
||||
end.
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
name,age,score
|
||||
Alice,20,85
|
||||
Bob,22,90
|
||||
Charlie,21,78
|
||||
|
|
|
@ -1 +1 @@
|
|||
3.11.1.3748
|
||||
3.11.1.3749
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.11.1.3748'
|
||||
!define VERSION '3.11.1.3749'
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ uses
|
|||
BBCMicroBit, School, SF, TwoPanelsWindow, NUnitABC, PlotWPF, XLSX, LightPT, Tasks, Мозаика,
|
||||
TasksArr, TasksMatr, TasksStr, Tasks1Begin, Tasks1BoolIfCase, Tasks1Loops, Tasks1Arr,
|
||||
WPF,
|
||||
DataFrameABC, DataFrameABCCore
|
||||
DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC
|
||||
;
|
||||
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
Collections, Arrays, Core, ClientServer, Countries,
|
||||
ABCDatabases,
|
||||
School, SF, TurtleABC,
|
||||
DataFrameABC, DataFrameABCCore
|
||||
DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC
|
||||
;
|
||||
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@
|
|||
File ..\bin\Lib\WPF.pcu
|
||||
File ..\bin\Lib\DataFrameABC.pcu
|
||||
File ..\bin\Lib\DataFrameABCCore.pcu
|
||||
File ..\bin\Lib\LinearAlgebraML.pcu
|
||||
File ..\bin\Lib\PreprocessorABC.pcu
|
||||
|
||||
File ..\bin\Lib\PABCRtl.dll
|
||||
File ..\bin\Lib\HelixToolkit.Wpf.dll
|
||||
|
|
@ -276,6 +278,8 @@
|
|||
${AddFile} "WPF.pcu"
|
||||
${AddFile} "DataFrameABC.pcu"
|
||||
${AddFile} "DataFrameABCCore.pcu"
|
||||
${AddFile} "LinearAlgebraML.pcu"
|
||||
${AddFile} "PreprocessorABC.pcu"
|
||||
|
||||
${AddFile} "turtle.png"
|
||||
|
||||
|
|
@ -407,6 +411,8 @@
|
|||
File ..\bin\Lib\WPF.pas
|
||||
File ..\bin\Lib\DataFrameABC.pas
|
||||
File ..\bin\Lib\DataFrameABCCore.pas
|
||||
File ..\bin\Lib\LinearAlgebraML.pas
|
||||
File ..\bin\Lib\PreprocessorABC.pas
|
||||
|
||||
|
||||
File ..\bin\Lib\__RedirectIOMode.vb
|
||||
|
|
@ -490,6 +496,8 @@
|
|||
${AddFile} "WPF.pas"
|
||||
${AddFile} "DataFrameABC.pas"
|
||||
${AddFile} "DataFrameABCCore.pas"
|
||||
${AddFile} "LinearAlgebraML.pas"
|
||||
${AddFile} "PreprocessorABC.pas"
|
||||
|
||||
${AddFile} "__RedirectIOMode.vb"
|
||||
${AddFile} "VBSystem.vb"
|
||||
|
|
|
|||
|
|
@ -110,8 +110,10 @@ copy bin\Lib\Speech.pcu Release\PascalABCNETLinux\Lib\Speech.pcu
|
|||
copy bin\Lib\Tasks.pcu Release\PascalABCNETLinux\Lib\Tasks.pcu
|
||||
copy bin\Lib\Timers.pcu Release\PascalABCNETLinux\Lib\Timers.pcu
|
||||
copy bin\Lib\TurtleABC.pcu Release\PascalABCNETLinux\Lib\TurtleABC.pcu
|
||||
copy bin\Lib\DataFrameABC.pcu Release\PascalABCNETLinux\LibSource\DataFrameABC.pcu
|
||||
copy bin\Lib\DataFrameABCCore.pcu Release\PascalABCNETLinux\LibSource\DataFrameABCCore.pcu
|
||||
copy bin\Lib\DataFrameABC.pcu Release\PascalABCNETLinux\Lib\DataFrameABC.pcu
|
||||
copy bin\Lib\DataFrameABCCore.pcu Release\PascalABCNETLinux\Lib\DataFrameABCCore.pcu
|
||||
copy bin\Lib\LinearAlgebraML.pcu Release\PascalABCNETLinux\Lib\LinearAlgebraML.pcu
|
||||
copy bin\Lib\PreprocessorABC.pcu Release\PascalABCNETLinux\Lib\PreprocessorABC.pcu
|
||||
|
||||
copy bin\Lib\ABCDatabases.pas Release\PascalABCNETLinux\LibSource\ABCDatabases.pas
|
||||
copy bin\Lib\BBCMicrobit.pas Release\PascalABCNETLinux\LibSource\BBCMicrobit.pas
|
||||
|
|
@ -155,6 +157,8 @@ copy bin\Lib\Робот.pas Release\PascalABCNETLinux\LibSource\Робот.pas
|
|||
copy bin\Lib\Чертежник.pas Release\PascalABCNETLinux\LibSource\Чертежник.pas
|
||||
copy bin\Lib\DataFrameABC.pas Release\PascalABCNETLinux\LibSource\DataFrameABC.pas
|
||||
copy bin\Lib\DataFrameABCCore.pas Release\PascalABCNETLinux\LibSource\DataFrameABCCore.pas
|
||||
copy bin\Lib\LinearAlgebraML.pas Release\PascalABCNETLinux\LibSource\LinearAlgebraML.pas
|
||||
copy bin\Lib\PreprocessorABC.pas Release\PascalABCNETLinux\LibSource\PreprocessorABC.pas
|
||||
|
||||
copy bin\Lng\Eng\.LanguageName Release\PascalABCNETLinux\Lng\Eng\.LanguageName
|
||||
copy bin\Lng\Eng\AspectsTree.dat Release\PascalABCNETLinux\Lng\Eng\AspectsTree.dat
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ type
|
|||
function CreateEmptyBySchema(schema: DataFrameSchema): DataFrame;
|
||||
|
||||
public
|
||||
/// Схема DataFrame (имена/типы/categorical), не содержит данных
|
||||
/// Схема DataFrame: имена, типы и признаки категориальности
|
||||
property Schema: DataFrameSchema read fschema;
|
||||
|
||||
/// Добавляет в DataFrame столбец-представление (view),
|
||||
|
|
@ -114,7 +114,7 @@ type
|
|||
procedure AddFloatColumn(name: string; data: array of real; valid: array of boolean);
|
||||
/// Добавляет строковый столбец
|
||||
procedure AddStrColumn(name: string; data: array of string; valid: array of boolean; isCategorical: boolean := true);
|
||||
/// Добавляет булев столбец
|
||||
/// Добавляет столбец логических значений
|
||||
procedure AddBoolColumn(name: string; data: array of boolean; valid: array of boolean);
|
||||
|
||||
/// Возвращает данные целочисленного столбца по имени
|
||||
|
|
@ -122,132 +122,137 @@ type
|
|||
/// Возвращает данные вещественного столбца по имени
|
||||
function GetFloatColumn(name: string): array of real;
|
||||
|
||||
/// Вычисляет сумму значений в столбце по индексу
|
||||
/// Вычисляет сумму значений столбца по индексу
|
||||
function Sum(colIndex: integer): real;
|
||||
/// Вычисляет сумму значений в столбце по имени
|
||||
/// Вычисляет сумму значений столбца по имени
|
||||
function Sum(colName: string): real;
|
||||
/// Подсчитывает количество валидных значений в столбце по индексу
|
||||
function Count(colIndex: integer): integer;
|
||||
/// Подсчитывает количество валидных значений в столбце по имени
|
||||
function Count(colName: string): integer;
|
||||
/// Вычисляет среднее значение в столбце по индексу
|
||||
/// Вычисляет среднее значение столбца по индексу
|
||||
function Mean(colIndex: integer): real;
|
||||
/// Вычисляет среднее значение в столбце по имени
|
||||
/// Вычисляет среднее значение столбца по имени
|
||||
function Mean(colName: string): real;
|
||||
/// Вычисляет медиану по валидным (non-NA) значениям столбца по индексу
|
||||
function Median(colIndex: integer): real;
|
||||
/// Вычисляет медиану по валидным (non-NA) значениям столбца по имени
|
||||
function Median(colName: string): real;
|
||||
/// Находит минимальное значение в столбце по индексу
|
||||
/// Находит минимальное значение столбца по индексу
|
||||
function Min(colIndex: integer): real;
|
||||
/// Находит минимальное значение в столбце по имени
|
||||
/// Находит минимальное значение столбца по имени
|
||||
function Min(colName: string): real;
|
||||
/// Находит максимальное значение в столбце по индексу
|
||||
/// Находит максимальное значение столбца по индексу
|
||||
function Max(colIndex: integer): real;
|
||||
/// Находит максимальное значение в столбце по имени
|
||||
/// Находит максимальное значение столбца по имени
|
||||
function Max(colName: string): real;
|
||||
/// Находит минимальное и максимальное значения в столбце по индексу
|
||||
/// Находит минимальное и максимальное значения столбца по индексу
|
||||
function MinMax(colIndex: integer): (real, real);
|
||||
/// Находит минимальное и максимальное значения в столбце по имени
|
||||
/// Находит минимальное и максимальное значения столбца по имени
|
||||
function MinMax(colName: string): (real, real);
|
||||
/// Вычисляет дисперсию значений в столбце по индексу
|
||||
/// Вычисляет дисперсию значений столбца по индексу
|
||||
function Variance(colIndex: integer): real;
|
||||
/// Вычисляет дисперсию значений в столбце по имени
|
||||
/// Вычисляет дисперсию значений столбца по имени
|
||||
function Variance(colName: string): real;
|
||||
/// Вычисляет стандартное отклонение в столбце по индексу
|
||||
/// Вычисляет стандартное отклонение столбца по индексу
|
||||
function Std(colIndex: integer): real;
|
||||
/// Вычисляет стандартное отклонение в столбце по имени
|
||||
/// Вычисляет стандартное отклонение столбца по имени
|
||||
function Std(colName: string): real;
|
||||
/// Вычисляет среднее и дисперсию в столбце по индексу
|
||||
/// Вычисляет среднее и дисперсию столбца по индексу
|
||||
function MeanVariance(colIndex: integer): (real, real);
|
||||
/// Вычисляет среднее и дисперсию в столбце по имени
|
||||
/// Вычисляет среднее и дисперсию столбца по имени
|
||||
function MeanVariance(colName: string): (real, real);
|
||||
|
||||
/// Возвращает полную статистику по столбцу по индексу
|
||||
/// Возвращает статистику столбца по индексу
|
||||
function Describe(colIndex: integer): DescribeStats;
|
||||
/// Возвращает полную статистику по столбцу по имени
|
||||
/// Возвращает статистику столбца по имени
|
||||
function Describe(colName: string): DescribeStats;
|
||||
/// Возвращает статистику по нескольким столбцам по именам
|
||||
function Describe(colNames: array of string): Dictionary<string, DescribeStats>;
|
||||
/// Возвращает статистику по нескольким столбцам по индексам
|
||||
function Describe(colIndices: array of integer): Dictionary<integer, DescribeStats>;
|
||||
/// Возвращает статистику по всем числовым столбцам
|
||||
/// Возвращает статистику по всем числовым столбц ам
|
||||
function DescribeAll: Dictionary<string, DescribeStats>;
|
||||
|
||||
/// Группирует данные по одному столбцу по индексу
|
||||
/// Группирует данные по столбцу по индексу
|
||||
function GroupBy(colIndex: integer): IGroupByContext;
|
||||
/// Группирует данные по одному столбцу по имени
|
||||
/// Группирует данные по столбцу по имени
|
||||
function GroupBy(colName: string): IGroupByContext;
|
||||
/// Группирует данные по нескольким столбцам по индексам
|
||||
function GroupBy(colIndices: array of integer): IGroupByContext;
|
||||
/// Группирует данные по нескольким столбцам по именам
|
||||
function GroupBy(colNames: array of string): IGroupByContext;
|
||||
|
||||
/// Возвращает первые n строк DataFrame
|
||||
/// Возвращает первые n строк
|
||||
function Head(n: integer): DataFrame;
|
||||
/// Возвращает последние n строк DataFrame
|
||||
/// Возвращает последние n строк
|
||||
function Tail(n: integer): DataFrame;
|
||||
|
||||
/// Фильтрует строки по предикату
|
||||
function Filter(pred: CursorPredicate): DataFrame;
|
||||
/// Выбирает указанные столбцы по индексам (семантика view)
|
||||
/// Выбирает столбцы по индексам
|
||||
function Select(colIndices: array of integer): DataFrame;
|
||||
/// Выбирает указанные столбцы по именам
|
||||
/// Выбирает столбцы по именам
|
||||
function Select(colNames: array of string): DataFrame;
|
||||
/// Сортирует DataFrame по одному столбцу по индексу
|
||||
/// Сортирует по столбцу по индексу
|
||||
function SortBy(colIndex: integer; descending: boolean := false): DataFrame;
|
||||
/// Сортирует DataFrame по одному столбцу по имени
|
||||
/// Сортирует по столбцу по имени
|
||||
function SortBy(colName: string; descending: boolean := false): DataFrame;
|
||||
/// Сортирует DataFrame по нескольким столбцам по индексам
|
||||
/// Сортирует по нескольким столбцам по индексам
|
||||
function SortBy(colIndices: array of integer; descending: array of boolean): DataFrame;
|
||||
/// Сортирует DataFrame по нескольким столбцам по именам
|
||||
/// Сортирует по нескольким столбцам по именам
|
||||
function SortBy(colNames: array of string; descending: array of boolean): DataFrame;
|
||||
|
||||
/// Удаляет указанные столбцы по индексам
|
||||
/// Удаляет столбцы по индексам
|
||||
function Drop(colIndices: array of integer): DataFrame;
|
||||
/// Удаляет указанные столбцы по именам
|
||||
/// Удаляет столбцы по именам
|
||||
function Drop(colNames: array of string): DataFrame;
|
||||
|
||||
/// Переименовывает столбец по индексу
|
||||
function Rename(colIndex: integer; newName: string): DataFrame;
|
||||
/// Переименовывает столбец по старому имени
|
||||
/// Переименовывает столбец по имени
|
||||
function Rename(oldName, newName: string): DataFrame;
|
||||
/// Переименовывает несколько столбцов
|
||||
function Rename(pairs: array of (string, string)): DataFrame;
|
||||
|
||||
/// Добавляет вычисляемый столбец целых чисел
|
||||
/// Добавляет вычисляемый целочисленный столбец
|
||||
function WithColumnInt(name: string; f: DataFrameCursor -> integer): DataFrame;
|
||||
/// Добавляет вычисляемый столбец целых чисел (сокращенная версия)
|
||||
/// Добавляет вычисляемый целочисленный столбец
|
||||
function WithColumn(name: string; f: DataFrameCursor -> integer): DataFrame := WithColumnInt(name, f);
|
||||
/// Добавляет вычисляемый столбец вещественных чисел
|
||||
/// Добавляет вычисляемый вещественный столбец
|
||||
function WithColumnFloat(name: string; f: DataFrameCursor -> real): DataFrame;
|
||||
/// Добавляет вычисляемый строковый столбец
|
||||
function WithColumnStr(name: string; f: DataFrameCursor -> string): DataFrame;
|
||||
/// Добавляет вычисляемый булев столбец
|
||||
/// Добавляет вычисляемый логический столбец
|
||||
function WithColumnBool(name: string; f: DataFrameCursor -> boolean): DataFrame;
|
||||
|
||||
/// Заменяет существующий числовой столбец, пересчитывая его по функции от курсора
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
function ReplaceColumnFloat(colName: string; f: DataFrameCursor -> real): DataFrame;
|
||||
/// Заменяет существующий числовой столбец, пересчитывая его по функции от курсора
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
function ReplaceColumnInt(colName: string; f: DataFrameCursor -> integer): DataFrame;
|
||||
|
||||
function AddDerivedIntColumn(name: string; f: DataFrameCursor -> integer): DataFrame;
|
||||
|
||||
/// Соединяет с другим DataFrame по нескольким ключам
|
||||
function Join(other: DataFrame; keys: array of string; kind: JoinKind := jkInner): DataFrame;
|
||||
/// Соединяет с другим DataFrame по одному ключу
|
||||
function Join(other: DataFrame; key: string; kind: JoinKind := jkInner): DataFrame;
|
||||
/// Соединяет с другим DataFrame по нескольким ключам
|
||||
function Join(other: DataFrame; keys: array of string; kind: JoinKind := jkInner): DataFrame;
|
||||
/// Соединяет с другим DataFrame по разным именам ключей
|
||||
function Join(other: DataFrame; leftKeys, rightKeys: array of string; kind: JoinKind := jkInner): DataFrame;
|
||||
|
||||
/// Выводит DataFrame в консоль с ограниченным количеством строк
|
||||
/// Выводит DataFrame
|
||||
procedure Print(decimals: integer := 3);
|
||||
/// Выводит DataFrame в консоль и переходит на новую строку
|
||||
/// Выводит DataFrame и переходит на новую строку
|
||||
procedure Println(decimals: integer := 3);
|
||||
/// Выводит предпросмотр DataFrame с настройкой отображаемых строк
|
||||
/// Выводит DataFrame с настраиваемым числом строк
|
||||
procedure PrintPreview(maxRows: integer; headRows: integer := -1; decimals: integer := 3);
|
||||
/// Выводит предпросмотр DataFrame и переходит на новую строку
|
||||
/// Выводит DataFrame с настраиваемым числом строк и переходит на новую строку
|
||||
procedure PrintlnPreview(maxRows: integer; headRows: integer := -1; decimals: integer := 3);
|
||||
/// Выводит схему датафрейма
|
||||
procedure PrintSchema;
|
||||
/// Выводит размер датафрейма, его схему и количество не NA-значений для каждого столбца
|
||||
/// Выводит размер, схему и количество валидных значений
|
||||
procedure PrintInfo;
|
||||
|
||||
/// Загружает DataFrame из CSV файла
|
||||
|
|
@ -274,41 +279,41 @@ type
|
|||
end;
|
||||
|
||||
type
|
||||
/// Прикладные статистические методы для анализа и подготовки табличных данных.
|
||||
/// Используется совместно с DataFrame для разведочного анализа данных (EDA) и задач машинного обучения
|
||||
Statistics = static class
|
||||
public
|
||||
/// Коэффициент корреляции Пирсона между двумя числовыми столбцами
|
||||
/// Пропущенные значения (NA) игнорируются попарно
|
||||
static function Correlation(df: DataFrame; colX, colY: string): real;
|
||||
|
||||
/// Матрица корреляций Пирсона для всех числовых столбцов DataFrame
|
||||
/// Первый столбец содержит имена признаков
|
||||
static function CorrelationMatrix(df: DataFrame): DataFrame;
|
||||
|
||||
/// Стандартизует числовой столбец: (x - mean) / std
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
static function Standardize(df: DataFrame; colName: string): DataFrame;
|
||||
/// Статистические методы для анализа табличных данных
|
||||
/// Используются совместно с DataFrame для анализа и подготовки данных
|
||||
Statistics = static class
|
||||
public
|
||||
/// Коэффициент корреляции Пирсона между двумя числовыми столбцами
|
||||
/// Пропущенные значения (NA) игнорируются попарно
|
||||
static function Correlation(df: DataFrame; colX, colY: string): real;
|
||||
|
||||
/// Стандартизует все числовые столбцы: (x - mean) / std
|
||||
static function StandardizeAll(df: DataFrame): DataFrame;
|
||||
|
||||
/// Нормализует числовой столбец в диапазон [0, 1]
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
static function Normalize(df: DataFrame; colName: string): DataFrame;
|
||||
/// Матрица корреляций Пирсона для всех числовых столбцов
|
||||
/// Первый столбец содержит имена признаков
|
||||
static function CorrelationMatrix(df: DataFrame): DataFrame;
|
||||
|
||||
/// Нормализует все числовые столбцы в диапазон [0, 1]
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
static function NormalizeAll(df: DataFrame): DataFrame;
|
||||
|
||||
/// p-квантиль числового столбца (0 ≤ p ≤ 1) — граница, ниже которой находится p·100% валидных значений
|
||||
/// Пропущенные значения (NA) игнорируются
|
||||
static function Quantile(df: DataFrame; colName: string; p: real): real;
|
||||
|
||||
/// Медиана числового столбца
|
||||
/// Эквивалентна Quantile(..., 0.5)
|
||||
static function Median(df: DataFrame; colName: string): real;
|
||||
end;
|
||||
/// Стандартизует числовой столбец: (x - mean) / std
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
static function Standardize(df: DataFrame; colName: string): DataFrame;
|
||||
|
||||
/// Стандартизует все числовые столбцы: (x - mean) / std
|
||||
static function StandardizeAll(df: DataFrame): DataFrame;
|
||||
|
||||
/// Нормализует числовой столбец в диапазон [0, 1]
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
static function Normalize(df: DataFrame; colName: string): DataFrame;
|
||||
|
||||
/// Нормализует все числовые столбцы в диапазон [0, 1]
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
static function NormalizeAll(df: DataFrame): DataFrame;
|
||||
|
||||
/// Вычисляет p-квантиль числового столбца (0 ≤ p ≤ 1)
|
||||
/// Пропущенные значения (NA) игнорируются
|
||||
static function Quantile(df: DataFrame; colName: string; p: real): real;
|
||||
|
||||
/// Вычисляет медиану числового столбца
|
||||
/// Эквивалентна Quantile(..., 0.5)
|
||||
static function Median(df: DataFrame; colName: string): real;
|
||||
end;
|
||||
|
||||
type
|
||||
/// Статический класс для загрузки данных из CSV файлов
|
||||
|
|
@ -2629,121 +2634,91 @@ begin
|
|||
Result.AssertSchemaConsistent;
|
||||
end;
|
||||
|
||||
|
||||
{ procedure DataFrame.PrintPreview(maxRows: integer; headRows: integer; decimals: integer);
|
||||
function DataFrame.ReplaceColumnInt(colName: string; f: DataFrameCursor -> integer): DataFrame;
|
||||
begin
|
||||
var colCount := columns.Count;
|
||||
if colCount = 0 then exit;
|
||||
var colIndex := ColumnIndex(colName);
|
||||
var rowCount := RowCount;
|
||||
|
||||
var data := new integer[rowCount];
|
||||
var valid: array of boolean := nil;
|
||||
|
||||
var cur := GetCursor;
|
||||
var row := 0;
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
try
|
||||
data[row] := f(cur);
|
||||
if valid <> nil then valid[row] := true;
|
||||
except
|
||||
on e: Exception do
|
||||
begin
|
||||
data[row] := 0;
|
||||
if valid = nil then
|
||||
begin
|
||||
valid := new boolean[rowCount];
|
||||
for var j := 0 to row - 1 do valid[j] := true;
|
||||
end;
|
||||
valid[row] := false;
|
||||
end;
|
||||
end;
|
||||
row += 1;
|
||||
end;
|
||||
|
||||
var res := new DataFrame;
|
||||
for var i := 0 to columns.Count - 1 do
|
||||
if i <> colIndex then
|
||||
res.AddColumnView(columns[i])
|
||||
else
|
||||
res.AddIntColumn(colName, data, valid);
|
||||
|
||||
Result := res;
|
||||
Result.AssertSchemaConsistent;
|
||||
end;
|
||||
|
||||
function DataFrame.AddDerivedIntColumn(
|
||||
name: string;
|
||||
f: DataFrameCursor -> integer
|
||||
): DataFrame;
|
||||
begin
|
||||
if Schema.HasColumn(name) then
|
||||
raise new Exception($'Column "{name}" already exists');
|
||||
|
||||
var rowCount := RowCount;
|
||||
if rowCount = 0 then exit;
|
||||
var data := new integer[rowCount];
|
||||
var valid: array of boolean := nil;
|
||||
|
||||
// вычисляем head / tail
|
||||
if maxRows < 1 then exit;
|
||||
|
||||
if rowCount <= maxRows then
|
||||
headRows := rowCount
|
||||
else
|
||||
var cur := GetCursor;
|
||||
var row := 0;
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
if headRows = -1 then
|
||||
headRows := (maxRows + 1) div 2;
|
||||
|
||||
if headRows < 0 then headRows := 0;
|
||||
if headRows > maxRows then headRows := maxRows;
|
||||
end;
|
||||
|
||||
var tailRows := maxRows - headRows;
|
||||
if tailRows < 0 then tailRows := 0;
|
||||
if tailRows > rowCount - headRows then
|
||||
tailRows := rowCount - headRows;
|
||||
|
||||
// 1. вычисляем ширины столбцов
|
||||
var widths := new integer[colCount];
|
||||
|
||||
// ширины по заголовкам
|
||||
for var j := 0 to colCount - 1 do
|
||||
widths[j] := columns[j].Info.Name.Length;
|
||||
|
||||
var cursor := GetCursor;
|
||||
|
||||
// ScanRow перемещает cursor
|
||||
var ScanRow: integer -> () := row ->
|
||||
begin
|
||||
cursor.MoveTo(row);
|
||||
for var j := 0 to colCount - 1 do
|
||||
begin
|
||||
var s: string;
|
||||
|
||||
if not cursor.IsValid(j) then
|
||||
s := 'NA'
|
||||
else
|
||||
case columns[j].Info.ColType of
|
||||
ctInt: s := cursor.Int(j).ToString;
|
||||
ctFloat: s := cursor.Float(j).ToString('F' + decimals);
|
||||
ctStr: s := cursor.Str(j);
|
||||
ctBool: s := cursor.Bool(j).ToString;
|
||||
try
|
||||
data[row] := f(cur);
|
||||
if valid <> nil then valid[row] := true;
|
||||
except
|
||||
on e: Exception do
|
||||
begin
|
||||
data[row] := 0;
|
||||
if valid = nil then
|
||||
begin
|
||||
valid := new boolean[rowCount];
|
||||
for var j := 0 to row - 1 do valid[j] := true;
|
||||
end;
|
||||
|
||||
if s.Length > widths[j] then
|
||||
widths[j] := s.Length;
|
||||
end;
|
||||
end;
|
||||
|
||||
var FormatValue: (integer) -> string := j ->
|
||||
begin
|
||||
if not cursor.IsValid(j) then
|
||||
Result := 'NA'
|
||||
else
|
||||
case columns[j].Info.ColType of
|
||||
ctInt: Result := cursor.Int(j).ToString;
|
||||
ctFloat: Result := cursor.Float(j).ToString('F' + decimals);
|
||||
ctStr: Result := cursor.Str(j);
|
||||
ctBool: Result := cursor.Bool(j).ToString;
|
||||
valid[row] := false;
|
||||
end;
|
||||
end;
|
||||
|
||||
// сканируем head
|
||||
for var i := 0 to headRows - 1 do
|
||||
ScanRow(i);
|
||||
|
||||
// сканируем tail
|
||||
if rowCount > headRows then
|
||||
for var i := rowCount - tailRows to rowCount - 1 do
|
||||
if i >= headRows then
|
||||
ScanRow(i);
|
||||
|
||||
// 2. печать заголовков
|
||||
for var j := 0 to colCount - 1 do
|
||||
PABCSystem.Print(columns[j].Info.Name.PadRight(widths[j] + 2));
|
||||
PABCSystem.Println;
|
||||
|
||||
// 3. печать head
|
||||
for var i := 0 to headRows - 1 do
|
||||
begin
|
||||
cursor.MoveTo(i);
|
||||
for var j := 0 to colCount - 1 do
|
||||
PABCSystem.Print(FormatValue(j).PadRight(widths[j] + 2));
|
||||
PABCSystem.Println;
|
||||
end;
|
||||
|
||||
// 4. многоточие
|
||||
if headRows + tailRows < rowCount then
|
||||
begin
|
||||
for var j := 0 to colCount - 1 do
|
||||
PABCSystem.Print('...'.PadRight(widths[j] + 2));
|
||||
PABCSystem.Println;
|
||||
end;
|
||||
|
||||
// 5. печать tail
|
||||
for var i := rowCount - tailRows to rowCount - 1 do
|
||||
if i >= headRows then
|
||||
begin
|
||||
cursor.MoveTo(i);
|
||||
for var j := 0 to colCount - 1 do
|
||||
PABCSystem.Print(FormatValue(j).PadRight(widths[j] + 2));
|
||||
PABCSystem.Println;
|
||||
end;
|
||||
end;}
|
||||
row += 1;
|
||||
end;
|
||||
|
||||
var res := new DataFrame;
|
||||
for var i := 0 to columns.Count - 1 do
|
||||
res.AddColumnView(columns[i]);
|
||||
|
||||
res.AddIntColumn(name, data, valid);
|
||||
|
||||
Result := res;
|
||||
Result.AssertSchemaConsistent;
|
||||
end;
|
||||
|
||||
|
||||
procedure DataFrame.PrintPreview(maxRows: integer; headRows: integer; decimals: integer);
|
||||
begin
|
||||
|
|
|
|||
949
bin/Lib/LinearAlgebraML.pas
Normal file
949
bin/Lib/LinearAlgebraML.pas
Normal file
|
|
@ -0,0 +1,949 @@
|
|||
unit LinearAlgebraML;
|
||||
|
||||
interface
|
||||
|
||||
type
|
||||
Vector = class
|
||||
private
|
||||
|
||||
static procedure CheckSameLength(const a, b: Vector);
|
||||
static procedure CheckNonEmpty(const v: Vector);
|
||||
procedure SetData(i: integer; value: real) := data[i] := value;
|
||||
public
|
||||
data: array of real;
|
||||
property Length: integer read data.Length;
|
||||
property Item[i: integer]: real read data[i] write SetData; default;
|
||||
|
||||
constructor Create(n: integer);
|
||||
constructor Create(values: array of real);
|
||||
constructor Create(values: array of integer);
|
||||
|
||||
function Clone: Vector;
|
||||
|
||||
static function operator +(a, b: Vector): Vector;
|
||||
static function operator -(a, b: Vector): Vector;
|
||||
|
||||
static function operator *(alpha: real; v: Vector): Vector;
|
||||
static function operator *(v: Vector; alpha: real): Vector;
|
||||
static function operator /(v: Vector; alpha: real): Vector;
|
||||
|
||||
// Dot product
|
||||
static function operator *(a, b: Vector): real;
|
||||
|
||||
static function operator +=(a: Vector; b: Vector): Vector;
|
||||
static function operator -=(a: Vector; b: Vector): Vector;
|
||||
static function operator *=(a: Vector; alpha: real): Vector;
|
||||
|
||||
static function operator implicit(a: array of real): Vector := new Vector(a);
|
||||
static function operator implicit(a: array of integer): Vector := new Vector(a);
|
||||
|
||||
// ---------- Основные методы ----------
|
||||
function Sum: real;
|
||||
function Mean: real;
|
||||
function Norm2: real;
|
||||
function Norm: real;
|
||||
|
||||
// ---------- Сервисные методы ----------
|
||||
function ToString: string; override := $'{data}';
|
||||
procedure Print := data.Print;
|
||||
procedure Println := data.Println;
|
||||
end;
|
||||
|
||||
Matrix = class
|
||||
private
|
||||
static procedure CheckSameSize(A, B: Matrix);
|
||||
static procedure CheckMulSize(A, B: Matrix);
|
||||
static procedure CheckVecSize(A: Matrix; x: Vector);
|
||||
|
||||
procedure SetData(i, j: integer; value: real) := data[i, j] := value;
|
||||
public
|
||||
data: array[,] of real;
|
||||
property Rows: integer read data.GetLength(0);
|
||||
property Cols: integer read data.GetLength(1);
|
||||
|
||||
property Item[i, j: integer]: real
|
||||
read data[i, j] write SetData; default;
|
||||
|
||||
constructor Create(r, c: integer);
|
||||
constructor Create(values: array[,] of real);
|
||||
|
||||
function Clone: Matrix;
|
||||
|
||||
static function operator implicit(a: array [,] of real): 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)))));
|
||||
|
||||
// ---------- value operators ----------
|
||||
static function operator +(A, B: Matrix): Matrix;
|
||||
static function operator -(A, B: Matrix): Matrix;
|
||||
|
||||
static function operator *(A: Matrix; x: Vector): Vector;
|
||||
static function operator *(A, B: Matrix): Matrix;
|
||||
|
||||
static function operator *(alpha: real; A: Matrix): Matrix;
|
||||
static function operator *(A: Matrix; alpha: real): Matrix;
|
||||
|
||||
// ---------- in-place operators ----------
|
||||
static function operator +=(A, B: Matrix): Matrix;
|
||||
static function operator -=(A, B: Matrix): Matrix;
|
||||
static function operator *=(A: Matrix; alpha: real): Matrix;
|
||||
|
||||
// ---------- Основные методы ----------
|
||||
/// Возвращает транспонированную матрицу Aᵀ
|
||||
function Transpose: Matrix;
|
||||
/// Проверяет, является ли матрица симметричной с заданным допуском.
|
||||
function IsSymmetric(tol: real := 1e-12): boolean;
|
||||
/// Вычисляет собственные значения и собственные векторы вещественной квадратной симметричной матрицы.
|
||||
///
|
||||
/// Возвращает:
|
||||
/// - values — вектор длины n, содержащий собственные значения,
|
||||
/// отсортированные по убыванию;
|
||||
/// - vectors — матрицу n × n, столбцы которой являются
|
||||
/// ортонормированными собственными векторами.
|
||||
///
|
||||
/// Выполняется разложение:
|
||||
/// A = V * diag(values) * Vᵀ
|
||||
///
|
||||
/// Метод основан на итерациях Якоби.
|
||||
function EigenSymmetric(tol: real := 1e-12; maxIter: integer := 100): (Vector, Matrix);
|
||||
/// Выполняет анализ главных компонент (PCA) по матрице данных.
|
||||
///
|
||||
/// Строки интерпретируются как объекты, столбцы — как признаки.
|
||||
/// Параметр k задаёт число главных компонент (1 ≤ k ≤ Cols).
|
||||
///
|
||||
/// Возвращает:
|
||||
/// - components — матрицу Cols × k главных компонент;
|
||||
/// - variances — соответствующие дисперсии.
|
||||
///
|
||||
/// Компоненты ортонормированы и отсортированы по убыванию дисперсии.
|
||||
function PCA(k: integer): (Matrix, Vector);
|
||||
|
||||
// ---------- Сервисные методы ----------
|
||||
function ToString: string; override := $'{data}';
|
||||
procedure Print := data.Print;
|
||||
procedure Println := data.Println;
|
||||
|
||||
function GetRow(i: integer): Vector;
|
||||
function GetCol(j: integer): Vector;
|
||||
|
||||
// ---------- Статические методы ----------
|
||||
/// Возвращает единичную матрицу размера n
|
||||
static function Identity(n: integer): Matrix;
|
||||
end;
|
||||
|
||||
/// Решает систему линейных уравнений A * x = b с помощью LU-разложения с частичным выбором главного элемента.
|
||||
/// A должна быть квадратной матрицей.
|
||||
///
|
||||
/// Временная сложность: O(n³).
|
||||
///
|
||||
/// Вызывает исключение, если A вырождена или размеры не согласованы
|
||||
function Solve(A: Matrix; b: Vector): Vector;
|
||||
|
||||
/// Решает систему линейных уравнений A * x = b, предполагая, что матрица A является
|
||||
/// симметричной положительно определённой (SPD).
|
||||
/// Используется разложение Холецкого (A = L * L^T).
|
||||
///
|
||||
/// Временная сложность: O(n³ / 3).
|
||||
///
|
||||
/// Вызывает исключение, если матрица A не симметричная положительно определенная
|
||||
/// или если размеры A и b не согласованы.
|
||||
function SolveSPD(A: Matrix; b: Vector): Vector;
|
||||
|
||||
/// Решает систему линейных уравнений A * x = b.
|
||||
///
|
||||
/// Метод автоматически выбирается для обеспечения
|
||||
/// максимальной производительности и численной устойчивости.
|
||||
///
|
||||
/// Вызывает исключение, если система неразрешима
|
||||
/// или размеры A и b не согласованы.
|
||||
function SolveAuto(A: Matrix; b: Vector): Vector;
|
||||
|
||||
|
||||
/// Решает систему A * x ≈ b методом ridge-регрессии с подавлением неустойчивых направлений.
|
||||
///
|
||||
/// Матрица A имеет размер m × n: m — число уравнений (строк A), n — число неизвестных (столбцов A).
|
||||
///
|
||||
/// Метод используется, когда обычное решение (lambda = 0) даёт слишком большие коэффициенты из-за
|
||||
/// сильной зависимости между столбцами матрицы A или из-за шума в данных.
|
||||
///
|
||||
/// Параметр lambda задаёт степень регуляризации:
|
||||
/// - lambda = 0
|
||||
/// эквивалентно обычному решению по методу наименьших квадратов;
|
||||
/// - lambda ≈ 1e-6 .. 1e-3
|
||||
/// слабая регуляризация;
|
||||
/// - lambda ≈ 1e-2 .. 1
|
||||
/// умеренная регуляризация (типично для практических и ML-задач);
|
||||
/// - lambda > 1
|
||||
/// сильная регуляризация, коэффициенты заметно уменьшаются.
|
||||
///
|
||||
/// Если данные хорошо обусловлены, используйте lambda = 0
|
||||
/// При шумных или коррелированных данных часто подходят значения 0.01 .. 0.1
|
||||
/// Увеличивайте lambda, если коэффициенты x становятся слишком большими.
|
||||
///
|
||||
/// Требуется, чтобы число уравнений было не меньше числа неизвестных (m ≥ n).
|
||||
/// Входные A и b изменяются. Возвращаемый вектор x имеет длину n.
|
||||
function SolveRidge(A: Matrix; b: Vector; lambda: real := 0): Vector;
|
||||
|
||||
implementation
|
||||
|
||||
uses System;
|
||||
|
||||
//-----------------------------
|
||||
// Vector
|
||||
//-----------------------------
|
||||
|
||||
constructor Vector.Create(n: integer);
|
||||
begin
|
||||
if n < 0 then
|
||||
raise new ArgumentOutOfRangeException('n', 'Vector length must be non-negative');
|
||||
data := new real[n];
|
||||
end;
|
||||
|
||||
constructor Vector.Create(values: array of real);
|
||||
begin
|
||||
if values = nil then
|
||||
raise new ArgumentNullException('values');
|
||||
data := Copy(values);
|
||||
end;
|
||||
|
||||
constructor Vector.Create(values: array of integer);
|
||||
begin
|
||||
if values = nil then
|
||||
raise new ArgumentNullException('values');
|
||||
data := values.Select(x -> real(x)).ToArray;
|
||||
end;
|
||||
|
||||
function Vector.Clone: Vector;
|
||||
begin
|
||||
Result := new Vector(data);
|
||||
end;
|
||||
|
||||
static procedure Vector.CheckSameLength(a, b: Vector);
|
||||
begin
|
||||
if a.Length <> b.Length then
|
||||
raise new ArgumentException(
|
||||
$'Vector length mismatch: {a.Length} vs {b.Length}');
|
||||
end;
|
||||
|
||||
static procedure Vector.CheckNonEmpty(v: Vector);
|
||||
begin
|
||||
if v.Length = 0 then
|
||||
raise new ArgumentException('Vector is empty');
|
||||
end;
|
||||
|
||||
static function Vector.operator +(a, b: Vector): Vector;
|
||||
begin
|
||||
CheckSameLength(a, b);
|
||||
Result := new Vector(a.Length);
|
||||
for var i := 0 to a.Length - 1 do
|
||||
Result.data[i] := a.data[i] + b.data[i];
|
||||
end;
|
||||
|
||||
static function Vector.operator -(a, b: Vector): Vector;
|
||||
begin
|
||||
CheckSameLength(a, b);
|
||||
Result := new Vector(a.Length);
|
||||
for var i := 0 to a.Length - 1 do
|
||||
Result.data[i] := a.data[i] - b.data[i];
|
||||
end;
|
||||
|
||||
static function Vector.operator *(alpha: real; v: Vector): Vector;
|
||||
begin
|
||||
Result := v * alpha;
|
||||
end;
|
||||
|
||||
static function Vector.operator *(v: Vector; alpha: real): Vector;
|
||||
begin
|
||||
Result := new Vector(v.Length);
|
||||
for var i := 0 to v.Length - 1 do
|
||||
Result.data[i] := alpha * v.data[i];
|
||||
end;
|
||||
|
||||
static function Vector.operator /(v: Vector; alpha: real): Vector;
|
||||
begin
|
||||
if alpha = 0.0 then
|
||||
raise new DivideByZeroException('Division by zero in Vector / scalar');
|
||||
Result := new Vector(v.Length);
|
||||
var inv := 1.0 / alpha;
|
||||
for var i := 0 to v.Length - 1 do
|
||||
Result.data[i] := v.data[i] * inv;
|
||||
end;
|
||||
|
||||
static function Vector.operator *(a, b: Vector): real;
|
||||
begin
|
||||
CheckSameLength(a, b);
|
||||
var s := 0.0;
|
||||
for var i := 0 to a.Length - 1 do
|
||||
s += a.data[i] * b.data[i];
|
||||
Result := s;
|
||||
end;
|
||||
|
||||
static function Vector.operator +=(a, b: Vector): Vector;
|
||||
begin
|
||||
CheckSameLength(a, b);
|
||||
for var i := 0 to a.Length - 1 do
|
||||
a.data[i] += b.data[i];
|
||||
Result := a;
|
||||
end;
|
||||
|
||||
static function Vector.operator -=(a, b: Vector): Vector;
|
||||
begin
|
||||
CheckSameLength(a, b);
|
||||
for var i := 0 to a.Length - 1 do
|
||||
a.data[i] -= b.data[i];
|
||||
Result := a;
|
||||
end;
|
||||
|
||||
static function Vector.operator *=(a: Vector; alpha: real): Vector;
|
||||
begin
|
||||
for var i := 0 to a.Length - 1 do
|
||||
a.data[i] *= alpha;
|
||||
Result := a;
|
||||
end;
|
||||
|
||||
function Vector.Sum: real;
|
||||
begin
|
||||
var s := 0.0;
|
||||
for var i := 0 to Length - 1 do
|
||||
s += data[i];
|
||||
Result := s;
|
||||
end;
|
||||
|
||||
function Vector.Mean: real;
|
||||
begin
|
||||
CheckNonEmpty(Self);
|
||||
Result := Sum / Length;
|
||||
end;
|
||||
|
||||
function Vector.Norm2: real;
|
||||
begin
|
||||
Result := Self * Self;
|
||||
end;
|
||||
|
||||
function Vector.Norm: real;
|
||||
begin
|
||||
Result := Sqrt(Norm2);
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// Matrix
|
||||
//-----------------------------
|
||||
constructor Matrix.Create(r, c: integer);
|
||||
begin
|
||||
if (r < 0) or (c < 0) then
|
||||
raise new ArgumentOutOfRangeException('Matrix size must be non-negative');
|
||||
data := new real[r, c];
|
||||
end;
|
||||
|
||||
constructor Matrix.Create(values: array[,] of real);
|
||||
begin
|
||||
if values = nil then
|
||||
raise new ArgumentNullException('values');
|
||||
data := Copy(values);
|
||||
end;
|
||||
|
||||
function Matrix.Clone: Matrix;
|
||||
begin
|
||||
Result := new Matrix(data);
|
||||
end;
|
||||
|
||||
static procedure Matrix.CheckSameSize(A, B: Matrix);
|
||||
begin
|
||||
if (A.Rows <> B.Rows) or (A.Cols <> B.Cols) then
|
||||
raise new ArgumentException(
|
||||
$'Matrix size mismatch: {A.Rows}x{A.Cols} vs {B.Rows}x{B.Cols}');
|
||||
end;
|
||||
|
||||
static procedure Matrix.CheckMulSize(A, B: Matrix);
|
||||
begin
|
||||
if A.Cols <> B.Rows then
|
||||
raise new ArgumentException(
|
||||
$'Matrix multiply size mismatch: {A.Rows}x{A.Cols} * {B.Rows}x{B.Cols}');
|
||||
end;
|
||||
|
||||
static procedure Matrix.CheckVecSize(A: Matrix; x: Vector);
|
||||
begin
|
||||
if A.Cols <> x.Length then
|
||||
raise new ArgumentException(
|
||||
$'Matrix-vector size mismatch: {A.Rows}x{A.Cols} * {x.Length}');
|
||||
end;
|
||||
|
||||
static function Matrix.operator +(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckSameSize(A, B);
|
||||
Result := new Matrix(A.Rows, A.Cols);
|
||||
for var i := 0 to A.Rows - 1 do
|
||||
for var j := 0 to A.Cols - 1 do
|
||||
Result.data[i, j] := A.data[i, j] + B.data[i, j];
|
||||
end;
|
||||
|
||||
static function Matrix.operator -(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckSameSize(A, B);
|
||||
Result := new Matrix(A.Rows, A.Cols);
|
||||
for var i := 0 to A.Rows - 1 do
|
||||
for var j := 0 to A.Cols - 1 do
|
||||
Result.data[i, j] := A.data[i, j] - B.data[i, j];
|
||||
end;
|
||||
|
||||
static function Matrix.operator *(alpha: real; A: Matrix): Matrix;
|
||||
begin
|
||||
Result := A * alpha;
|
||||
end;
|
||||
|
||||
static function Matrix.operator *(A: Matrix; alpha: real): Matrix;
|
||||
begin
|
||||
Result := new Matrix(A.Rows, A.Cols);
|
||||
for var i := 0 to A.Rows - 1 do
|
||||
for var j := 0 to A.Cols - 1 do
|
||||
Result.data[i, j] := alpha * A.data[i, j];
|
||||
end;
|
||||
|
||||
static function Matrix.operator *(A: Matrix; x: Vector): Vector;
|
||||
begin
|
||||
CheckVecSize(A, x);
|
||||
Result := new Vector(A.Rows);
|
||||
for var i := 0 to A.Rows - 1 do
|
||||
begin
|
||||
var s := 0.0;
|
||||
for var j := 0 to A.Cols - 1 do
|
||||
s += A.data[i, j] * x[j];
|
||||
Result[i] := s;
|
||||
end;
|
||||
end;
|
||||
|
||||
static function Matrix.operator *(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckMulSize(A, B);
|
||||
Result := new Matrix(A.Rows, B.Cols);
|
||||
for var i := 0 to A.Rows - 1 do
|
||||
for var k := 0 to A.Cols - 1 do
|
||||
begin
|
||||
var aik := A.data[i, k];
|
||||
if aik <> 0.0 then
|
||||
for var j := 0 to B.Cols - 1 do
|
||||
Result.data[i, j] += aik * B.data[k, j];
|
||||
end;
|
||||
end;
|
||||
|
||||
static function Matrix.operator +=(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckSameSize(A, B);
|
||||
for var i := 0 to A.Rows - 1 do
|
||||
for var j := 0 to A.Cols - 1 do
|
||||
A.data[i, j] += B.data[i, j];
|
||||
Result := A;
|
||||
end;
|
||||
|
||||
static function Matrix.operator -=(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckSameSize(A, B);
|
||||
for var i := 0 to A.Rows - 1 do
|
||||
for var j := 0 to A.Cols - 1 do
|
||||
A.data[i, j] -= B.data[i, j];
|
||||
Result := A;
|
||||
end;
|
||||
|
||||
static function Matrix.operator *=(A: Matrix; alpha: real): Matrix;
|
||||
begin
|
||||
for var i := 0 to A.Rows - 1 do
|
||||
for var j := 0 to A.Cols - 1 do
|
||||
A.data[i, j] *= alpha;
|
||||
Result := A;
|
||||
end;
|
||||
|
||||
function Matrix.Transpose: Matrix;
|
||||
begin
|
||||
Result := new Matrix(Cols, Rows);
|
||||
for var i := 0 to Rows - 1 do
|
||||
for var j := 0 to Cols - 1 do
|
||||
Result.data[j, i] := data[i, j];
|
||||
end;
|
||||
|
||||
function Matrix.GetRow(i: integer): Vector;
|
||||
begin
|
||||
if (i < 0) or (i >= Rows) then
|
||||
raise new ArgumentOutOfRangeException('i');
|
||||
Result := new Vector(Cols);
|
||||
for var j := 0 to Cols - 1 do
|
||||
Result[j] := data[i, j];
|
||||
end;
|
||||
|
||||
function Matrix.GetCol(j: integer): Vector;
|
||||
begin
|
||||
if (j < 0) or (j >= Cols) then
|
||||
raise new ArgumentOutOfRangeException('j');
|
||||
Result := new Vector(Rows);
|
||||
for var i := 0 to Rows - 1 do
|
||||
Result[i] := data[i, j];
|
||||
end;
|
||||
|
||||
static function Matrix.Identity(n: integer): Matrix;
|
||||
begin
|
||||
if n < 0 then
|
||||
raise new ArgumentOutOfRangeException('n', 'Matrix size must be non-negative');
|
||||
|
||||
Result := new Matrix(n, n);
|
||||
for var i := 0 to n - 1 do
|
||||
Result[i, i] := 1.0;
|
||||
end;
|
||||
|
||||
function Matrix.IsSymmetric(tol: real): boolean;
|
||||
begin
|
||||
if Rows <> Cols then
|
||||
begin
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
|
||||
for var i := 0 to Rows - 1 do
|
||||
for var j := i + 1 to Cols - 1 do
|
||||
if Abs(data[i, j] - data[j, i]) > tol then
|
||||
begin
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
|
||||
Result := true;
|
||||
end;
|
||||
|
||||
function Matrix.EigenSymmetric(tol: real; maxIter: integer): (Vector, Matrix);
|
||||
begin
|
||||
if Rows <> Cols then
|
||||
raise new ArgumentException('Matrix must be square');
|
||||
|
||||
if not IsSymmetric(tol) then
|
||||
raise new ArgumentException('Matrix must be symmetric');
|
||||
|
||||
var n := Rows;
|
||||
|
||||
var M := Clone;
|
||||
var V := Matrix.Identity(n);
|
||||
|
||||
for var iter := 0 to maxIter - 1 do
|
||||
begin
|
||||
// --- Норма вне-диагонали
|
||||
var off := 0.0;
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := i + 1 to n - 1 do
|
||||
off += M[i, j] * M[i, j];
|
||||
|
||||
if Sqrt(off) < tol then
|
||||
break;
|
||||
|
||||
// --- Поиск максимального вне-диагонального
|
||||
var p := 0;
|
||||
var q := 1;
|
||||
var maxVal := 0.0;
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := i + 1 to n - 1 do
|
||||
begin
|
||||
var v1 := Abs(M[i, j]);
|
||||
if v1 > maxVal then
|
||||
begin
|
||||
maxVal := v1;
|
||||
p := i;
|
||||
q := j;
|
||||
end;
|
||||
end;
|
||||
|
||||
var app := M[p, p];
|
||||
var aqq := M[q, q];
|
||||
var apq := M[p, q];
|
||||
|
||||
if Abs(apq) < tol then
|
||||
continue;
|
||||
|
||||
// --- Устойчивая формула вращения
|
||||
var tau := (aqq - app) / (2.0 * apq);
|
||||
|
||||
var t: real;
|
||||
if tau >= 0.0 then
|
||||
t := 1.0 / (tau + Sqrt(1.0 + tau * tau))
|
||||
else
|
||||
t := -1.0 / (-tau + Sqrt(1.0 + tau * tau));
|
||||
|
||||
var c := 1.0 / Sqrt(1.0 + t * t);
|
||||
var s := t * c;
|
||||
|
||||
// --- Обновление M
|
||||
for var k := 0 to n - 1 do
|
||||
begin
|
||||
if (k <> p) and (k <> q) then
|
||||
begin
|
||||
var mkp := M[k, p];
|
||||
var mkq := M[k, q];
|
||||
|
||||
var newkp := c * mkp - s * mkq;
|
||||
var newkq := s * mkp + c * mkq;
|
||||
|
||||
M[k, p] := newkp;
|
||||
M[p, k] := newkp;
|
||||
|
||||
M[k, q] := newkq;
|
||||
M[q, k] := newkq;
|
||||
end;
|
||||
end;
|
||||
|
||||
M[p, p] := app - t * apq;
|
||||
M[q, q] := aqq + t * apq;
|
||||
M[p, q] := 0.0;
|
||||
M[q, p] := 0.0;
|
||||
|
||||
// --- Обновление V
|
||||
for var k := 0 to n - 1 do
|
||||
begin
|
||||
var vkp := V[k, p];
|
||||
var vkq := V[k, q];
|
||||
|
||||
V[k, p] := c * vkp - s * vkq;
|
||||
V[k, q] := s * vkp + c * vkq;
|
||||
end;
|
||||
end;
|
||||
|
||||
// --- Eigenvalues
|
||||
var values := new Vector(n);
|
||||
for var i := 0 to n - 1 do
|
||||
values[i] := M[i, i];
|
||||
|
||||
// --- Сортировка по убыванию
|
||||
for var i := 0 to n - 2 do
|
||||
for var j := i + 1 to n - 1 do
|
||||
if values[j] > values[i] then
|
||||
begin
|
||||
var tmp := values[i];
|
||||
values[i] := values[j];
|
||||
values[j] := tmp;
|
||||
|
||||
for var k := 0 to n - 1 do
|
||||
begin
|
||||
var tv := V[k, i];
|
||||
V[k, i] := V[k, j];
|
||||
V[k, j] := tv;
|
||||
end;
|
||||
end;
|
||||
|
||||
// --- Нормализация знаков (для стабильности PCA)
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var maxAbs := 0.0;
|
||||
var idx := 0;
|
||||
|
||||
for var k := 0 to n - 1 do
|
||||
begin
|
||||
var v1 := Abs(V[k, i]);
|
||||
if v1 > maxAbs then
|
||||
begin
|
||||
maxAbs := v1;
|
||||
idx := k;
|
||||
end;
|
||||
end;
|
||||
|
||||
if V[idx, i] < 0.0 then
|
||||
for var k := 0 to n - 1 do
|
||||
V[k, i] := -V[k, i];
|
||||
end;
|
||||
|
||||
Result := (values, V);
|
||||
end;
|
||||
|
||||
function Matrix.PCA(k: integer): (Matrix, Vector);
|
||||
begin
|
||||
var m := Rows;
|
||||
var n := Cols;
|
||||
|
||||
if k < 1 then
|
||||
raise new ArgumentException('k must be >= 1');
|
||||
|
||||
if k > n then
|
||||
raise new ArgumentException('k cannot exceed number of features');
|
||||
|
||||
if m < 2 then
|
||||
raise new ArgumentException('At least two samples required');
|
||||
|
||||
// --- Центрирование
|
||||
var Xc := Clone;
|
||||
|
||||
for var j := 0 to n - 1 do
|
||||
begin
|
||||
var mean := 0.0;
|
||||
for var i := 0 to m - 1 do
|
||||
mean += Xc[i, j];
|
||||
mean /= m;
|
||||
|
||||
for var i := 0 to m - 1 do
|
||||
Xc[i, j] -= mean;
|
||||
end;
|
||||
|
||||
// --- Ковариационная матрица
|
||||
var C := new Matrix(n, n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := i to n - 1 do
|
||||
begin
|
||||
var s := 0.0;
|
||||
for var t := 0 to m - 1 do
|
||||
s += Xc[t, i] * Xc[t, j];
|
||||
|
||||
s /= (m - 1);
|
||||
|
||||
C[i, j] := s;
|
||||
C[j, i] := s;
|
||||
end;
|
||||
|
||||
// --- Eigen
|
||||
var (values, V) := C.EigenSymmetric;
|
||||
|
||||
// --- Выбор k компонент
|
||||
var components := new Matrix(n, k);
|
||||
var variances := new Vector(k);
|
||||
|
||||
for var j := 0 to k - 1 do
|
||||
begin
|
||||
variances[j] := values[j];
|
||||
for var i := 0 to n - 1 do
|
||||
components[i, j] := V[i, j];
|
||||
end;
|
||||
|
||||
Result := (components, variances);
|
||||
end;
|
||||
|
||||
|
||||
|
||||
// Helper
|
||||
function Cholesky(A: Matrix): Matrix;
|
||||
begin
|
||||
if A.Rows <> A.Cols then
|
||||
raise new ArgumentException('Matrix must be square for Cholesky');
|
||||
|
||||
var n := A.Rows;
|
||||
var L := new Matrix(n, n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := 0 to i do
|
||||
begin
|
||||
var s := A[i, j];
|
||||
for var k := 0 to j - 1 do
|
||||
s -= L[i, k] * L[j, k];
|
||||
|
||||
if i = j then
|
||||
begin
|
||||
if s <= 0.0 then
|
||||
raise new InvalidOperationException('Matrix is not SPD');
|
||||
L[i, i] := Sqrt(s);
|
||||
end
|
||||
else
|
||||
L[i, j] := s / L[j, j];
|
||||
end;
|
||||
|
||||
Result := L;
|
||||
end;
|
||||
|
||||
// Helper
|
||||
function LUDecompose(A: Matrix): (Matrix, array of integer);
|
||||
begin
|
||||
if A.Rows <> A.Cols then
|
||||
raise new ArgumentException('Matrix must be square for LU');
|
||||
|
||||
var n := A.Rows;
|
||||
var LU := A.Clone;
|
||||
var p := new integer[n];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
p[i] := i;
|
||||
|
||||
for var k := 0 to n - 1 do
|
||||
begin
|
||||
var maxRow := k;
|
||||
var maxVal := Abs(LU[k, k]);
|
||||
|
||||
for var i := k + 1 to n - 1 do
|
||||
if Abs(LU[i, k]) > maxVal then
|
||||
begin
|
||||
maxVal := Abs(LU[i, k]);
|
||||
maxRow := i;
|
||||
end;
|
||||
|
||||
if maxVal = 0.0 then
|
||||
raise new InvalidOperationException('Matrix is singular');
|
||||
|
||||
if maxRow <> k then
|
||||
begin
|
||||
for var j := 0 to n - 1 do
|
||||
begin
|
||||
var t := LU[k, j];
|
||||
LU[k, j] := LU[maxRow, j];
|
||||
LU[maxRow, j] := t;
|
||||
end;
|
||||
|
||||
var tp := p[k];
|
||||
p[k] := p[maxRow];
|
||||
p[maxRow] := tp;
|
||||
end;
|
||||
|
||||
for var i := k + 1 to n - 1 do
|
||||
begin
|
||||
LU[i, k] /= LU[k, k];
|
||||
for var j := k + 1 to n - 1 do
|
||||
LU[i, j] -= LU[i, k] * LU[k, j];
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := (LU, p);
|
||||
end;
|
||||
|
||||
// Helper
|
||||
function Permute(b: Vector; p: array of integer): Vector;
|
||||
begin
|
||||
var n := b.Length;
|
||||
var r := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
r[i] := b[p[i]];
|
||||
|
||||
Result := r;
|
||||
end;
|
||||
|
||||
// Helper
|
||||
function SolveLowerTriangularUnit(LU: Matrix; b: Vector): Vector;
|
||||
begin
|
||||
var n := LU.Rows;
|
||||
var y := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var s := b[i];
|
||||
for var j := 0 to i - 1 do
|
||||
s -= LU[i, j] * y[j];
|
||||
y[i] := s;
|
||||
end;
|
||||
|
||||
Result := y;
|
||||
end;
|
||||
|
||||
function SolveLowerTriangular(L: Matrix; b: Vector): Vector;
|
||||
begin
|
||||
var n := L.Rows;
|
||||
var y := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var s := b[i];
|
||||
for var j := 0 to i - 1 do
|
||||
s -= L[i, j] * y[j];
|
||||
y[i] := s / L[i, i];
|
||||
end;
|
||||
|
||||
Result := y;
|
||||
end;
|
||||
|
||||
|
||||
// Helper
|
||||
function SolveUpperTriangular(U: Matrix; y: Vector): Vector;
|
||||
begin
|
||||
var n := U.Rows;
|
||||
var x := new Vector(n);
|
||||
|
||||
for var i := n - 1 downto 0 do
|
||||
begin
|
||||
var s := y[i];
|
||||
for var j := i + 1 to n - 1 do
|
||||
s -= U[i, j] * x[j];
|
||||
x[i] := s / U[i, i];
|
||||
end;
|
||||
|
||||
Result := x;
|
||||
end;
|
||||
|
||||
function Solve(A: Matrix; b: Vector): Vector;
|
||||
begin
|
||||
if A.Rows <> A.Cols then
|
||||
raise new ArgumentException('Matrix A must be square');
|
||||
|
||||
if b.Length <> A.Rows then
|
||||
raise new ArgumentException('Vector size mismatch in Solve');
|
||||
|
||||
var (LU, p) := LUDecompose(A);
|
||||
var pb := Permute(b, p);
|
||||
var y := SolveLowerTriangularUnit(LU, pb);
|
||||
Result := SolveUpperTriangular(LU, y);
|
||||
end;
|
||||
|
||||
function SolveSPD(A: Matrix; b: Vector): Vector;
|
||||
begin
|
||||
if A.Rows <> A.Cols then
|
||||
raise new ArgumentException('Matrix A must be square');
|
||||
|
||||
if b.Length <> A.Rows then
|
||||
raise new ArgumentException('Vector size mismatch in SolveSPD');
|
||||
|
||||
var L := Cholesky(A);
|
||||
var y := SolveLowerTriangular(L, b);
|
||||
Result := SolveUpperTriangular(L.Transpose, y);
|
||||
end;
|
||||
|
||||
function SolveAuto(A: Matrix; b: Vector): Vector;
|
||||
begin
|
||||
try
|
||||
Result := SolveSPD(A, b);
|
||||
except
|
||||
Result := Solve(A, b);
|
||||
end;
|
||||
end;
|
||||
|
||||
function SolveRidge(A: Matrix; b: Vector; lambda: real): Vector;
|
||||
begin
|
||||
if lambda < 0.0 then
|
||||
raise new ArgumentException('lambda must be >= 0');
|
||||
|
||||
var m := A.Rows;
|
||||
var n := A.Cols;
|
||||
|
||||
if b.Length <> m then
|
||||
raise new ArgumentException('Vector length mismatch in SolveRidge');
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// 1. Compute AtA = A^T * A
|
||||
// ------------------------------------------------------------
|
||||
var AtA := new Matrix(n, n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := 0 to n - 1 do
|
||||
begin
|
||||
var s := 0.0;
|
||||
for var k := 0 to m - 1 do
|
||||
s += A[k, i] * A[k, j];
|
||||
AtA[i, j] := s;
|
||||
end;
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// 2. Add lambda * I
|
||||
// ------------------------------------------------------------
|
||||
if lambda <> 0.0 then
|
||||
for var i := 0 to n - 1 do
|
||||
AtA[i, i] += lambda;
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// 3. Compute Atb = A^T * b
|
||||
// ------------------------------------------------------------
|
||||
var Atb := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var s := 0.0;
|
||||
for var k := 0 to m - 1 do
|
||||
s += A[k, i] * b[k];
|
||||
Atb[i] := s;
|
||||
end;
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// 4. Solve SPD system
|
||||
// ------------------------------------------------------------
|
||||
Result := SolveSPD(AtA, Atb);
|
||||
end;
|
||||
|
||||
|
||||
|
||||
|
||||
end.
|
||||
729
bin/Lib/PreprocessorABC.pas
Normal file
729
bin/Lib/PreprocessorABC.pas
Normal file
|
|
@ -0,0 +1,729 @@
|
|||
// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
// PreprocessorABC v.1.0
|
||||
|
||||
{
|
||||
DataFrameABC
|
||||
↓
|
||||
PreprocessorABC
|
||||
├─ IPreprocessor
|
||||
├─ Scalers / Encoders / Imputer
|
||||
└─ Pipeline
|
||||
↓
|
||||
MLCore
|
||||
}
|
||||
|
||||
/// PreprocessorABC — модуль подготовки табличных данных для анализа данных и машинного обучения.
|
||||
/// Содержит типовые преобразования признаков с семантикой Fit / Transform.
|
||||
/// Работает совместно с DataFrameABC и не содержит моделей машинного обучения.
|
||||
unit PreprocessorABC;
|
||||
|
||||
interface
|
||||
|
||||
uses DataFrameABC;
|
||||
uses System;
|
||||
|
||||
type
|
||||
/// Базовый интерфейс шагов подготовки данных.
|
||||
/// Определяет семантику операций Fit и Transform
|
||||
IPreprocessor = interface
|
||||
/// Анализирует DataFrame и и сохраняет параметры шага
|
||||
function Fit(df: DataFrame): IPreprocessor;
|
||||
/// Применяет сохранённые параметры к DataFrame.
|
||||
/// Возвращает новый DataFrame
|
||||
function Transform(df: DataFrame): DataFrame;
|
||||
/// Выполняет Fit и Transform последовательно
|
||||
function FitTransform(df: DataFrame): DataFrame;
|
||||
end;
|
||||
|
||||
/// Приводит числовые столбцы к нулевому среднему и единичному стандартному отклонению.
|
||||
/// При Fit вычисляет среднее значение и стандартное отклонение столбцов.
|
||||
/// Применяет преобразование: x' = (x - mean) / std.
|
||||
/// Пропущенные значения (NA) сохраняются.
|
||||
StandardScaler = class(IPreprocessor)
|
||||
private
|
||||
cols: array of string;
|
||||
means: array of real;
|
||||
stds: array of real;
|
||||
fitted: boolean;
|
||||
public
|
||||
/// Создаёт StandardScaler для указанных числовых столбцов.
|
||||
constructor Create(params columns: array of string);
|
||||
/// Вычисляет среднее значение и стандартное отклонение для каждого столбца.
|
||||
function Fit(df: DataFrame): IPreprocessor;
|
||||
/// Возвращает DataFrame со стандартизованными числовыми столбцами.
|
||||
function Transform(df: DataFrame): DataFrame;
|
||||
/// Последовательно выполняет Fit и Transform.
|
||||
function FitTransform(df: DataFrame): DataFrame;
|
||||
end;
|
||||
|
||||
/// Приводит числовые столбцы к заданному диапазону значений
|
||||
/// При Fit вычисляет минимальное и максимальное значения столбцов
|
||||
/// Применяет преобразование: x' = (x - min) / (max - min)
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
MinMaxScaler = class(IPreprocessor)
|
||||
private
|
||||
cols: array of string;
|
||||
mins: array of real;
|
||||
maxs: array of real;
|
||||
fitted: boolean;
|
||||
public
|
||||
/// Создаёт MinMaxScaler для указанных столбцов
|
||||
constructor Create(params columns: array of string);
|
||||
|
||||
/// Вычисляет минимальные и максимальные значения столбцов
|
||||
function Fit(df: DataFrame): IPreprocessor;
|
||||
/// Применяет масштабирование к DataFrame
|
||||
/// Возвращает новый DataFrame
|
||||
function Transform(df: DataFrame): DataFrame;
|
||||
/// Последовательно выполняет Fit и Transform.
|
||||
function FitTransform(df: DataFrame): DataFrame;
|
||||
end;
|
||||
|
||||
/// Кодирует строковый категориальный столбец в числовые значения
|
||||
/// Категории фиксируются при Fit
|
||||
/// Работает только со строковыми столбцами
|
||||
LabelEncoder = class(IPreprocessor)
|
||||
private
|
||||
col: string;
|
||||
mapping: Dictionary<string, integer>;
|
||||
fitted: boolean;
|
||||
public
|
||||
/// Создаёт LabelEncoder для указанного столбца
|
||||
constructor Create(column: string);
|
||||
|
||||
/// Определяет множество категорий и сохраняет их коды
|
||||
function Fit(df: DataFrame): IPreprocessor;
|
||||
/// Заменяет категории их числовыми кодами
|
||||
/// Возвращает новый DataFrame
|
||||
function Transform(df: DataFrame): DataFrame;
|
||||
/// Выполняет Fit и Transform последовательно
|
||||
function FitTransform(df: DataFrame): DataFrame;
|
||||
end;
|
||||
|
||||
/// Кодирует строковый категориальный столбец в набор бинарных (one-hot) столбцов
|
||||
/// Категории фиксируются при Fit
|
||||
/// Неизвестные категории приводят к ошибке
|
||||
/// Пропущенные значения (NA) кодируются нулями
|
||||
OneHotEncoder = class(IPreprocessor)
|
||||
private
|
||||
col: string;
|
||||
categories: array of string;
|
||||
indexByValue: Dictionary<string, integer>;
|
||||
fitted: boolean;
|
||||
public
|
||||
/// Создаёт OneHotEncoder для указанного столбца
|
||||
constructor Create(column: string);
|
||||
|
||||
/// Определяет множество категорий столбца
|
||||
function Fit(df: DataFrame): IPreprocessor;
|
||||
/// Заменяет столбец набором бинарных столбцов
|
||||
/// Возвращает новый DataFrame
|
||||
function Transform(df: DataFrame): DataFrame;
|
||||
/// Выполняет Fit и Transform последовательно
|
||||
function FitTransform(df: DataFrame): DataFrame;
|
||||
end;
|
||||
|
||||
ImputeStrategy = (isMean, isConstant);
|
||||
|
||||
/// Заполняет пропущенные значения (NA) в числовых столбцах
|
||||
/// Поддерживает стратегии isMean и isConstant
|
||||
/// Работает только с Int и Float столбцами
|
||||
Imputer = class(IPreprocessor)
|
||||
private
|
||||
cols: array of string;
|
||||
strategy: ImputeStrategy;
|
||||
constants: array of object;
|
||||
means: array of real;
|
||||
fitted: boolean;
|
||||
public
|
||||
/// Создаёт Imputer с заданной стратегией заполнения
|
||||
constructor Create(strategy: ImputeStrategy; params columns: array of string);
|
||||
/// Создаёт Imputer с константной стратегией заполнения
|
||||
constructor Create(strategy: ImputeStrategy; value: object; params columns: array of string);
|
||||
|
||||
/// Вычисляет значения для заполнения пропусков
|
||||
function Fit(df: DataFrame): IPreprocessor;
|
||||
/// Заполняет пропущенные значения в DataFrame
|
||||
/// Возвращает новый DataFrame
|
||||
function Transform(df: DataFrame): DataFrame;
|
||||
/// Выполняет Fit и Transform последовательно
|
||||
function FitTransform(df: DataFrame): DataFrame;
|
||||
end;
|
||||
|
||||
|
||||
type
|
||||
/// Pipeline (конвейер) шагов подготовки данных.
|
||||
/// Выполняет шаги последовательно с семантикой Fit / Transform. Pipeline = class
|
||||
Pipeline = class
|
||||
private
|
||||
steps: List<IPreprocessor>;
|
||||
fitted: boolean;
|
||||
public
|
||||
constructor;
|
||||
/// Добавляет шаг в конец pipeline
|
||||
function Add(p: IPreprocessor): Pipeline;
|
||||
/// Обучает все шаги pipeline на DataFrame
|
||||
function Fit(df: DataFrame): Pipeline;
|
||||
/// Применяет обученный pipeline к DataFrame
|
||||
function Transform(df: DataFrame): DataFrame;
|
||||
/// Выполняет Fit и Transform последовательно
|
||||
function FitTransform(df: DataFrame): DataFrame;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
//-----------------------------
|
||||
// StandardScaler
|
||||
//-----------------------------
|
||||
|
||||
constructor StandardScaler.Create(params columns: array of string);
|
||||
begin
|
||||
if (columns = nil) or (columns.Length = 0) then
|
||||
raise new ArgumentException('StandardScaler: columns not specified');
|
||||
|
||||
cols := columns;
|
||||
fitted := false;
|
||||
end;
|
||||
|
||||
function StandardScaler.Fit(df: DataFrame): IPreprocessor;
|
||||
begin
|
||||
var n := cols.Length;
|
||||
SetLength(means, n);
|
||||
SetLength(stds, n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var colName := cols[i];
|
||||
var idx := df.Schema.IndexOf(colName);
|
||||
|
||||
var ct := df.Schema.ColumnTypeAt(idx);
|
||||
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
||||
raise new Exception($'StandardScaler: column "{colName}" is not numeric');
|
||||
|
||||
var sum := 0.0;
|
||||
var sum2 := 0.0;
|
||||
var cnt := 0;
|
||||
|
||||
var cur := df.GetCursor;
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
if not cur.IsValid(idx) then continue;
|
||||
var x := cur.Float(idx);
|
||||
sum += x;
|
||||
sum2 += x * x;
|
||||
cnt += 1;
|
||||
end;
|
||||
|
||||
if cnt = 0 then
|
||||
raise new Exception($'StandardScaler: column "{colName}" has no valid values');
|
||||
|
||||
means[i] := sum / cnt;
|
||||
var v := sum2 / cnt - means[i] * means[i];
|
||||
stds[i] := Sqrt(v);
|
||||
|
||||
if stds[i] = 0 then
|
||||
raise new Exception($'StandardScaler: zero variance in column "{colName}"');
|
||||
end;
|
||||
|
||||
fitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function StandardScaler.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('StandardScaler: not fitted');
|
||||
|
||||
var res := df;
|
||||
|
||||
for var i := 0 to cols.Length - 1 do
|
||||
begin
|
||||
var colName := cols[i];
|
||||
var idx := df.Schema.IndexOf(colName);
|
||||
var mean := means[i];
|
||||
var std := stds[i];
|
||||
|
||||
res := res.ReplaceColumnFloat(
|
||||
colName,
|
||||
c ->
|
||||
(if c.IsValid(idx)
|
||||
then (c.Float(idx) - mean) / std
|
||||
else real.NaN)
|
||||
);
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function StandardScaler.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// MinMaxScaler
|
||||
//-----------------------------
|
||||
|
||||
constructor MinMaxScaler.Create(params columns: array of string);
|
||||
begin
|
||||
if (columns = nil) or (columns.Length = 0) then
|
||||
raise new ArgumentException('MinMaxScaler: columns not specified');
|
||||
|
||||
cols := columns;
|
||||
fitted := false;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.Fit(df: DataFrame): IPreprocessor;
|
||||
begin
|
||||
var n := cols.Length;
|
||||
SetLength(mins, n);
|
||||
SetLength(maxs, n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var colName := cols[i];
|
||||
var idx := df.Schema.IndexOf(colName);
|
||||
|
||||
var ct := df.Schema.ColumnTypeAt(idx);
|
||||
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
||||
raise new Exception($'MinMaxScaler: column "{colName}" is not numeric');
|
||||
|
||||
var first := true;
|
||||
var minv, maxv: real;
|
||||
|
||||
var cur := df.GetCursor;
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
if not cur.IsValid(idx) then continue;
|
||||
var x := cur.Float(idx);
|
||||
|
||||
if first then
|
||||
begin
|
||||
minv := x;
|
||||
maxv := x;
|
||||
first := false;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if x < minv then minv := x;
|
||||
if x > maxv then maxv := x;
|
||||
end;
|
||||
end;
|
||||
|
||||
if first then
|
||||
raise new Exception($'MinMaxScaler: column "{colName}" has no valid values');
|
||||
|
||||
if minv = maxv then
|
||||
raise new Exception($'MinMaxScaler: constant column "{colName}"');
|
||||
|
||||
mins[i] := minv;
|
||||
maxs[i] := maxv;
|
||||
end;
|
||||
|
||||
fitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('MinMaxScaler: not fitted');
|
||||
|
||||
var res := df;
|
||||
|
||||
for var i := 0 to cols.Length - 1 do
|
||||
begin
|
||||
var colName := cols[i];
|
||||
var idx := df.Schema.IndexOf(colName);
|
||||
var minv := mins[i];
|
||||
var maxv := maxs[i];
|
||||
var scale := maxv - minv;
|
||||
|
||||
res := res.ReplaceColumnFloat(
|
||||
colName,
|
||||
c ->
|
||||
(if c.IsValid(idx)
|
||||
then (c.Float(idx) - minv) / scale
|
||||
else real.NaN)
|
||||
);
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// LabelEncoder
|
||||
//-----------------------------
|
||||
|
||||
constructor LabelEncoder.Create(column: string);
|
||||
begin
|
||||
if column = '' then
|
||||
raise new ArgumentException('LabelEncoder: column not specified');
|
||||
|
||||
col := column;
|
||||
fitted := false;
|
||||
end;
|
||||
|
||||
function LabelEncoder.Fit(df: DataFrame): IPreprocessor;
|
||||
begin
|
||||
var idx := df.Schema.IndexOf(col);
|
||||
|
||||
if df.Schema.ColumnTypeAt(idx) <> ColumnType.ctStr then
|
||||
raise new Exception($'LabelEncoder: column "{col}" is not string');
|
||||
|
||||
mapping := new Dictionary<string, integer>;
|
||||
|
||||
var cur := df.GetCursor;
|
||||
var nextId := 0;
|
||||
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
if not cur.IsValid(idx) then continue;
|
||||
|
||||
var s := cur.Str(idx);
|
||||
if not mapping.ContainsKey(s) then
|
||||
begin
|
||||
mapping[s] := nextId;
|
||||
nextId += 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
fitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function LabelEncoder.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('LabelEncoder: not fitted');
|
||||
|
||||
var idx := df.Schema.IndexOf(col);
|
||||
|
||||
Result := df.ReplaceColumnInt(
|
||||
col,
|
||||
c ->
|
||||
if not c.IsValid(idx) then
|
||||
raise new Exception('NA') // будет поймано как NA
|
||||
else
|
||||
begin
|
||||
var s := c.Str(idx);
|
||||
if not mapping.ContainsKey(s) then
|
||||
raise new Exception(
|
||||
$'LabelEncoder: unseen category "{s}"'
|
||||
);
|
||||
Result := mapping[s];
|
||||
end
|
||||
);
|
||||
end;
|
||||
|
||||
function LabelEncoder.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// OneHotEncoder
|
||||
//-----------------------------
|
||||
|
||||
constructor OneHotEncoder.Create(column: string);
|
||||
begin
|
||||
if column = '' then
|
||||
raise new ArgumentException('OneHotEncoder: column not specified');
|
||||
col := column;
|
||||
fitted := false;
|
||||
end;
|
||||
|
||||
function OneHotEncoder.Fit(df: DataFrame): IPreprocessor;
|
||||
begin
|
||||
var idx := df.Schema.IndexOf(col);
|
||||
if df.Schema.ColumnTypeAt(idx) <> ColumnType.ctStr then
|
||||
raise new Exception(
|
||||
$'OneHotEncoder: column "{col}" is not string or has no valid values'
|
||||
);
|
||||
|
||||
indexByValue := new Dictionary<string, integer>;
|
||||
var values := new List<string>;
|
||||
|
||||
var cur := df.GetCursor;
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
if not cur.IsValid(idx) then continue;
|
||||
var s := cur.Str(idx);
|
||||
if not indexByValue.ContainsKey(s) then
|
||||
begin
|
||||
indexByValue[s] := values.Count;
|
||||
values.Add(s);
|
||||
end;
|
||||
end;
|
||||
|
||||
categories := values.ToArray;
|
||||
fitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function OneHotEncoder.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('OneHotEncoder: not fitted');
|
||||
|
||||
var srcIdx := df.Schema.IndexOf(col);
|
||||
var catCount := categories.Length;
|
||||
|
||||
// === ШАГ 1. Проверка на unseen категории ===
|
||||
var cur := df.GetCursor;
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
if not cur.IsValid(srcIdx) then continue;
|
||||
|
||||
var s := cur.Str(srcIdx);
|
||||
if not indexByValue.ContainsKey(s) then
|
||||
raise new Exception($'OneHotEncoder: unseen category "{s}"');
|
||||
end;
|
||||
|
||||
// === ШАГ 2. Генерация one-hot столбцов ===
|
||||
var res := df;
|
||||
|
||||
for var j := 0 to catCount - 1 do
|
||||
begin
|
||||
var catIdx := j;
|
||||
var newName := col + '_' + categories[j];
|
||||
|
||||
var Encode: DataFrameCursor -> integer := c ->
|
||||
begin
|
||||
if not c.IsValid(srcIdx) then
|
||||
begin
|
||||
Result := 0;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if indexByValue[c.Str(srcIdx)] = catIdx then
|
||||
Result := 1
|
||||
else
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
res := res.AddDerivedIntColumn(newName, Encode);
|
||||
end;
|
||||
|
||||
// === ШАГ 3. Удаление исходного столбца ===
|
||||
res := res.Drop([srcIdx]);
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
function OneHotEncoder.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// Imputer
|
||||
//-----------------------------
|
||||
|
||||
constructor Imputer.Create(strategy: ImputeStrategy; params columns: array of string);
|
||||
begin
|
||||
if strategy <> isMean then
|
||||
raise new ArgumentException('Imputer: this constructor is for isMean');
|
||||
|
||||
if (columns = nil) or (columns.Length = 0) then
|
||||
raise new ArgumentException('Imputer: columns not specified');
|
||||
|
||||
self.strategy := strategy;
|
||||
self.cols := columns;
|
||||
self.constants := nil;
|
||||
fitted := false;
|
||||
end;
|
||||
|
||||
constructor Imputer.Create(strategy: ImputeStrategy; value: object; params columns: array of string);
|
||||
begin
|
||||
if strategy <> isConstant then
|
||||
raise new ArgumentException('Imputer: this constructor is for isConstant');
|
||||
|
||||
if (columns = nil) or (columns.Length = 0) then
|
||||
raise new ArgumentException('Imputer: columns not specified');
|
||||
|
||||
self.strategy := strategy;
|
||||
self.cols := columns;
|
||||
|
||||
// одна и та же константа для всех столбцов
|
||||
self.constants := new object[columns.Length];
|
||||
for var i := 0 to columns.Length - 1 do
|
||||
self.constants[i] := value;
|
||||
|
||||
fitted := false;
|
||||
end;
|
||||
|
||||
function Imputer.Fit(df: DataFrame): IPreprocessor;
|
||||
begin
|
||||
if strategy = isMean then
|
||||
begin
|
||||
SetLength(means, cols.Length);
|
||||
|
||||
for var i := 0 to cols.Length - 1 do
|
||||
begin
|
||||
var name := cols[i];
|
||||
var idx := df.Schema.IndexOf(name);
|
||||
var ct := df.Schema.ColumnTypeAt(idx);
|
||||
|
||||
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
||||
raise new Exception($'Imputer(mean): column "{name}" is not numeric');
|
||||
|
||||
var sum := 0.0;
|
||||
var cnt := 0;
|
||||
|
||||
var cur := df.GetCursor;
|
||||
while cur.MoveNext do
|
||||
if cur.IsValid(idx) then
|
||||
begin
|
||||
sum += cur.Float(idx);
|
||||
cnt += 1;
|
||||
end;
|
||||
|
||||
if cnt = 0 then
|
||||
raise new Exception($'Imputer(mean): column "{name}" has no valid values');
|
||||
|
||||
means[i] := sum / cnt;
|
||||
end;
|
||||
end;
|
||||
|
||||
fitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function Imputer.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('Imputer: not fitted');
|
||||
|
||||
var res := df;
|
||||
|
||||
for var i := 0 to cols.Length - 1 do
|
||||
begin
|
||||
var name := cols[i];
|
||||
var idx := df.Schema.IndexOf(name);
|
||||
var ct := df.Schema.ColumnTypeAt(idx);
|
||||
|
||||
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
||||
raise new Exception($'Imputer: column "{name}" is not numeric');
|
||||
|
||||
if strategy = isMean then
|
||||
begin
|
||||
var m := means[i];
|
||||
res := res.ReplaceColumnFloat(
|
||||
name,
|
||||
c -> (if c.IsValid(idx) then c.Float(idx) else m)
|
||||
);
|
||||
end
|
||||
else
|
||||
begin
|
||||
var v := constants[i];
|
||||
if v = nil then
|
||||
raise new Exception($'Imputer(constant): value is nil for column "{name}"');
|
||||
|
||||
if ct = ColumnType.ctInt then
|
||||
begin
|
||||
var k: integer;
|
||||
try
|
||||
k := integer(v);
|
||||
except
|
||||
on e: Exception do
|
||||
raise new Exception($'Imputer(constant): value type mismatch for column "{name}"');
|
||||
end;
|
||||
|
||||
res := res.ReplaceColumnInt(
|
||||
name,
|
||||
c -> (if c.IsValid(idx) then c.Int(idx) else k)
|
||||
);
|
||||
end
|
||||
else
|
||||
begin
|
||||
var r: real;
|
||||
try
|
||||
r := real(v);
|
||||
except
|
||||
on e: Exception do
|
||||
raise new Exception($'Imputer(constant): value type mismatch for column "{name}"');
|
||||
end;
|
||||
|
||||
res := res.ReplaceColumnFloat(
|
||||
name,
|
||||
c -> (if c.IsValid(idx) then c.Float(idx) else r)
|
||||
);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function Imputer.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// Pipeline
|
||||
//-----------------------------
|
||||
|
||||
constructor Pipeline.Create;
|
||||
begin
|
||||
steps := new List<IPreprocessor>;
|
||||
fitted := false;
|
||||
end;
|
||||
|
||||
function Pipeline.Add(p: IPreprocessor): Pipeline;
|
||||
begin
|
||||
if fitted then
|
||||
raise new Exception('Cannot add step after Fit');
|
||||
|
||||
steps.Add(p);
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function Pipeline.Fit(df: DataFrame): Pipeline;
|
||||
begin
|
||||
var current := df;
|
||||
|
||||
for var i := 0 to steps.Count - 1 do
|
||||
begin
|
||||
steps[i] := steps[i].Fit(current);
|
||||
current := steps[i].Transform(current);
|
||||
end;
|
||||
|
||||
fitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function Pipeline.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('Pipeline is not fitted');
|
||||
|
||||
var current := df;
|
||||
foreach var step in steps do
|
||||
current := step.Transform(current);
|
||||
|
||||
Result := current;
|
||||
end;
|
||||
|
||||
function Pipeline.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
Loading…
Reference in a new issue