dataframe.concat сделан

This commit is contained in:
Mikhalkovich Stanislav 2026-05-30 19:26:43 +03:00
parent 3938120846
commit 490d539df3
9 changed files with 192 additions and 22 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 = "3828";
public const string Revision = "3830";
public const string MainVersion = Major + "." + Minor;
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;

View file

@ -1,4 +1,4 @@
%COREVERSION%=1
%REVISION%=3828
%REVISION%=3830
%MINOR%=11
%MAJOR%=3

View file

@ -0,0 +1,18 @@
uses DataFrameABC;
begin
var df1 := DataFrame.FromCsvText('''
id,name,age
1,Ann,10
2,Bob,12
''');
var df2 := DataFrame.FromCsvText('''
id,name,age
3,Cat,11
4,Dan,13
''');
var df := DataFrame.Concat(df1, df2);
df.Print;
end.

View file

@ -1 +1 @@
3.11.1.3828
3.11.1.3830

View file

@ -1 +1 @@
!define VERSION '3.11.1.3828'
!define VERSION '3.11.1.3830'

View file

@ -370,6 +370,12 @@ type
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 по строкам.
/// Все таблицы должны иметь одинаковую схему:
/// имена столбцов, их порядок, типы и categorical-флаги должны совпадать.
/// Возвращает новый DataFrame.
static function Concat(params dfs: array of DataFrame): DataFrame;
/// Выводит DataFrame с настраиваемым числом строк
procedure Print(maxRows: integer := 10; headRows: integer := -1; decimals: integer := 2);
@ -618,6 +624,13 @@ const
'CSV format error: expected {0} columns, got {1}';
ER_EMPTY_CSV =
'CSV-файл пуст!!Empty CSV';
ER_CONCAT_EMPTY =
'DataFrame.Concat требует хотя бы один DataFrame!!DataFrame.Concat requires at least one DataFrame';
ER_CONCAT_DF_NULL =
'DataFrame.Concat не принимает nil-таблицы!!DataFrame.Concat does not accept nil dataframes';
ER_CONCAT_SCHEMA_MISMATCH =
'DataFrame.Concat требует одинаковую схему: имена, порядок, типы и categorical-флаги столбцов должны совпадать!!' +
'DataFrame.Concat requires identical schema: column names, order, types, and categorical flags must match';
ER_CSV_UNCLOSED_QUOTE =
'Ошибка формата CSV: незакрытая кавычка!!CSV format error: unclosed quote';
ER_CSV_INVALID_BOOL =
@ -4455,6 +4468,129 @@ begin
Result := CsvLoader.LoadFromLines(text.ToLines);
end;
static function DataFrame.Concat(params dfs: array of DataFrame): DataFrame;
begin
if (dfs = nil) or (dfs.Length = 0) then
ArgumentError(ER_CONCAT_EMPTY);
for var i := 0 to dfs.Length - 1 do
if dfs[i] = nil then
ArgumentError(ER_CONCAT_DF_NULL);
var first := dfs[0];
var schema := first.fSchema;
for var di := 1 to dfs.Length - 1 do
begin
var curSchema := dfs[di].fSchema;
if curSchema.ColumnCount <> schema.ColumnCount then
ArgumentError(ER_CONCAT_SCHEMA_MISMATCH);
for var ci := 0 to schema.ColumnCount - 1 do
if (curSchema.NameAt(ci) <> schema.NameAt(ci)) or
(curSchema.ColumnTypeAt(ci) <> schema.ColumnTypeAt(ci)) or
(curSchema.IsCategoricalAt(ci) <> schema.IsCategoricalAt(ci)) then
ArgumentError(ER_CONCAT_SCHEMA_MISMATCH);
end;
var totalRows := 0;
for var di := 0 to dfs.Length - 1 do
totalRows += dfs[di].RowCount;
var res := new DataFrame;
for var ci := 0 to schema.ColumnCount - 1 do
begin
var name := schema.NameAt(ci);
case schema.ColumnTypeAt(ci) of
ctInt:
begin
var data := new integer[totalRows];
var valid := new boolean[totalRows];
var pos := 0;
for var di := 0 to dfs.Length - 1 do
begin
var col := IntColumn(dfs[di].columns[ci]);
for var r := 0 to col.Data.Length - 1 do
begin
data[pos] := col.Data[r];
valid[pos] := col.IsValid[r];
pos += 1;
end;
end;
res.AddIntColumn(name, data, valid);
end;
ctFloat:
begin
var data := new real[totalRows];
var valid := new boolean[totalRows];
var pos := 0;
for var di := 0 to dfs.Length - 1 do
begin
var col := FloatColumn(dfs[di].columns[ci]);
for var r := 0 to col.Data.Length - 1 do
begin
data[pos] := col.Data[r];
valid[pos] := col.IsValid[r];
pos += 1;
end;
end;
res.AddFloatColumn(name, data, valid);
end;
ctStr:
begin
var data := new string[totalRows];
var valid := new boolean[totalRows];
var pos := 0;
for var di := 0 to dfs.Length - 1 do
begin
var col := StrColumn(dfs[di].columns[ci]);
for var r := 0 to col.Data.Length - 1 do
begin
data[pos] := col.Data[r];
valid[pos] := col.IsValid[r];
pos += 1;
end;
end;
res.AddStrColumn(name, data, valid);
end;
ctBool:
begin
var data := new boolean[totalRows];
var valid := new boolean[totalRows];
var pos := 0;
for var di := 0 to dfs.Length - 1 do
begin
var col := BoolColumn(dfs[di].columns[ci]);
for var r := 0 to col.Data.Length - 1 do
begin
data[pos] := col.Data[r];
valid[pos] := col.IsValid[r];
pos += 1;
end;
end;
res.AddBoolColumn(name, data, valid);
end;
end;
end;
res.SetSchema(schema);
Result := res;
end;
procedure DataFrame.ToCsv(filename: string);
begin
CsvSaver.Save(self, filename, ',', true);

View file

@ -44,18 +44,29 @@ unit MLABC;
// ПОЛИТИКА PREDICT
//
// 1. Классификация:
// Predict возвращает метки классов в том же виде,
// в каком они были поданы модели при обучении.
// Для матричных моделей Predict возвращает метки классов
// в том виде, в каком они были поданы модели при обучении.
//
// ClassificationMatrixPipeline следует той же политике:
// Predict возвращает метки классов в том виде,
// в каком они были поданы вложенной модели при обучении.
//
// ClassificationDataPipeline работает с DataFrame, где целевой столбец
// часто является строковым. Поэтому внутри pipeline целевой столбец
// кодируется в целые числа, и Predict возвращает именно эти коды.
//
// PredictLabels возвращает строковые метки классов.
//
// Таким образом, различие естественно:
// матричные модели и ClassificationMatrixPipeline возвращают метки
// из пространства обучения модели, а ClassificationDataPipeline
// возвращает коды, построенные при кодировании целевого столбца.
//
// 2. Регрессия:
// Predict возвращает вектор числовых предсказаний.
//
// 3. Кластеризация:
// Predict и FitPredict возвращают номера кластеров.
//
// Внутреннее кодирование классов может использоваться внутри модели,
// но наружу через Predict оно не выдаётся.
// =============================================================
interface

View file

@ -7116,6 +7116,9 @@ function GradientBoostingClassifier.PredictStage(X: Matrix; m: integer): array o
begin
var probs := PredictStageProba(X, m);
if fLabels = nil then
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
var n := probs.RowCount;
SetLength(Result, n);
@ -7131,7 +7134,7 @@ begin
best := cls;
end;
Result[i] := fClasses[best];
Result[i] := fLabels.ClassValueAt(best);
end;
end;

View file

@ -879,21 +879,23 @@ end;
function ClassificationDataPipeline.PredictLabels(df: DataFrame): array of string;
begin
if df = nil then
ArgumentNullError(ER_ARG_NULL, 'df');
if not fFitted then
NotFittedError(ER_FIT_NOT_CALLED);
var encoded := Predict(df);
var classes := GetClassLabels;
Result := new string[encoded.Length];
for var i := 0 to encoded.Length - 1 do
begin
var idx := encoded[i];
if (idx < 0) or (idx >= classes.Length) then
Error(ER_LABEL_INDEX_OUT_OF_RANGE, idx, classes.Length);
Result[i] := classes[idx];
end;
if fModel = nil then
ArgumentError(ER_MODEL_NULL);
if not (fModel is IClassifier) then
Error(ER_PREDICT_NOT_SUPPORTED);
var current := Transform(df);
var X := current.ToMatrix(fFinalFeatures);
X := TransformMatrix(X);
Result := (fModel as IClassifier).PredictLabels(X);
end;
function ClassificationDataPipeline.PredictProba(df: DataFrame): Matrix;