DataAdapters

Dataset iris.csv

PrintPreview поправил

Класс Dataset
This commit is contained in:
Mikhalkovich Stanislav 2026-03-11 10:50:15 +03:00
parent a40a45d47a
commit 463d7c30ef
17 changed files with 943 additions and 95 deletions

View file

@ -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 = "3773";
public const string Revision = "3776";
public const string MainVersion = Major + "." + Minor;
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;

View file

@ -1,4 +1,4 @@
%MINOR%=11
%REVISION%=3773
%COREVERSION%=1
%REVISION%=3776
%MINOR%=11
%MAJOR%=3

View file

@ -7,5 +7,7 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<BaseOutputPath>$(OutputPath)</BaseOutputPath>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,16 @@
uses MLABC;
begin
var ds := Datasets.Iris;
Println('Dataset: ', ds.Name);
Println('Task: ', ds.Task);
Println;
Println('Rows:', ds.Data.RowCount);
Println('Features:', ds.Features.Length);
Println;
Println('Head:');
ds.Data.Print;
end.

View file

@ -1 +1 @@
3.11.1.3773
3.11.1.3776

View file

@ -0,0 +1,151 @@
sepal_length,sepal_width,petal_length,petal_width,species
5.1,3.5,1.4,0.2,setosa
4.9,3.0,1.4,0.2,setosa
4.7,3.2,1.3,0.2,setosa
4.6,3.1,1.5,0.2,setosa
5.0,3.6,1.4,0.2,setosa
5.4,3.9,1.7,0.4,setosa
4.6,3.4,1.4,0.3,setosa
5.0,3.4,1.5,0.2,setosa
4.4,2.9,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
5.4,3.7,1.5,0.2,setosa
4.8,3.4,1.6,0.2,setosa
4.8,3.0,1.4,0.1,setosa
4.3,3.0,1.1,0.1,setosa
5.8,4.0,1.2,0.2,setosa
5.7,4.4,1.5,0.4,setosa
5.4,3.9,1.3,0.4,setosa
5.1,3.5,1.4,0.3,setosa
5.7,3.8,1.7,0.3,setosa
5.1,3.8,1.5,0.3,setosa
5.4,3.4,1.7,0.2,setosa
5.1,3.7,1.5,0.4,setosa
4.6,3.6,1.0,0.2,setosa
5.1,3.3,1.7,0.5,setosa
4.8,3.4,1.9,0.2,setosa
5.0,3.0,1.6,0.2,setosa
5.0,3.4,1.6,0.4,setosa
5.2,3.5,1.5,0.2,setosa
5.2,3.4,1.4,0.2,setosa
4.7,3.2,1.6,0.2,setosa
4.8,3.1,1.6,0.2,setosa
5.4,3.4,1.5,0.4,setosa
5.2,4.1,1.5,0.1,setosa
5.5,4.2,1.4,0.2,setosa
4.9,3.1,1.5,0.2,setosa
5.0,3.2,1.2,0.2,setosa
5.5,3.5,1.3,0.2,setosa
4.9,3.6,1.4,0.1,setosa
4.4,3.0,1.3,0.2,setosa
5.1,3.4,1.5,0.2,setosa
5.0,3.5,1.3,0.3,setosa
4.5,2.3,1.3,0.3,setosa
4.4,3.2,1.3,0.2,setosa
5.0,3.5,1.6,0.6,setosa
5.1,3.8,1.9,0.4,setosa
4.8,3.0,1.4,0.3,setosa
5.1,3.8,1.6,0.2,setosa
4.6,3.2,1.4,0.2,setosa
5.3,3.7,1.5,0.2,setosa
5.0,3.3,1.4,0.2,setosa
7.0,3.2,4.7,1.4,versicolor
6.4,3.2,4.5,1.5,versicolor
6.9,3.1,4.9,1.5,versicolor
5.5,2.3,4.0,1.3,versicolor
6.5,2.8,4.6,1.5,versicolor
5.7,2.8,4.5,1.3,versicolor
6.3,3.3,4.7,1.6,versicolor
4.9,2.4,3.3,1.0,versicolor
6.6,2.9,4.6,1.3,versicolor
5.2,2.7,3.9,1.4,versicolor
5.0,2.0,3.5,1.0,versicolor
5.9,3.0,4.2,1.5,versicolor
6.0,2.2,4.0,1.0,versicolor
6.1,2.9,4.7,1.4,versicolor
5.6,2.9,3.6,1.3,versicolor
6.7,3.1,4.4,1.4,versicolor
5.6,3.0,4.5,1.5,versicolor
5.8,2.7,4.1,1.0,versicolor
6.2,2.2,4.5,1.5,versicolor
5.6,2.5,3.9,1.1,versicolor
5.9,3.2,4.8,1.8,versicolor
6.1,2.8,4.0,1.3,versicolor
6.3,2.5,4.9,1.5,versicolor
6.1,2.8,4.7,1.2,versicolor
6.4,2.9,4.3,1.3,versicolor
6.6,3.0,4.4,1.4,versicolor
6.8,2.8,4.8,1.4,versicolor
6.7,3.0,5.0,1.7,versicolor
6.0,2.9,4.5,1.5,versicolor
5.7,2.6,3.5,1.0,versicolor
5.5,2.4,3.8,1.1,versicolor
5.5,2.4,3.7,1.0,versicolor
5.8,2.7,3.9,1.2,versicolor
6.0,2.7,5.1,1.6,versicolor
5.4,3.0,4.5,1.5,versicolor
6.0,3.4,4.5,1.6,versicolor
6.7,3.1,4.7,1.5,versicolor
6.3,2.3,4.4,1.3,versicolor
5.6,3.0,4.1,1.3,versicolor
5.5,2.5,4.0,1.3,versicolor
5.5,2.6,4.4,1.2,versicolor
6.1,3.0,4.6,1.4,versicolor
5.8,2.6,4.0,1.2,versicolor
5.0,2.3,3.3,1.0,versicolor
5.6,2.7,4.2,1.3,versicolor
5.7,3.0,4.2,1.2,versicolor
5.7,2.9,4.2,1.3,versicolor
6.2,2.9,4.3,1.3,versicolor
5.1,2.5,3.0,1.1,versicolor
5.7,2.8,4.1,1.3,versicolor
6.3,3.3,6.0,2.5,virginica
5.8,2.7,5.1,1.9,virginica
7.1,3.0,5.9,2.1,virginica
6.3,2.9,5.6,1.8,virginica
6.5,3.0,5.8,2.2,virginica
7.6,3.0,6.6,2.1,virginica
4.9,2.5,4.5,1.7,virginica
7.3,2.9,6.3,1.8,virginica
6.7,2.5,5.8,1.8,virginica
7.2,3.6,6.1,2.5,virginica
6.5,3.2,5.1,2.0,virginica
6.4,2.7,5.3,1.9,virginica
6.8,3.0,5.5,2.1,virginica
5.7,2.5,5.0,2.0,virginica
5.8,2.8,5.1,2.4,virginica
6.4,3.2,5.3,2.3,virginica
6.5,3.0,5.5,1.8,virginica
7.7,3.8,6.7,2.2,virginica
7.7,2.6,6.9,2.3,virginica
6.0,2.2,5.0,1.5,virginica
6.9,3.2,5.7,2.3,virginica
5.6,2.8,4.9,2.0,virginica
7.7,2.8,6.7,2.0,virginica
6.3,2.7,4.9,1.8,virginica
6.7,3.3,5.7,2.1,virginica
7.2,3.2,6.0,1.8,virginica
6.2,2.8,4.8,1.8,virginica
6.1,3.0,4.9,1.8,virginica
6.4,2.8,5.6,2.1,virginica
7.2,3.0,5.8,1.6,virginica
7.4,2.8,6.1,1.9,virginica
7.9,3.8,6.4,2.0,virginica
6.4,2.8,5.6,2.2,virginica
6.3,2.8,5.1,1.5,virginica
6.1,2.6,5.6,1.4,virginica
7.7,3.0,6.1,2.3,virginica
6.3,3.4,5.6,2.4,virginica
6.4,3.1,5.5,1.8,virginica
6.0,3.0,4.8,1.8,virginica
6.9,3.1,5.4,2.1,virginica
6.7,3.1,5.6,2.4,virginica
6.9,3.1,5.1,2.3,virginica
5.8,2.7,5.1,1.9,virginica
6.8,3.2,5.9,2.3,virginica
6.7,3.3,5.7,2.5,virginica
6.7,3.0,5.2,2.3,virginica
6.3,2.5,5.0,1.9,virginica
6.5,3.0,5.2,2.0,virginica
6.2,3.4,5.4,2.3,virginica
5.9,3.0,5.1,1.8,virginica
1 sepal_length sepal_width petal_length petal_width species
2 5.1 3.5 1.4 0.2 setosa
3 4.9 3.0 1.4 0.2 setosa
4 4.7 3.2 1.3 0.2 setosa
5 4.6 3.1 1.5 0.2 setosa
6 5.0 3.6 1.4 0.2 setosa
7 5.4 3.9 1.7 0.4 setosa
8 4.6 3.4 1.4 0.3 setosa
9 5.0 3.4 1.5 0.2 setosa
10 4.4 2.9 1.4 0.2 setosa
11 4.9 3.1 1.5 0.1 setosa
12 5.4 3.7 1.5 0.2 setosa
13 4.8 3.4 1.6 0.2 setosa
14 4.8 3.0 1.4 0.1 setosa
15 4.3 3.0 1.1 0.1 setosa
16 5.8 4.0 1.2 0.2 setosa
17 5.7 4.4 1.5 0.4 setosa
18 5.4 3.9 1.3 0.4 setosa
19 5.1 3.5 1.4 0.3 setosa
20 5.7 3.8 1.7 0.3 setosa
21 5.1 3.8 1.5 0.3 setosa
22 5.4 3.4 1.7 0.2 setosa
23 5.1 3.7 1.5 0.4 setosa
24 4.6 3.6 1.0 0.2 setosa
25 5.1 3.3 1.7 0.5 setosa
26 4.8 3.4 1.9 0.2 setosa
27 5.0 3.0 1.6 0.2 setosa
28 5.0 3.4 1.6 0.4 setosa
29 5.2 3.5 1.5 0.2 setosa
30 5.2 3.4 1.4 0.2 setosa
31 4.7 3.2 1.6 0.2 setosa
32 4.8 3.1 1.6 0.2 setosa
33 5.4 3.4 1.5 0.4 setosa
34 5.2 4.1 1.5 0.1 setosa
35 5.5 4.2 1.4 0.2 setosa
36 4.9 3.1 1.5 0.2 setosa
37 5.0 3.2 1.2 0.2 setosa
38 5.5 3.5 1.3 0.2 setosa
39 4.9 3.6 1.4 0.1 setosa
40 4.4 3.0 1.3 0.2 setosa
41 5.1 3.4 1.5 0.2 setosa
42 5.0 3.5 1.3 0.3 setosa
43 4.5 2.3 1.3 0.3 setosa
44 4.4 3.2 1.3 0.2 setosa
45 5.0 3.5 1.6 0.6 setosa
46 5.1 3.8 1.9 0.4 setosa
47 4.8 3.0 1.4 0.3 setosa
48 5.1 3.8 1.6 0.2 setosa
49 4.6 3.2 1.4 0.2 setosa
50 5.3 3.7 1.5 0.2 setosa
51 5.0 3.3 1.4 0.2 setosa
52 7.0 3.2 4.7 1.4 versicolor
53 6.4 3.2 4.5 1.5 versicolor
54 6.9 3.1 4.9 1.5 versicolor
55 5.5 2.3 4.0 1.3 versicolor
56 6.5 2.8 4.6 1.5 versicolor
57 5.7 2.8 4.5 1.3 versicolor
58 6.3 3.3 4.7 1.6 versicolor
59 4.9 2.4 3.3 1.0 versicolor
60 6.6 2.9 4.6 1.3 versicolor
61 5.2 2.7 3.9 1.4 versicolor
62 5.0 2.0 3.5 1.0 versicolor
63 5.9 3.0 4.2 1.5 versicolor
64 6.0 2.2 4.0 1.0 versicolor
65 6.1 2.9 4.7 1.4 versicolor
66 5.6 2.9 3.6 1.3 versicolor
67 6.7 3.1 4.4 1.4 versicolor
68 5.6 3.0 4.5 1.5 versicolor
69 5.8 2.7 4.1 1.0 versicolor
70 6.2 2.2 4.5 1.5 versicolor
71 5.6 2.5 3.9 1.1 versicolor
72 5.9 3.2 4.8 1.8 versicolor
73 6.1 2.8 4.0 1.3 versicolor
74 6.3 2.5 4.9 1.5 versicolor
75 6.1 2.8 4.7 1.2 versicolor
76 6.4 2.9 4.3 1.3 versicolor
77 6.6 3.0 4.4 1.4 versicolor
78 6.8 2.8 4.8 1.4 versicolor
79 6.7 3.0 5.0 1.7 versicolor
80 6.0 2.9 4.5 1.5 versicolor
81 5.7 2.6 3.5 1.0 versicolor
82 5.5 2.4 3.8 1.1 versicolor
83 5.5 2.4 3.7 1.0 versicolor
84 5.8 2.7 3.9 1.2 versicolor
85 6.0 2.7 5.1 1.6 versicolor
86 5.4 3.0 4.5 1.5 versicolor
87 6.0 3.4 4.5 1.6 versicolor
88 6.7 3.1 4.7 1.5 versicolor
89 6.3 2.3 4.4 1.3 versicolor
90 5.6 3.0 4.1 1.3 versicolor
91 5.5 2.5 4.0 1.3 versicolor
92 5.5 2.6 4.4 1.2 versicolor
93 6.1 3.0 4.6 1.4 versicolor
94 5.8 2.6 4.0 1.2 versicolor
95 5.0 2.3 3.3 1.0 versicolor
96 5.6 2.7 4.2 1.3 versicolor
97 5.7 3.0 4.2 1.2 versicolor
98 5.7 2.9 4.2 1.3 versicolor
99 6.2 2.9 4.3 1.3 versicolor
100 5.1 2.5 3.0 1.1 versicolor
101 5.7 2.8 4.1 1.3 versicolor
102 6.3 3.3 6.0 2.5 virginica
103 5.8 2.7 5.1 1.9 virginica
104 7.1 3.0 5.9 2.1 virginica
105 6.3 2.9 5.6 1.8 virginica
106 6.5 3.0 5.8 2.2 virginica
107 7.6 3.0 6.6 2.1 virginica
108 4.9 2.5 4.5 1.7 virginica
109 7.3 2.9 6.3 1.8 virginica
110 6.7 2.5 5.8 1.8 virginica
111 7.2 3.6 6.1 2.5 virginica
112 6.5 3.2 5.1 2.0 virginica
113 6.4 2.7 5.3 1.9 virginica
114 6.8 3.0 5.5 2.1 virginica
115 5.7 2.5 5.0 2.0 virginica
116 5.8 2.8 5.1 2.4 virginica
117 6.4 3.2 5.3 2.3 virginica
118 6.5 3.0 5.5 1.8 virginica
119 7.7 3.8 6.7 2.2 virginica
120 7.7 2.6 6.9 2.3 virginica
121 6.0 2.2 5.0 1.5 virginica
122 6.9 3.2 5.7 2.3 virginica
123 5.6 2.8 4.9 2.0 virginica
124 7.7 2.8 6.7 2.0 virginica
125 6.3 2.7 4.9 1.8 virginica
126 6.7 3.3 5.7 2.1 virginica
127 7.2 3.2 6.0 1.8 virginica
128 6.2 2.8 4.8 1.8 virginica
129 6.1 3.0 4.9 1.8 virginica
130 6.4 2.8 5.6 2.1 virginica
131 7.2 3.0 5.8 1.6 virginica
132 7.4 2.8 6.1 1.9 virginica
133 7.9 3.8 6.4 2.0 virginica
134 6.4 2.8 5.6 2.2 virginica
135 6.3 2.8 5.1 1.5 virginica
136 6.1 2.6 5.6 1.4 virginica
137 7.7 3.0 6.1 2.3 virginica
138 6.3 3.4 5.6 2.4 virginica
139 6.4 3.1 5.5 1.8 virginica
140 6.0 3.0 4.8 1.8 virginica
141 6.9 3.1 5.4 2.1 virginica
142 6.7 3.1 5.6 2.4 virginica
143 6.9 3.1 5.1 2.3 virginica
144 5.8 2.7 5.1 1.9 virginica
145 6.8 3.2 5.9 2.3 virginica
146 6.7 3.3 5.7 2.5 virginica
147 6.7 3.0 5.2 2.3 virginica
148 6.3 2.5 5.0 1.9 virginica
149 6.5 3.0 5.2 2.0 virginica
150 6.2 3.4 5.4 2.3 virginica
151 5.9 3.0 5.1 1.8 virginica

View file

@ -0,0 +1,34 @@
name = Iris
task = classification
features = sepal_length,sepal_width,petal_length,petal_width
target = species
description.en = The classic Fisher iris dataset. One of the most famous datasets in machine learning and statistics.
description.ru = Классический датасет ирисов Фишера. Один из самых известных датасетов в машинном обучении.
source = UCI Machine Learning Repository
url = https://github.com/mwaskom/seaborn-data/blob/master/iris.csv
feature.sepal_length = float
feature.sepal_width = float
feature.petal_length = float
feature.petal_width = float
feature.species = categorical
feature.sepal_length.ru = длина чашелистика
feature.sepal_width.ru = ширина чашелистика
feature.petal_length.ru = длина лепестка
feature.petal_width.ru = ширина лепестка
feature.species.ru = вид ириса
feature.sepal_length.en = sepal length
feature.sepal_width.en = sepal width
feature.petal_length.en = petal length
feature.petal_width.en = petal width
feature.species.en = iris species
value.species.setosa.ru = ирис щетинистый
value.species.versicolor.ru = ирис разноцветный
value.species.virginica.ru = ирис виргинский

View file

@ -1 +1 @@
!define VERSION '3.11.1.3773'
!define VERSION '3.11.1.3776'

View file

@ -22,7 +22,7 @@ uses
WPF,
DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC,
MetricsABC, MLABC, MLCoreABC, MLModelsABC, ValidationML, MLExceptions, InspectionML, MLPipelineABC,
PlotML, MLDatasets
PlotML, MLDatasets, DataAdapters
;
begin

View file

@ -12,7 +12,7 @@
School, SF, TurtleABC,
DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC,
MetricsABC, MLABC, MLCoreABC, MLModelsABC, ValidationML, MLExceptions, InspectionML, MLPipelineABC,
PlotML, MLDatasets
PlotML, MLDatasets, DataAdapters
;
begin

View file

@ -193,6 +193,8 @@
File ..\bin\Lib\MLPipelineABC.pcu
File ..\bin\Lib\PlotML.pcu
File ..\bin\Lib\MLDatasets.pcu
File ..\bin\Lib\DataAdapters.pcu
File ..\bin\Lib\PABCRtl.dll
File ..\bin\Lib\HelixToolkit.Wpf.dll
@ -300,6 +302,7 @@
${AddFile} "MLPipelineABC.pcu"
${AddFile} "PlotML.pcu"
${AddFile} "MLDatasets.pcu"
${AddFile} "DataAdapters.pcu"
${AddFile} "turtle.png"
@ -444,6 +447,7 @@
File ..\bin\Lib\MLPipelineABC.pas
File ..\bin\Lib\PlotML.pas
File ..\bin\Lib\MLDatasets.pas
File ..\bin\Lib\DataAdapters.pas
File ..\bin\Lib\__RedirectIOMode.vb
@ -539,6 +543,8 @@
${AddFile} "MLPipelineABC.pas"
${AddFile} "PlotML.pas"
${AddFile} "MLDatasets.pas"
${AddFile} "DataAdapters.pas"
${AddFile} "__RedirectIOMode.vb"
${AddFile} "VBSystem.vb"

View file

@ -6,6 +6,10 @@
File "Files\Databases\*.txt"
File "Files\Words\*.txt"
SetOutPath "$INSTDIR\Files\Datasets"
File "Files\Datasets\*.csv"
File "Files\Datasets\*.meta"
SetOutPath "$INSTDIR\Files\Images"
File "Files\Images\*.png"
SectionEnd

110
bin/Lib/DataAdapters.pas Normal file
View file

@ -0,0 +1,110 @@
unit DataAdapters;
interface
uses DataFrameABC;
uses LinearAlgebraML;
/// Преобразует вектор меток классов в массив целых чисел.
/// Используется при визуализации и других задачах,
/// где метки должны быть представлены как 0,1,2,...
/// Значения округляются функцией Round, чтобы устранить
/// возможные небольшие численные ошибки
function LabelsToInts(y: Vector): array of integer;
function EncodeLabels(labels: array of string): array of integer;
implementation
uses MLExceptions;
const
ER_TO_MATRIX_NO_COLUMNS =
'ToMatrix: не указаны столбцы!!ToMatrix: no columns specified';
ER_TO_VECTOR_NON_NUMERIC =
'ToVector: столбец "{0}" содержит нечисловые или NA значения!!' +
'ToVector: column "{0}" contains non-numeric or NA values';
ER_TO_MATRIX_NON_NUMERIC =
'Столбец "{0}" содержит нечисловые или NA значения!!Column "{0}" contains non-numeric or NA values';
function LabelsToInts(y: Vector): array of integer;
begin
if y = nil then
ArgumentNullError(ER_ARG_NULL, 'y');
Result := ArrGen(y.Length, i -> Round(y[i]));
end;
function EncodeLabels(labels: array of string): array of integer;
begin
if labels = nil then
ArgumentNullError(ER_ARG_NULL, 'labels');
var map := new Dictionary<string, integer>;
var res := new integer[labels.Length];
var next := 0;
for var i := 0 to labels.Length - 1 do
begin
var s := labels[i];
if not map.ContainsKey(s) then
begin
map[s] := next;
next += 1;
end;
res[i] := map[s];
end;
Result := res;
end;
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
begin
var df := Self;
var n := df.RowCount;
var p := colNames.Length;
if p = 0 then
ArgumentError(ER_TO_MATRIX_NO_COLUMNS);
Result := new Matrix(n, p);
for var j := 0 to p - 1 do
begin
var col := df[colNames[j]];
for var i := 0 to n - 1 do
begin
var value: real;
if not col.TryGetNumericValue(i, value) then
ArgumentError(ER_TO_MATRIX_NON_NUMERIC, colNames[j]);
Result[i,j] := value;
end;
end;
end;
function ToVector(Self: DataFrame; colName: string): Vector; extensionmethod;
begin
var df := Self;
var n := df.RowCount;
Result := new Vector(n);
var col := df[colName];
for var i := 0 to n - 1 do
begin
var value: real;
if not col.TryGetNumericValue(i, value) then
ArgumentError(ER_TO_VECTOR_NON_NUMERIC, colName);
Result[i] := value;
end;
end;
end.

View file

@ -114,9 +114,24 @@ type
/// Возвращает индекс столбца по имени
function ColumnIndex(name: string): integer;
function HasColumn(name: string): boolean;
/// Создает курсор для итерации по строкам
function GetCursor: DataFrameCursor;
/// Разбивает таблицу на обучающую и тестовую выборки.
///
/// Строки случайным образом перемешиваются и делятся на две части:
/// train и test. Доля тестовой выборки задаётся параметром testRatio.
///
/// testRatio = 0.2 означает, что 20% строк попадут в тестовую выборку.
///
/// Если seed >= 0, разбиение будет детерминированным.
/// Если seed = -1, используется случайная инициализация генератора.
///
/// Возвращает кортеж (trainDataFrame, testDataFrame).
function TrainTestSplit(testRatio: real := 0.2; seed: integer := -1): (DataFrame, DataFrame);
/// Добавляет столбец целых чисел
procedure AddIntColumn(name: string; data: array of integer; valid: array of boolean := nil; isCategorical: boolean := false);
/// Добавляет столбец вещественных чисел
@ -130,6 +145,10 @@ type
function GetIntColumn(name: string): array of integer;
/// Возвращает данные вещественного столбца по имени
function GetFloatColumn(name: string): array of real;
/// Возвращает данные строкового столбца по имени
function GetStrColumn(name: string): array of string;
/// Возвращает данные логического столбца по имени
function GetBoolColumn(name: string): array of boolean;
/// Вычисляет сумму значений столбца по индексу
function Sum(colIndex: integer): real;
@ -428,7 +447,9 @@ const
ER_CSV_INVALID_BOOL =
'Некорректное логическое значение "{0}" в столбце "{1}"!!' +
'Invalid bool "{0}" in column "{1}"';
ER_TEST_RATIO_INVALID =
'Некорректное значение testRatio = {0}. Ожидается число в диапазоне (0, 1).' +
'Invalid testRatio = {0}. Expected value in range (0, 1).';
type
/// Класс для группировки данных
@ -1478,6 +1499,11 @@ begin
Error(ER_COLUMN_NOT_FOUND, name);
end;
function DataFrame.HasColumn(name: string): boolean;
begin
Result := fSchema.HasColumn(name);
end;
function DataFrame.GetCursor: DataFrameCursor :=
new DataFrameCursor(columns.ToArray,fSchema);
@ -1495,6 +1521,67 @@ begin
Result := c.Data;
end;
function DataFrame.GetStrColumn(name: string): array of string;
begin
var i := ColumnIndex(name);
var c := StrColumn(columns[i]);
Result := c.Data;
end;
function DataFrame.GetBoolColumn(name: string): array of boolean;
begin
var i := ColumnIndex(name);
var c := BoolColumn(columns[i]);
Result := c.Data;
end;
function DataFrame.TrainTestSplit(testRatio: real; seed: integer): (DataFrame, DataFrame);
begin
if Self = nil then
ArgumentNullError(ER_ARG_NULL, 'DataFrame');
if (testRatio <= 0.0) or (testRatio >= 1.0) then
ArgumentError(ER_TEST_RATIO_INVALID, testRatio);
var n := RowCount;
if n < 2 then
ArgumentError(ER_EMPTY_DATA, 'TrainTestSplit');
var actualSeed := if seed >= 0 then seed else System.Environment.TickCount and integer.MaxValue;
var rnd := new System.Random(actualSeed);
var idx := Arr(0..n-1);
idx.Shuffle(rnd);
var rawSize := Round(n * testRatio);
var testSize := rawSize.Clamp(1, n - 1);
// --- маркер тестовых строк
var isTest := new boolean[n];
for var i := 0 to testSize - 1 do
isTest[idx[i]] := true;
var trainDf := new DataFrame;
var testDf := new DataFrame;
var cur := GetCursor;
var row := 0;
while cur.MoveNext do
begin
if isTest[row] then
testDf.AppendRowFromCursor(self, cur)
else
trainDf.AppendRowFromCursor(self, cur);
row += 1;
end;
Result := (trainDf, testDf);
end;
procedure DataFrame.AddIntColumn(name: string; data: array of integer; valid: array of boolean; isCategorical: boolean);
begin
if (columns.Count > 0) and (data.Length <> RowCount) then
@ -1531,7 +1618,7 @@ begin
else
begin
if valid.Length <> data.Length then
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
DimensionError(ER_COLUMN_VALID_LENGTH_MISMATCH);
c.IsValid := valid;
end;
@ -2835,7 +2922,7 @@ begin
end;
procedure DataFrame.PrintPreview(maxRows: integer; headRows: integer; decimals: integer);
{procedure DataFrame.PrintPreview(maxRows: integer; headRows: integer; decimals: integer);
begin
var ColumnSeparator := ' ';
var colCount := columns.Count;
@ -3025,6 +3112,179 @@ begin
PABCSystem.Print(FormatValue(j) + ColumnSeparator);
PABCSystem.Println;
end;
end;}
procedure DataFrame.PrintPreview(maxRows: integer; headRows: integer; decimals: integer);
begin
var colCount := columns.Count;
if colCount = 0 then exit;
var rowCount := RowCount;
if rowCount = 0 then exit;
if maxRows < 1 then exit;
if decimals < 0 then decimals := 0;
if rowCount <= maxRows then
headRows := rowCount
else
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;
var widths := new integer[colCount];
// --- начальная ширина = длина заголовка ---
for var j := 0 to colCount - 1 do
begin
widths[j] := columns[j].Info.Name.Length;
if widths[j] < 2 then
widths[j] := 2;
end;
var cursor := GetCursor;
// --- scan head ---
for var i := 0 to headRows - 1 do
begin
cursor.MoveTo(i);
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;
end;
if s.Length > widths[j] then
widths[j] := s.Length;
end;
end;
// --- scan tail ---
if rowCount > headRows then
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
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;
end;
if s.Length > widths[j] then
widths[j] := s.Length;
end;
end;
// --- header ---
for var j := 0 to colCount - 1 do
begin
PABCSystem.Print(columns[j].Info.Name.PadLeft(widths[j]));
if j < colCount - 1 then
Write(' ');
end;
PABCSystem.Println;
// --- head rows ---
for var i := 0 to headRows - 1 do
begin
cursor.MoveTo(i);
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;
end;
PABCSystem.Print(s.PadLeft(widths[j]));
if j < colCount - 1 then
Write(' ');
end;
PABCSystem.Println;
end;
// --- ellipsis ---
if headRows + tailRows < rowCount then
begin
for var j := 0 to colCount - 1 do
begin
PABCSystem.Print($'…'.PadLeft(widths[j]));
if j < colCount - 1 then
Write(' ');
end;
PABCSystem.Println;
end;
// --- tail rows ---
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
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;
end;
PABCSystem.Print(s.PadLeft(widths[j]));
if j < colCount - 1 then
Write(' ');
end;
PABCSystem.Println;
end;
end;
procedure DataFrame.PrintlnPreview(maxRows: integer; headRows: integer; decimals: integer);
@ -3035,7 +3295,7 @@ end;
procedure DataFrame.Print(decimals: integer);
begin
PrintPreview(20, 10, decimals);
PrintPreview(10, 5, decimals);
end;
procedure DataFrame.Println(decimals: integer);
@ -3934,7 +4194,6 @@ end;
// CSVLoader
//-----------------------------
{procedure ScanFields(line: string; delimiter: char;
starts, lens: array of integer; var actualCount: integer);
begin

View file

@ -15,6 +15,7 @@ uses MLExceptions;
uses InspectionML;
uses MLPipelineABC;
uses MLDatasets;
uses DataAdapters;
type
Vector = LinearAlgebraML.Vector;
@ -79,21 +80,17 @@ type
Datasets = MLDatasets.Datasets;
/// Преобразует вектор меток классов в массив целых чисел.
/// Используется при визуализации и других задачах,
/// где метки должны быть представлены как 0,1,2,...
/// Значения округляются функцией Round, чтобы устранить
/// возможные небольшие численные ошибки
function LabelsToInts(y: Vector): array of integer;
function LabelsToInts(y: Vector): array of integer;
function EncodeLabels(labels: array of string): array of integer;
implementation
function LabelsToInts(y: Vector): array of integer;
begin
if y = nil then
ArgumentNullError(ER_ARG_NULL, 'y');
Result := ArrGen(y.Length, i -> Round(y[i]));
Result := DataAdapters.LabelsToInts(y);
end;
function EncodeLabels(labels: array of string): array of integer := DataAdapters.EncodeLabels(labels);
end.

View file

@ -5,6 +5,56 @@ interface
uses DataFrameABC, LinearAlgebraML;
type
TaskType = (Classification, Regression, Clustering);
/// ML-датасет: таблица данных вместе с метаданными задачи.
///
/// Dataset содержит:
/// Data таблицу данных (DataFrame)
/// Features список признаков
/// Target целевую переменную (если есть)
/// Task тип задачи (classification/regression/clustering)
///
/// Предоставляет удобные методы для получения матриц признаков
/// и целевых значений для обучения моделей
Dataset = class
public
Name: string;
Data: DataFrame;
Features: array of string;
Target: string;
Task: TaskType;
/// Возвращает матрицу признаков X.
/// Используются столбцы, указанные в Features.
function ToX: Matrix;
/// Возвращает вектор целевой переменной y.
/// Доступно только для задач с учителем.
function ToY: Vector;
/// Возвращает пару (X, y) для обучения модели.
function ToXY: (Matrix, Vector);
/// Возвращает (X, y), где y преобразован в целочисленные метки классов.
/// Используется для задач классификации.
function ToXYInt: (Matrix, array of integer);
/// Возвращает true, если датасет относится к задаче с учителем
/// (classification или regression).
function IsSupervised: boolean;
/// Разбивает датасет на обучающую и тестовую части.
/// testRatio доля тестовой выборки.
function TrainTestSplit(testRatio: real := 0.2; seed: integer := -1): (Dataset, Dataset);
/// Возвращает первые n строк таблицы данных.
function Head(n: integer := 5): DataFrame;
/// Возвращает краткое описание датасета (метаданные).
function Describe: DataFrame;
end;
/// Набор генераторов и загрузчиков датасетов для задач машинного обучения.
/// Содержит синтетические генераторы (MakeBlobs, MakeMoons, MakeRegression)
/// и реальные учебные датасеты (например, RussianHousing, StudentExam).
@ -78,7 +128,7 @@ type
/// noise стандартное отклонение гауссовского шума, добавляемого к координатам.
/// factor отношение радиуса внутреннего круга к внешнему (0 < factor < 1).
/// shuffle перемешивать ли порядок объектов.
/// seed значение генератора случайных чисел (-1 означает использовать текущее время).
/// seed значение генератора случайных чисел (-1 означает использовать текущее время)
static function MakeCircles(
n: integer := 300;
noise: real := 0.05;
@ -96,7 +146,7 @@ type
/// turns число оборотов спирали.
/// radius максимальный радиус спирали.
/// shuffle перемешивать ли порядок объектов.
/// seed значение генератора случайных чисел (-1 означает использовать текущее время).
/// seed значение генератора случайных чисел (-1 означает использовать текущее время)
static function MakeSpiral(
n: integer := 300;
classes: integer := 2;
@ -106,31 +156,67 @@ type
shuffle: boolean := true;
seed: integer := -1): (Matrix, Vector);
/// Загружает датасет по имени.
///
/// Датасет ищется в каталоге:
/// Files\Datasets
///
/// Для датасета должны существовать файлы:
/// name.meta метаданные датасета
/// name.csv данные таблицы
///
/// Метаданные определяют:
/// тип задачи (classification / regression / clustering)
/// целевую переменную
/// список признаков (опционально)
///
/// Если признаки не указаны в .meta, используются
/// все столбцы таблицы, кроме целевой переменной.
///
/// Пример:
/// var ds := Datasets.Load('Iris');
static function Load(name: string): Dataset;
// --- DataFrame датасеты (реалистичные таблицы, считываемые из csv)
/// Датасет Iris (классификация).
///
/// 150 объектов, 4 признака:
/// sepal_length, sepal_width, petal_length, petal_width.
/// Целевая переменная: species.
///
/// Пример:
/// var ds := Datasets.Iris;
/// var (X,y) := ds.ToXYInt;
static function Iris: Dataset;
/// Датасет цен на квартиры (задача регрессии)
static function RussianHousing: DataFrame;
static function RussianHousing: Dataset;
/// Датасет результатов экзамена студентов (классификация)
static function StudentExam: DataFrame;
static function StudentExam: Dataset;
/// Датасет банковских клиентов (классификация одобрения кредита)
static function BankClients: DataFrame;
static function BankClients: Dataset;
/// Датасет поездок такси (регрессия стоимости поездки)
static function TaxiTrips: DataFrame;
static function TaxiTrips: Dataset;
/// Датасет транспортной активности пассажиров (кластеризация)
static function MoscowTransport: DataFrame;
static function MoscowTransport: Dataset;
/// Датасет интернет-покупок пользователей (классификация покупки)
static function OnlineShopping: DataFrame;
static function OnlineShopping: Dataset;
static function LoadMeta(path: string): Dictionary<string,string>;
static function ParseFeatures(meta: Dictionary<string,string>): array of string;
end;
implementation
uses MLExceptions;
uses DataAdapters;
const
ER_PARAM_GT_ZERO =
@ -141,7 +227,25 @@ const
'Параметр {0} должен быть >= 0!!Parameter {0} must be >= 0';
ER_PARAM_BETWEEN_01 =
'Параметр {0} должен быть в диапазоне (0,1)!!Parameter {0} must be in range (0,1)';
ER_DATASET_NO_TARGET =
'У датасета нет целевой переменной (задача кластеризации).' +
'Dataset has no target variable (clustering task).';
ER_DATASET_TARGET_NOT_FOUND =
'Целевая переменная "{0}" не найдена в таблице.' +
'Target column "{0}" not found in DataFrame';
ER_DATASET_META_NOT_FOUND =
'Файл метаданных датасета "{0}" не найден!!Dataset meta file "{0}" not found';
ER_DATASET_CSV_NOT_FOUND =
'Файл данных датасета "{0}" не найден!!Dataset csv file "{0}" not found';
ER_DATASET_TASK_UNKNOWN =
'Неизвестный тип задачи датасета "{0}"!!Unknown dataset task "{0}"';
ER_DATASET_TASK_MISSING =
'В метаданных датасета "{0}" отсутствует поле task!!Dataset meta missing field "task" for "{0}"';
ER_DATASET_FEATURE_NOT_FOUND =
'Признак "{0}" не найден в датасете!!Feature column "{0}" not found in dataset';
ER_DATASET_FEATURE_EQUALS_TARGET =
'Признак "{0}" совпадает с целевой переменной!!Feature "{0}" equals target column';
function Normal(rnd: System.Random): real;
begin
var u1 := rnd.NextDouble;
@ -149,15 +253,116 @@ begin
Result := Sqrt(-2 * Ln(u1)) * Cos(2 * Pi * u2);
end;
/// Генерирует синтетический датасет из нескольких гауссовых кластеров.
/// Возвращает матрицу признаков X и вектор меток кластеров y.
///
/// Параметры:
/// n количество объектов (точек)
/// centers число кластеров
/// clusterStd стандартное отклонение точек внутри кластера
/// nFeatures число признаков (размерность пространства)
/// seed значение генератора случайных чисел (seed < 0 случайный)
//-----------------------------
// Dataset
//-----------------------------
function Dataset.ToX(): Matrix;
begin
Result := Data.ToMatrix(Features);
end;
function Dataset.ToY(): Vector;
begin
if Task = Clustering then
ArgumentError(ER_DATASET_NO_TARGET);
Result := Data.ToVector(Target);
end;
function Dataset.ToXY(): (Matrix, Vector);
begin
Result := (ToX, ToY);
end;
function Dataset.ToXYInt: (Matrix, array of integer);
begin
if Task <> Classification then
ArgumentError(ER_DATASET_NO_TARGET);
var X := ToX;
var labels := Data.GetStrColumn(Target);
var y := EncodeLabels(labels);
Result := (X, y);
end;
function Dataset.IsSupervised: boolean;
begin
Result := Task <> Clustering;
end;
function Dataset.TrainTestSplit(testRatio: real; seed: integer): (Dataset, Dataset);
begin
if Self = nil then
ArgumentNullError(ER_ARG_NULL, 'Dataset');
var (trainDf, testDf) := Data.TrainTestSplit(testRatio, seed);
var trainDs := new Dataset;
trainDs.Name := Name;
trainDs.Data := trainDf;
trainDs.Features := Features;
trainDs.Target := Target;
trainDs.Task := Task;
var testDs := new Dataset;
testDs.Name := Name;
testDs.Data := testDf;
testDs.Features := Features;
testDs.Target := Target;
testDs.Task := Task;
Result := (trainDs, testDs);
end;
function Dataset.Head(n: integer): DataFrame;
begin
if Data = nil then
ArgumentNullError(ER_ARG_NULL, 'Data');
Result := Data.Head(n);
end;
function Dataset.Describe: DataFrame;
begin
if Data = nil then
ArgumentNullError(ER_ARG_NULL, 'Data');
var props := Arr(
'Name',
'Rows',
'Columns',
'Task',
'Target',
'FeatureCount',
'Features'
);
var values := Arr(
Name,
Data.RowCount.ToString,
Data.ColumnCount.ToString,
Task.ToString,
Target,
Features.Length.ToString,
Features.JoinToString(', ')
);
var valid := ArrFill(props.Length, true);
var df := new DataFrame;
df.AddStrColumn('Property', props, valid, true);
df.AddStrColumn('Value', values, valid, false);
Result := df;
end;
//-----------------------------
// Datasets
//-----------------------------
static function Datasets.MakeBlobs(
n: integer; centers: integer;
clusterStd: real; nFeatures: integer;
@ -454,35 +659,144 @@ begin
Result := (X, y);
end;
static function Datasets.RussianHousing: DataFrame;
function ParseTask(s: string): TaskType;
begin
case s.ToLower of
'classification': Result := Classification;
'regression': Result := Regression;
'clustering': Result := Clustering;
else
ArgumentError(ER_DATASET_TASK_UNKNOWN, s);
end;
end;
static function Datasets.Load(name: string): Dataset;
begin
if name = nil then
ArgumentNullError(ER_ARG_NULL, 'name');
var baseDir := PascalABCDirectory + 'Files\Datasets\';
var metaPath := baseDir + name + '.meta';
var csvPath := baseDir + name + '.csv';
if not FileExists(metaPath) then
ArgumentError(ER_DATASET_META_NOT_FOUND, name);
if not FileExists(csvPath) then
ArgumentError(ER_DATASET_CSV_NOT_FOUND, name);
var meta := LoadMeta(metaPath);
if not meta.ContainsKey('task') then
ArgumentError(ER_DATASET_TASK_MISSING, name);
var df := DataFrame.FromCsv(csvPath);
var ds := new Dataset;
ds.Name := name;
ds.Data := df;
ds.Task := ParseTask(meta['task']);
ds.Target := meta['target'];
ds.Features := ParseFeatures(meta);
// --- проверка target
if (ds.Target <> nil) and not df.HasColumn(ds.Target) then
ArgumentError(ER_DATASET_TARGET_NOT_FOUND, ds.Target);
// --- проверка features
foreach var f in ds.Features do
begin
if not df.HasColumn(f) then
ArgumentError(ER_DATASET_FEATURE_NOT_FOUND, f);
if (ds.Target <> nil) and (f = ds.Target) then
ArgumentError(ER_DATASET_FEATURE_EQUALS_TARGET, f);
end;
Result := ds;
end;
static function Datasets.Iris: Dataset;
begin
Result := Load('Iris');
end;
static function Datasets.RussianHousing: Dataset;
begin
Result := nil;
end;
static function Datasets.StudentExam: DataFrame;
static function Datasets.StudentExam: Dataset;
begin
Result := nil;
end;
static function Datasets.BankClients: DataFrame;
static function Datasets.BankClients: Dataset;
begin
Result := nil;
end;
static function Datasets.TaxiTrips: DataFrame;
static function Datasets.TaxiTrips: Dataset;
begin
Result := nil;
end;
static function Datasets.MoscowTransport: DataFrame;
static function Datasets.MoscowTransport: Dataset;
begin
Result := nil;
end;
static function Datasets.OnlineShopping: DataFrame;
static function Datasets.OnlineShopping: Dataset;
begin
Result := nil;
end;
static function Datasets.LoadMeta(path: string): Dictionary<string,string>;
begin
if path = nil then
ArgumentNullError(ER_ARG_NULL, 'path');
if not FileExists(path) then
ArgumentError(ER_DATASET_META_NOT_FOUND, path);
var dict := new Dictionary<string,string>;
foreach var line in ReadLines(path) do
begin
var s := line.Trim;
if s = '' then
continue;
if s.StartsWith('#') then
continue;
var p := s.IndexOf('=');
if p < 0 then
continue;
var key := s.Substring(0,p).Trim;
var val := s.Substring(p+1).Trim;
dict[key] := val;
end;
Result := dict;
end;
static function Datasets.ParseFeatures(meta: Dictionary<string,string>): array of string;
begin
if not meta.ContainsKey('features') then
exit(nil);
Result :=
meta['features']
.Split(',')
.Select(s -> s.Trim)
.ToArray;
end;
end.

View file

@ -161,6 +161,7 @@ type
implementation
uses MLExceptions;
uses DataAdapters;
const
ER_PIPELINE_MODIFY_AFTER_FIT =
@ -202,52 +203,6 @@ const
ER_DATAPIPE_DUPLICATE_FEATURE =
'Повторяющийся признак: {0}!!Duplicate feature: {0}';
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
begin
var df := Self;
var n := df.RowCount;
var p := colNames.Length;
if p = 0 then
ArgumentError(ER_TO_MATRIX_NO_COLUMNS);
Result := new Matrix(n, p);
for var j := 0 to p - 1 do
begin
var col := df[colNames[j]];
for var i := 0 to n - 1 do
begin
var value: real;
if not col.TryGetNumericValue(i, value) then
ArgumentError(ER_TO_MATRIX_NON_NUMERIC, colNames[j]);
Result[i,j] := value;
end;
end;
end;
function ToVector(Self: DataFrame; colName: string): Vector; extensionmethod;
begin
var df := Self;
var n := df.RowCount;
Result := new Vector(n);
var col := df[colName];
for var i := 0 to n - 1 do
begin
var value: real;
if not col.TryGetNumericValue(i, value) then
ArgumentError(ER_TO_VECTOR_NON_NUMERIC, colName);
Result[i] := value;
end;
end;
//-----------------------------
// DataPipeline
//-----------------------------