From d8c0645ac0390b87a0ad7f2ddfe233b7c2b5fb63 Mon Sep 17 00:00:00 2001 From: Mikhalkovich Stanislav Date: Wed, 11 Feb 2026 14:24:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D1=83=D0=BB=D0=B8=20ML=20LinearAlgebraABC=20=D0=B8=20Pre?= =?UTF-8?q?processorABC.pas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Configuration/GlobalAssemblyInfo.cs | 2 +- Configuration/Version.defs | 4 +- .../StandardUnits/ML/DataFrameABC/df01.pas | 13 - .../StandardUnits/ML/DataFrameABC/df01a.pas | 7 - .../StandardUnits/ML/DataFrameABC/df02.pas | 13 - .../StandardUnits/ML/DataFrameABC/df03.pas | 13 - .../StandardUnits/ML/DataFrameABC/df04.pas | 13 - .../StandardUnits/ML/DataFrameABC/df05.pas | 13 - .../StandardUnits/ML/DataFrameABC/df06.pas | 16 - .../StandardUnits/ML/DataFrameABC/df07.pas | 16 - .../StandardUnits/ML/DataFrameABC/df08.pas | 16 - .../StandardUnits/ML/DataFrameABC/df09.pas | 18 - .../StandardUnits/ML/DataFrameABC/df10.pas | 17 - .../StandardUnits/ML/DataFrameABC/df11.pas | 16 - .../StandardUnits/ML/DataFrameABC/df12.pas | 16 - .../StandardUnits/ML/DataFrameABC/df13.pas | 16 - .../StandardUnits/ML/DataFrameABC/df14.pas | 16 - .../StandardUnits/ML/DataFrameABC/df15.pas | 20 - .../StandardUnits/ML/DataFrameABC/df16.pas | 20 - .../StandardUnits/ML/DataFrameABC/df17.pas | 20 - .../StandardUnits/ML/DataFrameABC/df18.pas | 13 - .../StandardUnits/ML/DataFrameABC/df19.pas | 16 - .../StandardUnits/ML/DataFrameABC/df20.pas | 21 - .../StandardUnits/ML/DataFrameABC/df21.pas | 18 - .../StandardUnits/ML/DataFrameABC/people.csv | 4 - Release/pabcversion.txt | 2 +- ReleaseGenerators/PascalABCNET_version.nsh | 2 +- ReleaseGenerators/RebuildStandartModules.pas | 2 +- .../RebuildStandartModulesMono.pas | 2 +- ReleaseGenerators/sect_Core.nsh | 8 + _GenerateLinuxVersion.bat | 8 +- bin/Lib/DataFrameABC.pas | 345 +++---- bin/Lib/LinearAlgebraML.pas | 949 ++++++++++++++++++ bin/Lib/PreprocessorABC.pas | 729 ++++++++++++++ 34 files changed, 1859 insertions(+), 545 deletions(-) delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df01.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df01a.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df02.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df03.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df04.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df05.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df06.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df07.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df08.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df09.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df10.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df11.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df12.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df13.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df14.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df15.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df16.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df17.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df18.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df19.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df20.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/df21.pas delete mode 100644 InstallerSamples/StandardUnits/ML/DataFrameABC/people.csv create mode 100644 bin/Lib/LinearAlgebraML.pas create mode 100644 bin/Lib/PreprocessorABC.pas diff --git a/Configuration/GlobalAssemblyInfo.cs b/Configuration/GlobalAssemblyInfo.cs index 9739fd326..2a7bdaf6b 100644 --- a/Configuration/GlobalAssemblyInfo.cs +++ b/Configuration/GlobalAssemblyInfo.cs @@ -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; diff --git a/Configuration/Version.defs b/Configuration/Version.defs index b7650be0f..4eedb6e2d 100644 --- a/Configuration/Version.defs +++ b/Configuration/Version.defs @@ -1,4 +1,4 @@ -%COREVERSION%=1 -%REVISION%=3748 %MINOR%=11 +%REVISION%=3749 +%COREVERSION%=1 %MAJOR%=3 diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df01.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df01.pas deleted file mode 100644 index ec728a97a..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df01.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df01a.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df01a.pas deleted file mode 100644 index 42ea7ed4d..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df01a.pas +++ /dev/null @@ -1,7 +0,0 @@ -// Загрузка данных из CSV-файла -uses DataFrameABC; - -begin - var df := DataFrame.FromCsv('people.csv'); - df.Print; -end. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df02.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df02.pas deleted file mode 100644 index f6bdc4129..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df02.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df03.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df03.pas deleted file mode 100644 index 42f17bb42..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df03.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df04.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df04.pas deleted file mode 100644 index 4352df28c..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df04.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df05.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df05.pas deleted file mode 100644 index 07b7f799d..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df05.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df06.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df06.pas deleted file mode 100644 index a28193833..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df06.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df07.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df07.pas deleted file mode 100644 index d5f11d132..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df07.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df08.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df08.pas deleted file mode 100644 index 5a80d5838..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df08.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df09.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df09.pas deleted file mode 100644 index d6f64f846..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df09.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df10.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df10.pas deleted file mode 100644 index 951ccb6ad..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df10.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df11.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df11.pas deleted file mode 100644 index c91fb23be..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df11.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df12.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df12.pas deleted file mode 100644 index 6778bbedd..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df12.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df13.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df13.pas deleted file mode 100644 index a59312960..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df13.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df14.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df14.pas deleted file mode 100644 index 87a780a8c..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df14.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df15.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df15.pas deleted file mode 100644 index d8f09acad..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df15.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df16.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df16.pas deleted file mode 100644 index 3e53a2ade..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df16.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df17.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df17.pas deleted file mode 100644 index aa6d27875..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df17.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df18.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df18.pas deleted file mode 100644 index 818113d82..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df18.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df19.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df19.pas deleted file mode 100644 index 02de4714b..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df19.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df20.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df20.pas deleted file mode 100644 index 90a188879..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df20.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/df21.pas b/InstallerSamples/StandardUnits/ML/DataFrameABC/df21.pas deleted file mode 100644 index 2a8fa4a88..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/df21.pas +++ /dev/null @@ -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. \ No newline at end of file diff --git a/InstallerSamples/StandardUnits/ML/DataFrameABC/people.csv b/InstallerSamples/StandardUnits/ML/DataFrameABC/people.csv deleted file mode 100644 index 5cc40c626..000000000 --- a/InstallerSamples/StandardUnits/ML/DataFrameABC/people.csv +++ /dev/null @@ -1,4 +0,0 @@ -name,age,score -Alice,20,85 -Bob,22,90 -Charlie,21,78 \ No newline at end of file diff --git a/Release/pabcversion.txt b/Release/pabcversion.txt index c9ca7cea2..d890865c6 100644 --- a/Release/pabcversion.txt +++ b/Release/pabcversion.txt @@ -1 +1 @@ -3.11.1.3748 +3.11.1.3749 diff --git a/ReleaseGenerators/PascalABCNET_version.nsh b/ReleaseGenerators/PascalABCNET_version.nsh index 57e634ba4..1bf0a6dfc 100644 --- a/ReleaseGenerators/PascalABCNET_version.nsh +++ b/ReleaseGenerators/PascalABCNET_version.nsh @@ -1 +1 @@ -!define VERSION '3.11.1.3748' +!define VERSION '3.11.1.3749' diff --git a/ReleaseGenerators/RebuildStandartModules.pas b/ReleaseGenerators/RebuildStandartModules.pas index c495459df..127e9ba3c 100644 --- a/ReleaseGenerators/RebuildStandartModules.pas +++ b/ReleaseGenerators/RebuildStandartModules.pas @@ -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 diff --git a/ReleaseGenerators/RebuildStandartModulesMono.pas b/ReleaseGenerators/RebuildStandartModulesMono.pas index 78eaf1ebc..651ed1ae0 100644 --- a/ReleaseGenerators/RebuildStandartModulesMono.pas +++ b/ReleaseGenerators/RebuildStandartModulesMono.pas @@ -10,7 +10,7 @@ Collections, Arrays, Core, ClientServer, Countries, ABCDatabases, School, SF, TurtleABC, - DataFrameABC, DataFrameABCCore + DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC ; begin diff --git a/ReleaseGenerators/sect_Core.nsh b/ReleaseGenerators/sect_Core.nsh index 0b2215b69..2e54b3eae 100644 --- a/ReleaseGenerators/sect_Core.nsh +++ b/ReleaseGenerators/sect_Core.nsh @@ -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" diff --git a/_GenerateLinuxVersion.bat b/_GenerateLinuxVersion.bat index 8421f554c..30d3a3eab 100644 --- a/_GenerateLinuxVersion.bat +++ b/_GenerateLinuxVersion.bat @@ -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 diff --git a/bin/Lib/DataFrameABC.pas b/bin/Lib/DataFrameABC.pas index 9aac5d667..29d59f7b2 100644 --- a/bin/Lib/DataFrameABC.pas +++ b/bin/Lib/DataFrameABC.pas @@ -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; /// Возвращает статистику по нескольким столбцам по индексам function Describe(colIndices: array of integer): Dictionary; - /// Возвращает статистику по всем числовым столбцам + /// Возвращает статистику по всем числовым столбц ам function DescribeAll: Dictionary; - /// Группирует данные по одному столбцу по индексу + /// Группирует данные по столбцу по индексу 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 diff --git a/bin/Lib/LinearAlgebraML.pas b/bin/Lib/LinearAlgebraML.pas new file mode 100644 index 000000000..fdf059ee2 --- /dev/null +++ b/bin/Lib/LinearAlgebraML.pas @@ -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. \ No newline at end of file diff --git a/bin/Lib/PreprocessorABC.pas b/bin/Lib/PreprocessorABC.pas new file mode 100644 index 000000000..9bfed56af --- /dev/null +++ b/bin/Lib/PreprocessorABC.pas @@ -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; + 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; + 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; + 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; + + 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; + var values := new List; + + 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; + 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. \ No newline at end of file