m.Print - исправлен вывод
DataFrame - Stats, Describe Примеры на датафрейм
This commit is contained in:
parent
828484f15a
commit
74f69500fa
|
|
@ -6,23 +6,16 @@ id,created_at,name
|
|||
1,2024-01-15,Alice
|
||||
2,2024-01-16 12:30:00,Bob
|
||||
''';
|
||||
|
||||
var schema := new Dictionary<string, ColumnType>;
|
||||
schema['created_at'] := ColumnType.ctDateTime;
|
||||
|
||||
var df1 := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
schema := schema
|
||||
var df1 := DataFrame.FromCsvText(
|
||||
text,
|
||||
columnTypes := Dict('created_at' to ColumnType.ctDateTime)
|
||||
);
|
||||
|
||||
Println('Явная схема:');
|
||||
df1.Print;
|
||||
Println(df1.DateTime('created_at')[0].Year);
|
||||
|
||||
var df2 := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
inferTypes := True
|
||||
);
|
||||
var df2 := DataFrame.FromCsvText(text);
|
||||
|
||||
Println;
|
||||
Println('Автоопределение:');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
uses MLABC;
|
||||
uses MLABC;
|
||||
|
||||
begin
|
||||
var text := '''
|
||||
|
|
@ -7,10 +7,7 @@ id,created_at,name
|
|||
2,16.01.2024 12:30:00,Bob
|
||||
''';
|
||||
|
||||
var df := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
inferTypes := True
|
||||
);
|
||||
var df := DataFrame.FromCsvText(text);
|
||||
|
||||
df.Print;
|
||||
Println;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
uses MLABC;
|
||||
uses MLABC;
|
||||
|
||||
begin
|
||||
var text := '''
|
||||
|
|
@ -8,10 +8,7 @@ id,created_at,name
|
|||
3,17.01.2024 09:15:00,Charlie
|
||||
''';
|
||||
|
||||
var df := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
inferTypes := True
|
||||
);
|
||||
var df := DataFrame.FromCsvText(text);
|
||||
|
||||
var sorted := df.SortBy('created_at');
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ id,created_at,name
|
|||
3,17.01.2024 09:15:00,Charlie
|
||||
''';
|
||||
|
||||
var df := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
inferTypes := True
|
||||
);
|
||||
var df := DataFrame.FromCsvText(text);
|
||||
|
||||
var filtered := df.Filter(cur -> cur.DateTime('created_at') >= DateTime.Create(2024, 1, 16));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
uses MLABC;
|
||||
uses MLABC;
|
||||
|
||||
begin
|
||||
var text := '''
|
||||
|
|
@ -9,10 +9,7 @@ id,created_at,name
|
|||
4,16.01.2024 12:30:00,Diana
|
||||
''';
|
||||
|
||||
var df := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
inferTypes := True
|
||||
);
|
||||
var df := DataFrame.FromCsvText(text);
|
||||
|
||||
var grouped := df.GroupBy('created_at').Count;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
uses MLABC;
|
||||
uses MLABC;
|
||||
|
||||
begin
|
||||
var text := '''
|
||||
|
|
@ -9,10 +9,7 @@ id,created_at,name
|
|||
4,05.03.2024 09:15:00,Diana
|
||||
''';
|
||||
|
||||
var df := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
inferTypes := True
|
||||
);
|
||||
var df := DataFrame.FromCsvText(text);
|
||||
|
||||
var df2 := df.WithColumnInt(
|
||||
'year',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
uses MLABC, DataFrameABC;
|
||||
uses MLABC, DataFrameABC;
|
||||
|
||||
begin
|
||||
var text := '''
|
||||
|
|
@ -8,10 +8,7 @@ id,created_at,name
|
|||
3,05.03.2024 09:15:45,Charlie
|
||||
''';
|
||||
|
||||
var df := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
inferTypes := True
|
||||
);
|
||||
var df := DataFrame.FromCsvText(text);
|
||||
|
||||
var df2 := df.WithDatePart('created_at', 'year', dpYear);
|
||||
var df3 := df2.WithDateParts('created_at', [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
uses MLABC, DataFrameABC, DataFrameABCCore;
|
||||
uses MLABC, DataFrameABC, DataFrameABCCore;
|
||||
uses TestHelpers in '..\TestHelpers.pas';
|
||||
|
||||
begin
|
||||
|
|
@ -34,3 +34,4 @@ Count
|
|||
Check(vc2.Int('Frequency')[0] = 2, 'ValueCounts conflict first frequency mismatch');
|
||||
CheckSchemaMatchesColumns(vc2);
|
||||
end.
|
||||
|
||||
|
|
|
|||
|
|
@ -301,15 +301,11 @@ type
|
|||
function MeanVariance(selector: DataFrameCursor -> DataValue): (real, real);
|
||||
|
||||
/// Возвращает статистику столбца по индексу
|
||||
function Describe(colIndex: integer): DescribeStats;
|
||||
function Stats(colIndex: integer): DescribeStats;
|
||||
/// Возвращает статистику столбца по имени
|
||||
function Describe(colName: string): DescribeStats;
|
||||
/// Возвращает статистику по нескольким столбцам по именам
|
||||
function Describe(colNames: array of string): Dictionary<string, DescribeStats>;
|
||||
/// Возвращает статистику по нескольким столбцам по индексам
|
||||
function Describe(colIndices: array of integer): Dictionary<integer, DescribeStats>;
|
||||
/// Возвращает статистику по всем числовым столбцам
|
||||
function DescribeAll: Dictionary<string, DescribeStats>;
|
||||
function Stats(colName: string): DescribeStats;
|
||||
/// Возвращает таблицу описательной статистики по всем числовым столбцам
|
||||
function Describe: DataFrame;
|
||||
|
||||
/// Возвращает уникальные непустые значения столбца
|
||||
/// в порядке первого появления
|
||||
|
|
@ -320,6 +316,8 @@ type
|
|||
/// Результат сортируется по убыванию Count,
|
||||
/// при равенстве частот сохраняется порядок первого появления
|
||||
function ValueCounts(colName: string): DataFrame;
|
||||
/// Возвращает таблицу частот значений столбца с явным именем столбца счётчиков
|
||||
function ValueCounts(colName: string; countColName: string): DataFrame;
|
||||
/// Возвращает число пропусков в столбце
|
||||
function MissingCount(colName: string): integer;
|
||||
/// Возвращает таблицу с числом пропусков по всем столбцам
|
||||
|
|
@ -462,20 +460,40 @@ type
|
|||
procedure PrintInfo;
|
||||
|
||||
/// Загружает DataFrame из CSV файла
|
||||
static function FromCsv(filename: string): DataFrame;
|
||||
static function FromCsv(
|
||||
filename: string;
|
||||
delimiter: char := ',';
|
||||
hasHeader: boolean := true;
|
||||
columnTypes: Dictionary<string, ColumnType> := nil;
|
||||
categoricalColumns: array of string := nil
|
||||
): DataFrame;
|
||||
/// Загружает DataFrame из многострочной строки в формате CSV
|
||||
static function FromCsvText(text: string): DataFrame;
|
||||
static function FromCsvText(
|
||||
text: string;
|
||||
delimiter: char := ',';
|
||||
hasHeader: boolean := true;
|
||||
columnTypes: Dictionary<string, ColumnType> := nil;
|
||||
categoricalColumns: array of string := nil
|
||||
): DataFrame;
|
||||
/// Загружает DataFrame из листа ODS-файла
|
||||
static function FromODS(filename: string; sheet: string := ''): DataFrame;
|
||||
static function FromODS(
|
||||
filename: string;
|
||||
sheet: string := '';
|
||||
hasHeader: boolean := true;
|
||||
columnTypes: Dictionary<string, ColumnType> := nil;
|
||||
categoricalColumns: array of string := nil
|
||||
): DataFrame;
|
||||
/// Сохраняет DataFrame в csv-файл
|
||||
procedure ToCsv(filename: string);
|
||||
private
|
||||
/// Проверяет валидность индекса столбца
|
||||
procedure CheckColumnIndex(colIndex: integer);
|
||||
function DescribeByIndices(colIndices: array of integer): Dictionary<integer, DescribeStats>;
|
||||
function DescribeAll: Dictionary<string, DescribeStats>;
|
||||
/// Добавляет строку из курсора
|
||||
//procedure AppendRowFromCursor(src: DataFrame; cur: DataFrameCursor);
|
||||
end;
|
||||
|
||||
|
||||
/// Тип операции группировки
|
||||
AggregationKind = (akCount, akSum, akMean, akStd, akMin, akMax);
|
||||
|
||||
|
|
@ -492,7 +510,6 @@ type
|
|||
function Min(colName: string): real;
|
||||
function Max(colName: string): real;
|
||||
end;
|
||||
|
||||
/// Интерфейс для группировки данных
|
||||
IGroupByContext = interface
|
||||
/// Возвращает DataFrame с количеством строк в каждой группе
|
||||
|
|
@ -568,7 +585,7 @@ type
|
|||
/// Эквивалентна Quantile(..., 0.5)
|
||||
static function Median(df: DataFrame; colName: string): real;
|
||||
end;
|
||||
|
||||
|
||||
type
|
||||
/// Статический класс для загрузки данных из CSV файлов
|
||||
CsvLoader = static class
|
||||
|
|
@ -581,7 +598,7 @@ type
|
|||
missingValues: array of string := nil; // Значения, считающиеся пропущенными
|
||||
trimWhitespace: boolean := true;
|
||||
strict: boolean := False; // Строгий режим (проверка формата) - бросать ли исключения
|
||||
schema: Dictionary<string, ColumnType> := nil;
|
||||
columnTypes: Dictionary<string, ColumnType> := nil;
|
||||
sampleSize: integer := 1000;
|
||||
ignoreColumns: array of string := nil;
|
||||
inferTypes: boolean := True;
|
||||
|
|
@ -602,7 +619,7 @@ type
|
|||
missingValues: array of string := nil;
|
||||
trimWhitespace: boolean := true;
|
||||
strict: boolean := False;
|
||||
schema: Dictionary<string, ColumnType> := nil;
|
||||
columnTypes: Dictionary<string, ColumnType> := nil;
|
||||
sampleSize: integer := 1000;
|
||||
ignoreColumns: array of string := nil;
|
||||
inferTypes: boolean := True;
|
||||
|
|
@ -763,6 +780,8 @@ const
|
|||
'Нижняя граница Clip больше верхней!!Clip lower bound is greater than upper bound';
|
||||
ER_CLIP_INT_BOUNDS_NON_INTEGER =
|
||||
'Для целочисленного столбца границы Clip должны быть целыми!!Clip bounds for Int column must be integers';
|
||||
ER_VALUECOUNTS_COUNTCOL_EMPTY =
|
||||
'Имя столбца счётчиков пусто!!ValueCounts count column name is empty';
|
||||
|
||||
function FormatDateTimeForPrint(dt: System.DateTime; fmt: string): string; forward;
|
||||
function CollectSelectorValues(df: DataFrame; selector: DataFrameCursor -> DataValue): List<real>; forward;
|
||||
|
|
@ -3127,7 +3146,7 @@ begin
|
|||
Result := (mean, acc / (cnt - 1));
|
||||
end;
|
||||
|
||||
function DataFrame.Describe(colIndex: integer): DescribeStats;
|
||||
function DataFrame.Stats(colIndex: integer): DescribeStats;
|
||||
begin
|
||||
CheckColumnIndex(colIndex);
|
||||
|
||||
|
|
@ -3194,30 +3213,17 @@ begin
|
|||
Result.Max := mx;
|
||||
end;
|
||||
|
||||
function DataFrame.Describe(colName: string): DescribeStats;
|
||||
function DataFrame.Stats(colName: string): DescribeStats;
|
||||
begin
|
||||
Result := Describe(ColumnIndex(colName));
|
||||
Result := Stats(ColumnIndex(colName));
|
||||
end;
|
||||
|
||||
function DataFrame.Describe(colNames: array of string): Dictionary<string, DescribeStats>;
|
||||
begin
|
||||
var res := new Dictionary<string, DescribeStats>;
|
||||
|
||||
foreach var name in colNames do
|
||||
begin
|
||||
var idx := ColumnIndex(name);
|
||||
res[name] := Describe(idx);
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function DataFrame.Describe(colIndices: array of integer): Dictionary<integer, DescribeStats>;
|
||||
function DataFrame.DescribeByIndices(colIndices: array of integer): Dictionary<integer, DescribeStats>;
|
||||
begin
|
||||
var res := new Dictionary<integer, DescribeStats>;
|
||||
|
||||
foreach var i in colIndices do
|
||||
res[i] := Describe(i);
|
||||
res[i] := Stats(i);
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
|
@ -3229,12 +3235,57 @@ begin
|
|||
for var i := 0 to ColumnCount - 1 do
|
||||
case columns[i].Info.ColType of
|
||||
ctInt, ctFloat:
|
||||
res[columns[i].Info.Name] := Describe(i);
|
||||
res[columns[i].Info.Name] := Stats(i);
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function DataFrame.Describe: DataFrame;
|
||||
begin
|
||||
var stats := DescribeAll;
|
||||
var names := new List<string>;
|
||||
var counts := new List<integer>;
|
||||
var means := new List<real>;
|
||||
var stds := new List<real>;
|
||||
var mins := new List<real>;
|
||||
var maxs := new List<real>;
|
||||
var allInt := true;
|
||||
|
||||
for var i := 0 to ColumnCount - 1 do
|
||||
case columns[i].Info.ColType of
|
||||
ctInt, ctFloat:
|
||||
begin
|
||||
var name := columns[i].Info.Name;
|
||||
var st := stats[name];
|
||||
names.Add(name);
|
||||
counts.Add(st.Count);
|
||||
means.Add(st.Mean);
|
||||
stds.Add(st.Std);
|
||||
mins.Add(st.Min);
|
||||
maxs.Add(st.Max);
|
||||
if columns[i].Info.ColType <> ctInt then
|
||||
allInt := false;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := new DataFrame;
|
||||
Result.AddStrColumn('Column', names.ToArray, nil);
|
||||
Result.AddIntColumn('Count', counts.ToArray, nil);
|
||||
Result.AddFloatColumn('Mean', means.ToArray, nil);
|
||||
Result.AddFloatColumn('Std', stds.ToArray, nil);
|
||||
if allInt then
|
||||
begin
|
||||
Result.AddIntColumn('Min', mins.Select(x -> Round(x)).ToArray, nil);
|
||||
Result.AddIntColumn('Max', maxs.Select(x -> Round(x)).ToArray, nil);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Result.AddFloatColumn('Min', mins.ToArray, nil);
|
||||
Result.AddFloatColumn('Max', maxs.ToArray, nil);
|
||||
end;
|
||||
end;
|
||||
|
||||
function DataFrame.Unique(colName: string): array of DataValue;
|
||||
begin
|
||||
var ci := ColumnIndex(colName);
|
||||
|
|
@ -3300,13 +3351,30 @@ begin
|
|||
end;
|
||||
|
||||
function DataFrame.ValueCounts(colName: string): DataFrame;
|
||||
begin
|
||||
Result := ValueCounts(colName, 'Count');
|
||||
end;
|
||||
|
||||
function ValueCounts(Self: Column): DataFrame; extensionmethod;
|
||||
begin
|
||||
var df := new DataFrame;
|
||||
df.AddColumnAlias(Self);
|
||||
Result := df.ValueCounts(Self.Info.Name);
|
||||
end;
|
||||
|
||||
function DataFrame.ValueCounts(colName: string; countColName: string): DataFrame;
|
||||
begin
|
||||
var ci := ColumnIndex(colName);
|
||||
var col := GetColumn(ci);
|
||||
var counts := new List<integer>;
|
||||
var order := new List<integer>;
|
||||
var res := new DataFrame;
|
||||
var countColName := if colName = 'Count' then 'Frequency' else 'Count';
|
||||
if countColName = nil then
|
||||
ArgumentNullError('countColName');
|
||||
if countColName = '' then
|
||||
ArgumentError(ER_VALUECOUNTS_COUNTCOL_EMPTY);
|
||||
if countColName = colName then
|
||||
ArgumentError(ER_COLUMN_ALREADY_EXISTS, countColName);
|
||||
|
||||
case col.Info.ColType of
|
||||
ctInt:
|
||||
|
|
@ -5317,30 +5385,75 @@ begin
|
|||
Result := res;
|
||||
end;
|
||||
|
||||
static function DataFrame.FromCsv(filename: string): DataFrame;
|
||||
static function DataFrame.FromCsv(
|
||||
filename: string;
|
||||
delimiter: char;
|
||||
hasHeader: boolean;
|
||||
columnTypes: Dictionary<string, ColumnType>;
|
||||
categoricalColumns: array of string
|
||||
): DataFrame;
|
||||
begin
|
||||
Result := CsvLoader.Load(filename);
|
||||
end;
|
||||
|
||||
static function DataFrame.FromCsvText(text: string): DataFrame;
|
||||
begin
|
||||
Result := CsvLoader.LoadFromLines(text.ToLines);
|
||||
end;
|
||||
|
||||
static function DataFrame.FromODS(filename: string; sheet: string): DataFrame;
|
||||
begin
|
||||
Result := LoadFromRows(
|
||||
ODSReader.ReadSheet(filename, sheet),
|
||||
true,
|
||||
Result := CsvLoader.Load(
|
||||
filename,
|
||||
delimiter,
|
||||
hasHeader,
|
||||
nil,
|
||||
nil,
|
||||
true,
|
||||
false,
|
||||
nil,
|
||||
columnTypes,
|
||||
1000,
|
||||
nil,
|
||||
true,
|
||||
nil,
|
||||
categoricalColumns
|
||||
);
|
||||
end;
|
||||
|
||||
static function DataFrame.FromCsvText(
|
||||
text: string;
|
||||
delimiter: char;
|
||||
hasHeader: boolean;
|
||||
columnTypes: Dictionary<string, ColumnType>;
|
||||
categoricalColumns: array of string
|
||||
): DataFrame;
|
||||
begin
|
||||
Result := CsvLoader.LoadFromLines(
|
||||
text.ToLines,
|
||||
delimiter,
|
||||
hasHeader,
|
||||
nil,
|
||||
true,
|
||||
false,
|
||||
columnTypes,
|
||||
1000,
|
||||
nil,
|
||||
true,
|
||||
nil,
|
||||
categoricalColumns
|
||||
);
|
||||
end;
|
||||
|
||||
static function DataFrame.FromODS(
|
||||
filename: string;
|
||||
sheet: string;
|
||||
hasHeader: boolean;
|
||||
columnTypes: Dictionary<string, ColumnType>;
|
||||
categoricalColumns: array of string
|
||||
): DataFrame;
|
||||
begin
|
||||
Result := LoadFromRows(
|
||||
ODSReader.ReadSheet(filename, sheet),
|
||||
hasHeader,
|
||||
nil,
|
||||
true,
|
||||
false,
|
||||
columnTypes,
|
||||
1000,
|
||||
nil,
|
||||
true,
|
||||
nil,
|
||||
categoricalColumns,
|
||||
false,
|
||||
100,
|
||||
0.2,
|
||||
|
|
@ -6429,7 +6542,17 @@ begin
|
|||
res.AddIntColumn(colNameAgg, counts[c], nil);
|
||||
|
||||
akSum:
|
||||
res.AddFloatColumn(colNameAgg, sums[c], nil);
|
||||
begin
|
||||
if colsIsInt[c] then
|
||||
begin
|
||||
var arr := new integer[n];
|
||||
for var g := 0 to n - 1 do
|
||||
arr[g] := Round(sums[c][g]);
|
||||
res.AddIntColumn(colNameAgg, arr, nil);
|
||||
end
|
||||
else
|
||||
res.AddFloatColumn(colNameAgg, sums[c], nil);
|
||||
end;
|
||||
|
||||
akMean:
|
||||
begin
|
||||
|
|
@ -6451,10 +6574,30 @@ begin
|
|||
end;
|
||||
|
||||
akMin:
|
||||
res.AddFloatColumn(colNameAgg, mins[c], nil);
|
||||
begin
|
||||
if colsIsInt[c] then
|
||||
begin
|
||||
var arr := new integer[n];
|
||||
for var g := 0 to n - 1 do
|
||||
arr[g] := Round(mins[c][g]);
|
||||
res.AddIntColumn(colNameAgg, arr, nil);
|
||||
end
|
||||
else
|
||||
res.AddFloatColumn(colNameAgg, mins[c], nil);
|
||||
end;
|
||||
|
||||
akMax:
|
||||
res.AddFloatColumn(colNameAgg, maxs[c], nil);
|
||||
begin
|
||||
if colsIsInt[c] then
|
||||
begin
|
||||
var arr := new integer[n];
|
||||
for var g := 0 to n - 1 do
|
||||
arr[g] := Round(maxs[c][g]);
|
||||
res.AddIntColumn(colNameAgg, arr, nil);
|
||||
end
|
||||
else
|
||||
res.AddFloatColumn(colNameAgg, maxs[c], nil);
|
||||
end;
|
||||
end;
|
||||
|
||||
// метаданные
|
||||
|
|
@ -6462,6 +6605,8 @@ begin
|
|||
|
||||
if kind = akCount then
|
||||
types.Add(ctInt)
|
||||
else if (kind in [akSum, akMin, akMax]) and colsIsInt[c] then
|
||||
types.Add(ctInt)
|
||||
else
|
||||
types.Add(ctFloat);
|
||||
|
||||
|
|
@ -7638,6 +7783,10 @@ begin
|
|||
if hasHeader then
|
||||
begin
|
||||
headers := Copy(current);
|
||||
if trimWhitespace then
|
||||
for var j := 0 to headers.Length - 1 do
|
||||
if headers[j] <> nil then
|
||||
headers[j] := headers[j].Trim;
|
||||
originalColCount := headers.Length;
|
||||
end
|
||||
else
|
||||
|
|
@ -8015,7 +8164,7 @@ static function CSVLoader.LoadFromLines(
|
|||
missingValues: array of string;
|
||||
trimWhitespace: boolean;
|
||||
strict: boolean;
|
||||
schema: Dictionary<string, ColumnType>;
|
||||
columnTypes: Dictionary<string, ColumnType>;
|
||||
sampleSize: integer;
|
||||
ignoreColumns: array of string;
|
||||
inferTypes: boolean;
|
||||
|
|
@ -8109,6 +8258,9 @@ begin
|
|||
if (s.Length >= 2) and (s[1] = '"') and (s[s.Length] = '"') then
|
||||
s := s.Substring(1, s.Length - 2);
|
||||
|
||||
if trimWhitespace then
|
||||
s := s.Trim;
|
||||
|
||||
parts[j] := s;
|
||||
end;
|
||||
|
||||
|
|
@ -8175,14 +8327,14 @@ begin
|
|||
begin
|
||||
var name := headers[j];
|
||||
|
||||
if (schema <> nil) and schema.ContainsKey(name) then
|
||||
if (columnTypes <> nil) and columnTypes.ContainsKey(name) then
|
||||
begin
|
||||
canBool[j] := false;
|
||||
canInt[j] := false;
|
||||
canFloat[j] := false;
|
||||
canDateTime[j] := false;
|
||||
|
||||
case schema[name] of
|
||||
case columnTypes[name] of
|
||||
ctBool: canBool[j] := true;
|
||||
ctInt: canInt[j] := true;
|
||||
ctFloat: canFloat[j] := true;
|
||||
|
|
@ -8231,6 +8383,8 @@ begin
|
|||
if (s.Length >= 2) and (s[1] = '"') and (s[s.Length] = '"') then
|
||||
s := s.Substring(1, s.Length - 2);
|
||||
|
||||
if trimWhitespace then
|
||||
s := s.Trim;
|
||||
parts[j] := s;
|
||||
end;
|
||||
|
||||
|
|
@ -8266,7 +8420,7 @@ begin
|
|||
|
||||
if not inferTypes then continue;
|
||||
|
||||
if (schema <> nil) and schema.ContainsKey(name) then
|
||||
if (columnTypes <> nil) and columnTypes.ContainsKey(name) then
|
||||
continue;
|
||||
|
||||
if (forceStrSet <> nil) and (name in forceStrSet) then
|
||||
|
|
@ -8303,7 +8457,7 @@ begin
|
|||
begin
|
||||
var name := headers[j];
|
||||
|
||||
if (schema <> nil) and schema.ContainsKey(name) then continue;
|
||||
if (columnTypes <> nil) and columnTypes.ContainsKey(name) then continue;
|
||||
if (forceStrSet <> nil) and (name in forceStrSet) then continue;
|
||||
if (catSet <> nil) and (name in catSet) then continue;
|
||||
|
||||
|
|
@ -8601,7 +8755,7 @@ static function CSVLoader.Load(filename: string;
|
|||
missingValues: array of string;
|
||||
trimWhitespace: boolean;
|
||||
strict: boolean;
|
||||
schema: Dictionary<string, ColumnType>;
|
||||
columnTypes: Dictionary<string, ColumnType>;
|
||||
sampleSize: integer;
|
||||
ignoreColumns: array of string;
|
||||
inferTypes: boolean;
|
||||
|
|
@ -8626,7 +8780,7 @@ begin
|
|||
missingValues,
|
||||
trimWhitespace,
|
||||
strict,
|
||||
schema,
|
||||
columnTypes,
|
||||
sampleSize,
|
||||
ignoreColumns,
|
||||
inferTypes,
|
||||
|
|
@ -8706,3 +8860,10 @@ begin
|
|||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -206,7 +206,15 @@ type
|
|||
static function operator +(a, b: DataValue): DataValue;
|
||||
static function operator -(a, b: DataValue): DataValue;
|
||||
static function operator *(a, b: DataValue): DataValue;
|
||||
static function operator *(a: DataValue; b: integer): real;
|
||||
static function operator *(a: DataValue; b: real): real;
|
||||
static function operator *(a: integer; b: DataValue): real;
|
||||
static function operator *(a: real; b: DataValue): real;
|
||||
static function operator /(a, b: DataValue): DataValue;
|
||||
static function operator /(a: DataValue; b: integer): real;
|
||||
static function operator /(a: DataValue; b: real): real;
|
||||
static function operator /(a: integer; b: DataValue): real;
|
||||
static function operator /(a: real; b: DataValue): real;
|
||||
end;
|
||||
|
||||
DataFrameCursor = class;
|
||||
|
|
@ -223,6 +231,26 @@ type
|
|||
Info: ColumnInfo;
|
||||
public
|
||||
IsValid: array of boolean; // Флаги валидности (может быть nil)
|
||||
/// Возвращает число валидных (non-NA) значений в столбце
|
||||
function Count: integer; virtual;
|
||||
/// Возвращает число пропусков в столбце
|
||||
function MissingCount: integer; virtual;
|
||||
/// Возвращает минимальное числовое значение столбца
|
||||
function Min: real; virtual;
|
||||
/// Возвращает максимальное числовое значение столбца
|
||||
function Max: real; virtual;
|
||||
/// Возвращает среднее числовое значение столбца
|
||||
function Mean: real; virtual;
|
||||
/// Возвращает медиану числовых значений столбца
|
||||
function Median: real; virtual;
|
||||
/// Возвращает выборочную дисперсию числовых значений столбца
|
||||
function Variance: real; virtual;
|
||||
/// Возвращает выборочное стандартное отклонение числовых значений столбца
|
||||
function Std: real; virtual;
|
||||
/// Возвращает уникальные непустые значения столбца в порядке первого появления
|
||||
function Unique: array of DataValue; virtual;
|
||||
/// Возвращает число различных непустых значений столбца
|
||||
function NUnique: integer; virtual;
|
||||
/// Пытается извлечь i-тое данное из столбца как числовое если это возможно
|
||||
function TryGetNumericValue(i: integer; var value: real): boolean; virtual; abstract;
|
||||
/// Возвращает количество строк в столбце
|
||||
|
|
@ -399,6 +427,7 @@ type
|
|||
end;
|
||||
|
||||
function MergedRightColumnName(leftSchema, rightSchema: DataFrameSchema; rightIndex: integer): string;
|
||||
function ColumnNumericValues(col: Column): List<real>;
|
||||
|
||||
implementation
|
||||
|
||||
|
|
@ -464,6 +493,10 @@ const
|
|||
'Нельзя выполнять арифметические операции для типов {0} и {1}!!Cannot apply arithmetic to types {0} and {1}';
|
||||
ER_DATAVALUE_DIVISION_BY_ZERO =
|
||||
'Деление на ноль!!Division by zero';
|
||||
ER_COLUMN_NUMERIC_REQUIRED =
|
||||
'Столбец "{0}" должен быть числовым!!Column "{0}" must be numeric';
|
||||
ER_COLUMN_NO_VALID_VALUES =
|
||||
'Столбец "{0}" не содержит валидных значений!!Column "{0}" has no valid values';
|
||||
|
||||
//-----------------------------
|
||||
// Сервисные функции
|
||||
|
|
@ -499,6 +532,28 @@ begin
|
|||
Error(ER_COLUMN_NOT_DATETIME);
|
||||
end;
|
||||
|
||||
procedure EnsureNumericColumn(col: Column);
|
||||
begin
|
||||
if (col = nil) or (col.Info = nil) then
|
||||
ArgumentNullError('col');
|
||||
|
||||
if not (col.Info.ColType in [ctInt, ctFloat]) then
|
||||
Error(ER_COLUMN_NUMERIC_REQUIRED, col.Info.Name);
|
||||
end;
|
||||
|
||||
function ColumnNumericValues(col: Column): List<real>;
|
||||
begin
|
||||
EnsureNumericColumn(col);
|
||||
|
||||
Result := new List<real>;
|
||||
for var i := 0 to col.RowCount - 1 do
|
||||
begin
|
||||
var value: real;
|
||||
if col.TryGetNumericValue(i, value) then
|
||||
Result.Add(value);
|
||||
end;
|
||||
end;
|
||||
|
||||
function ColumnTypeName(t: ColumnType): string;
|
||||
begin
|
||||
case t of
|
||||
|
|
@ -834,6 +889,42 @@ begin
|
|||
Result := MakeNumeric(a.Name, a.Float * b.Float, t = ctInt);
|
||||
end;
|
||||
|
||||
static function DataValue.operator *(a: DataValue; b: integer): real;
|
||||
begin
|
||||
if System.Object.ReferenceEquals(a, nil) then
|
||||
ArgumentNullError('a');
|
||||
if not a.fIsValid then
|
||||
Error(ER_VALUE_IS_NA, a.fName);
|
||||
Result := a.Float * b;
|
||||
end;
|
||||
|
||||
static function DataValue.operator *(a: DataValue; b: real): real;
|
||||
begin
|
||||
if System.Object.ReferenceEquals(a, nil) then
|
||||
ArgumentNullError('a');
|
||||
if not a.fIsValid then
|
||||
Error(ER_VALUE_IS_NA, a.fName);
|
||||
Result := a.Float * b;
|
||||
end;
|
||||
|
||||
static function DataValue.operator *(a: integer; b: DataValue): real;
|
||||
begin
|
||||
if System.Object.ReferenceEquals(b, nil) then
|
||||
ArgumentNullError('b');
|
||||
if not b.fIsValid then
|
||||
Error(ER_VALUE_IS_NA, b.fName);
|
||||
Result := a * b.Float;
|
||||
end;
|
||||
|
||||
static function DataValue.operator *(a: real; b: DataValue): real;
|
||||
begin
|
||||
if System.Object.ReferenceEquals(b, nil) then
|
||||
ArgumentNullError('b');
|
||||
if not b.fIsValid then
|
||||
Error(ER_VALUE_IS_NA, b.fName);
|
||||
Result := a * b.Float;
|
||||
end;
|
||||
|
||||
static function DataValue.operator /(a, b: DataValue): DataValue;
|
||||
begin
|
||||
ArithmeticType(a, b);
|
||||
|
|
@ -842,6 +933,50 @@ begin
|
|||
Result := new DataValue(a.Name, a.Float / b.Float);
|
||||
end;
|
||||
|
||||
static function DataValue.operator /(a: DataValue; b: integer): real;
|
||||
begin
|
||||
if System.Object.ReferenceEquals(a, nil) then
|
||||
ArgumentNullError('a');
|
||||
if not a.fIsValid then
|
||||
Error(ER_VALUE_IS_NA, a.fName);
|
||||
if b = 0 then
|
||||
Error(ER_DATAVALUE_DIVISION_BY_ZERO);
|
||||
Result := a.Float / b;
|
||||
end;
|
||||
|
||||
static function DataValue.operator /(a: DataValue; b: real): real;
|
||||
begin
|
||||
if System.Object.ReferenceEquals(a, nil) then
|
||||
ArgumentNullError('a');
|
||||
if not a.fIsValid then
|
||||
Error(ER_VALUE_IS_NA, a.fName);
|
||||
if b = 0 then
|
||||
Error(ER_DATAVALUE_DIVISION_BY_ZERO);
|
||||
Result := a.Float / b;
|
||||
end;
|
||||
|
||||
static function DataValue.operator /(a: integer; b: DataValue): real;
|
||||
begin
|
||||
if System.Object.ReferenceEquals(b, nil) then
|
||||
ArgumentNullError('b');
|
||||
if not b.fIsValid then
|
||||
Error(ER_VALUE_IS_NA, b.fName);
|
||||
if b.Float = 0 then
|
||||
Error(ER_DATAVALUE_DIVISION_BY_ZERO);
|
||||
Result := a / b.Float;
|
||||
end;
|
||||
|
||||
static function DataValue.operator /(a: real; b: DataValue): real;
|
||||
begin
|
||||
if System.Object.ReferenceEquals(b, nil) then
|
||||
ArgumentNullError('b');
|
||||
if not b.fIsValid then
|
||||
Error(ER_VALUE_IS_NA, b.fName);
|
||||
if b.Float = 0 then
|
||||
Error(ER_DATAVALUE_DIVISION_BY_ZERO);
|
||||
Result := a / b.Float;
|
||||
end;
|
||||
|
||||
procedure DataFrameSchema.Print;
|
||||
begin
|
||||
if fNames.Length = 0 then
|
||||
|
|
@ -1052,6 +1187,146 @@ end;
|
|||
// Columns
|
||||
//-----------------------------
|
||||
|
||||
function Column.Count: integer;
|
||||
begin
|
||||
Result := 0;
|
||||
for var i := 0 to RowCount - 1 do
|
||||
if IsValid[i] then
|
||||
Result += 1;
|
||||
end;
|
||||
|
||||
function Column.MissingCount: integer;
|
||||
begin
|
||||
Result := 0;
|
||||
for var i := 0 to RowCount - 1 do
|
||||
if not IsValid[i] then
|
||||
Result += 1;
|
||||
end;
|
||||
|
||||
function Column.Min: real;
|
||||
begin
|
||||
var values := ColumnNumericValues(Self);
|
||||
if values.Count = 0 then
|
||||
Error(ER_COLUMN_NO_VALID_VALUES, Info.Name);
|
||||
Result := values.Min;
|
||||
end;
|
||||
|
||||
function Column.Max: real;
|
||||
begin
|
||||
var values := ColumnNumericValues(Self);
|
||||
if values.Count = 0 then
|
||||
Error(ER_COLUMN_NO_VALID_VALUES, Info.Name);
|
||||
Result := values.Max;
|
||||
end;
|
||||
|
||||
function Column.Mean: real;
|
||||
begin
|
||||
var values := ColumnNumericValues(Self);
|
||||
if values.Count = 0 then
|
||||
exit(0.0);
|
||||
Result := values.Sum / values.Count;
|
||||
end;
|
||||
|
||||
function Column.Median: real;
|
||||
begin
|
||||
var values := ColumnNumericValues(Self);
|
||||
if values.Count = 0 then
|
||||
Error(ER_COLUMN_NO_VALID_VALUES, Info.Name);
|
||||
|
||||
values.Sort;
|
||||
var n := values.Count;
|
||||
if n mod 2 = 1 then
|
||||
Result := values[n div 2]
|
||||
else
|
||||
Result := (values[n div 2 - 1] + values[n div 2]) / 2.0;
|
||||
end;
|
||||
|
||||
function Column.Variance: real;
|
||||
begin
|
||||
var values := ColumnNumericValues(Self);
|
||||
var cnt := values.Count;
|
||||
|
||||
if cnt <= 1 then
|
||||
exit(0.0);
|
||||
|
||||
var mean := values.Sum / cnt;
|
||||
var acc := 0.0;
|
||||
foreach var v in values do
|
||||
begin
|
||||
var d := v - mean;
|
||||
acc += d * d;
|
||||
end;
|
||||
|
||||
Result := acc / (cnt - 1);
|
||||
end;
|
||||
|
||||
function Column.Std: real;
|
||||
begin
|
||||
Result := Sqrt(Variance);
|
||||
end;
|
||||
|
||||
function Column.Unique: array of DataValue;
|
||||
begin
|
||||
var values := new List<DataValue>;
|
||||
|
||||
case Info.ColType of
|
||||
ctInt:
|
||||
begin
|
||||
var c := IntColumn(Self);
|
||||
var seen := new HashSet<integer>;
|
||||
for var i := 0 to c.Data.Length - 1 do
|
||||
if c.IsValid[i] and seen.Add(c.Data[i]) then
|
||||
values.Add(new DataValue(Info.Name, c.Data[i]));
|
||||
end;
|
||||
|
||||
ctFloat:
|
||||
begin
|
||||
var c := FloatColumn(Self);
|
||||
var seen := new HashSet<real>;
|
||||
for var i := 0 to c.Data.Length - 1 do
|
||||
if c.IsValid[i] and seen.Add(c.Data[i]) then
|
||||
values.Add(new DataValue(Info.Name, c.Data[i]));
|
||||
end;
|
||||
|
||||
ctStr:
|
||||
begin
|
||||
var c := StrColumn(Self);
|
||||
var seen := new HashSet<string>;
|
||||
for var i := 0 to c.Data.Length - 1 do
|
||||
if c.IsValid[i] and seen.Add(c.Data[i]) then
|
||||
values.Add(new DataValue(Info.Name, c.Data[i]));
|
||||
end;
|
||||
|
||||
ctBool:
|
||||
begin
|
||||
var c := BoolColumn(Self);
|
||||
var seen := new HashSet<boolean>;
|
||||
for var i := 0 to c.Data.Length - 1 do
|
||||
if c.IsValid[i] and seen.Add(c.Data[i]) then
|
||||
values.Add(new DataValue(Info.Name, c.Data[i]));
|
||||
end;
|
||||
|
||||
ctDateTime:
|
||||
begin
|
||||
var c := DateTimeColumn(Self);
|
||||
var seen := new HashSet<System.DateTime>;
|
||||
for var i := 0 to c.Data.Length - 1 do
|
||||
if c.IsValid[i] and seen.Add(c.Data[i]) then
|
||||
values.Add(new DataValue(Info.Name, c.Data[i]));
|
||||
end;
|
||||
|
||||
else
|
||||
Error(ER_UNKNOWN_COLUMN_TYPE);
|
||||
end;
|
||||
|
||||
Result := values.ToArray;
|
||||
end;
|
||||
|
||||
function Column.NUnique: integer;
|
||||
begin
|
||||
Result := Unique.Length;
|
||||
end;
|
||||
|
||||
constructor IntColumn.Create(name: string; values: array of integer; valid: array of boolean);
|
||||
begin
|
||||
inherited Create;
|
||||
|
|
@ -1475,3 +1750,5 @@ begin
|
|||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,12 @@ type
|
|||
AggregationKind = DataFrameABC.AggregationKind;
|
||||
|
||||
const
|
||||
ctInt = ColumnType.ctInt;
|
||||
ctFloat = ColumnType.ctFloat;
|
||||
ctStr = ColumnType.ctStr;
|
||||
ctBool = ColumnType.ctBool;
|
||||
ctDateTime = ColumnType.ctDateTime;
|
||||
|
||||
akMean = AggregationKind.akMean;
|
||||
akMin = AggregationKind.akMin;
|
||||
akMax = AggregationKind.akMax;
|
||||
|
|
@ -226,3 +232,4 @@ implementation
|
|||
function EncodeLabels(labels: array of string): array of integer := MLUtilsABC.EncodeLabels(labels);
|
||||
|
||||
end.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
|
||||
// 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)
|
||||
|
||||
/// Стандартный модуль
|
||||
|
|
@ -13592,15 +13592,30 @@ end;
|
|||
/// Вывод двумерного вещественного массива по формату :w:f
|
||||
function Print(Self: array [,] of real; w: integer := 7; f: integer := 2): array [,] of real; extensionmethod;
|
||||
begin
|
||||
for var i := 0 to Self.RowCount - 1 do
|
||||
if PrintMatrixWithFormat then
|
||||
begin
|
||||
var widths := new integer[Self.ColCount];
|
||||
for var j := 0 to Self.ColCount - 1 do
|
||||
begin
|
||||
if PrintMatrixWithFormat then
|
||||
Write(FormatValue(Self[i, j], w, f))
|
||||
else Print(Self[i, j]);
|
||||
end;
|
||||
Writeln;
|
||||
widths[j] := w;
|
||||
for var i := 0 to Self.RowCount - 1 do
|
||||
widths[j] := Max(widths[j], FormatValue(Self[i, j], 0, f).Length + 1);
|
||||
end;
|
||||
for var i := 0 to Self.RowCount - 1 do
|
||||
begin
|
||||
for var j := 0 to Self.ColCount - 1 do
|
||||
Write(FormatValue(Self[i, j], 0, f).PadLeft(widths[j]));
|
||||
Writeln;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
for var i := 0 to Self.RowCount - 1 do
|
||||
begin
|
||||
for var j := 0 to Self.ColCount - 1 do
|
||||
Print(Self[i, j]);
|
||||
Writeln;
|
||||
end;
|
||||
end;
|
||||
Result := Self;
|
||||
end;
|
||||
|
|
|
|||
Loading…
Reference in a new issue