2020-10-03 18:03:48 +00:00
|
|
|
unit GridProcs;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
Classes, SysUtils, Grids,
|
|
|
|
Globals, DictionaryUnit;
|
|
|
|
|
2020-10-17 22:42:41 +00:00
|
|
|
function CollectVecValues(AGrid: TStringGrid; AColIndex: Integer;
|
2020-10-03 21:49:09 +00:00
|
|
|
AColCheck: IntDyneVec = nil): DblDyneVec;
|
2020-10-03 18:03:48 +00:00
|
|
|
|
2020-10-17 22:42:41 +00:00
|
|
|
function CollectMatValues(AGrid: TStringGrid; AColIndices: IntDyneVec): DblDyneMat;
|
|
|
|
|
2020-10-03 18:03:48 +00:00
|
|
|
procedure GetMinMax(AGrid: TStringGrid; AColIndex: Integer;
|
|
|
|
const AColCheck: IntDyneVec; out AMin, AMax: Double);
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
function GetVariableIndex(AGrid: TStringGrid; const AVarName: String): Integer;
|
|
|
|
|
2020-10-03 18:03:48 +00:00
|
|
|
function GoodRecord(AGrid: TStringGrid; ARow: integer;
|
|
|
|
const AColCheck: IntDyneVec): boolean;
|
|
|
|
|
|
|
|
function IsEmptyNumericValue(AGrid: TStringGrid; ARow, ACol: Integer): Boolean;
|
|
|
|
|
|
|
|
function IsFiltered(AGrid: TStringGrid; ARow: integer): boolean;
|
|
|
|
|
|
|
|
function IsMissingValueCode(AGrid: TStringGrid; ARow, ACol: Integer): Boolean;
|
|
|
|
|
2020-10-12 21:53:18 +00:00
|
|
|
function IsNumericCol(AColIndex: Integer): Boolean;
|
|
|
|
|
2020-10-03 18:03:48 +00:00
|
|
|
function ValidValue(AGrid: TStringGrid; ARow, ACol: integer): boolean;
|
|
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
|
|
|
Math;
|
|
|
|
|
|
|
|
{ Extracts the values in the given column from the grid and returns them as an
|
|
|
|
array.
|
|
|
|
Cells which are filtered or empty are not considered. This check is extended
|
|
|
|
over all columns specified by the column indices in AColCheck; AColCheck
|
|
|
|
should be empty to consider only the current column.
|
|
|
|
Non-numeric values in the considered cell will raise an exception.
|
|
|
|
|
|
|
|
NOTE: AColCheck must not be overdimensioned! }
|
2020-10-17 22:42:41 +00:00
|
|
|
function CollectVecValues(AGrid: TStringGrid; AColIndex: Integer; AColCheck: IntDyneVec): DblDyneVec;
|
2020-10-03 18:03:48 +00:00
|
|
|
var
|
|
|
|
row, n: Integer;
|
2020-10-03 21:49:09 +00:00
|
|
|
val: Double;
|
2020-10-03 18:03:48 +00:00
|
|
|
begin
|
|
|
|
SetLength(Result, AGrid.RowCount);
|
|
|
|
n := 0;
|
|
|
|
for row := 1 to AGrid.RowCount-1 do
|
|
|
|
begin
|
|
|
|
if Length(AColCheck) = 0 then
|
|
|
|
begin
|
|
|
|
if not ValidValue(AGrid, row, AColIndex) then continue;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
if not GoodRecord(AGrid, row, AColCheck) then continue;
|
|
|
|
end;
|
2020-10-03 21:49:09 +00:00
|
|
|
if TryStrToFloat(trim(AGrid.Cells[AColIndex, row]), val) then
|
|
|
|
Result[n] := val
|
|
|
|
else
|
|
|
|
raise ELazStats.CreateFmt('Non-numeric string "%s" in column %d, row %d',
|
|
|
|
[AGrid.Cells[AColIndex, row], AColIndex, row]);
|
2020-10-03 18:03:48 +00:00
|
|
|
inc(n);
|
|
|
|
end;
|
|
|
|
SetLength(Result, n);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-10-17 22:42:41 +00:00
|
|
|
{ Extracts the grid values from the columns with indices given by AColIndices
|
|
|
|
and puts them into the columns of the result matrix.
|
|
|
|
This means: The result matrix contains the variables as columns and the
|
|
|
|
cases as rows. }
|
|
|
|
function CollectMatValues(AGrid: TStringGrid; AColIndices: IntDyneVec): DblDyneMat;
|
|
|
|
var
|
|
|
|
nr, r, c, i, j: Integer;
|
|
|
|
val: Double;
|
|
|
|
begin
|
|
|
|
SetLength(Result, AGrid.RowCount, Length(AColIndices));
|
|
|
|
nr := 0;
|
|
|
|
for r:= 1 to AGrid.RowCount-1 do
|
|
|
|
begin
|
|
|
|
if not GoodRecord(AGrid, r, AColIndices) then Continue;
|
|
|
|
i := r - 1;
|
|
|
|
for j := 0 to High(AColIndices) do
|
|
|
|
begin
|
|
|
|
c := AColIndices[j];
|
|
|
|
if TryStrToFloat(trim(AGrid.Cells[c, r]), val) then
|
|
|
|
Result[i, j] := val;
|
|
|
|
end;
|
|
|
|
inc(nr); // count the number of rows in the matrix.
|
|
|
|
end;
|
|
|
|
SetLength(Result, nr);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-10-03 18:03:48 +00:00
|
|
|
{ Determines the minimum and maximum of the values in the specified column of
|
|
|
|
the grid. Rows with "invalid" data are ignored. If AColCheck contains other
|
|
|
|
column indices these cells must be "valid", too. }
|
|
|
|
procedure GetMinMax(AGrid: TStringGrid; AColIndex: Integer;
|
|
|
|
const AColCheck: IntDyneVec; out AMin, AMax: Double);
|
|
|
|
var
|
|
|
|
row: Integer;
|
|
|
|
value: Double;
|
|
|
|
begin
|
|
|
|
AMin := Infinity;
|
|
|
|
AMax := -Infinity;
|
|
|
|
for row := 1 to AGrid.RowCount-1 do
|
|
|
|
begin
|
|
|
|
if Length(AColCheck) = 0 then
|
|
|
|
begin
|
|
|
|
if not ValidValue(AGrid, row, AColIndex) then continue;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
if not GoodRecord(AGrid, row, AColCheck) then continue;
|
|
|
|
end;
|
|
|
|
value := StrToFloat(trim(AGrid.Cells[AColIndex, row]));
|
|
|
|
if value < AMin then AMin := value;
|
|
|
|
if value > AMax then AMax := value;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
{ Finds the index of the variable with the specified name among the columns of
|
|
|
|
the grid. }
|
|
|
|
function GetVariableIndex(AGrid: TStringGrid; const AVarName: String): Integer;
|
|
|
|
begin
|
2020-10-17 22:42:41 +00:00
|
|
|
if AVarName <> '' then
|
|
|
|
Result := AGrid.Rows[0].IndexOf(AVarName)
|
|
|
|
else
|
|
|
|
Result := -1;
|
2020-10-09 21:35:35 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-10-03 18:03:48 +00:00
|
|
|
{ Checks whether all cells specified for the given row in the columns listed in
|
|
|
|
the GridPos array are "valid": not filtered and not empty }
|
|
|
|
function GoodRecord(AGrid: TStringGrid; ARow: integer;
|
|
|
|
const AColCheck: IntDyneVec): boolean;
|
|
|
|
var
|
|
|
|
i, j: integer;
|
|
|
|
begin
|
|
|
|
Result := true;
|
|
|
|
for i := 0 to High(AColCheck) do
|
|
|
|
begin
|
|
|
|
j := AColCheck[i];
|
|
|
|
if not ValidValue(AGrid, ARow, j) then
|
|
|
|
Result := false;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ Checks whether the cell in the given row in the given numeric column is empty. }
|
|
|
|
function IsEmptyNumericValue(AGrid: TStringGrid; ARow, ACol: Integer): Boolean;
|
|
|
|
var
|
|
|
|
value: String;
|
|
|
|
isStringField: Boolean;
|
|
|
|
begin
|
|
|
|
value := Trim(AGrid.Cells[ACol, ARow]);
|
|
|
|
isStringField := DictionaryFrm.DictGrid.Cells[4, ACol] = 'S';
|
|
|
|
Result := not IsStringField and (value = '');
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ Checks whether the specified row is "filtered". Two criteria are needed for
|
|
|
|
a row to be filtered:
|
|
|
|
- The cell in column FilterCol (global value) must contain the text 'NO'.
|
|
|
|
- The global variable "FilterOn" must be TRUE. }
|
|
|
|
function IsFiltered(AGrid: TStringGrid; ARow: integer): boolean;
|
|
|
|
begin
|
|
|
|
Result := FilterOn and (Trim(AGrid.Cells[FilterCol, ARow]) = 'NO');
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ Checks whether specified cell contains the "missing value code" defined by
|
|
|
|
the Dictionary }
|
|
|
|
function IsMissingValueCode(AGrid: TStringGrid; ARow, ACol: Integer): Boolean;
|
|
|
|
var
|
|
|
|
missingCode: String;
|
|
|
|
value: String;
|
|
|
|
begin
|
2020-10-17 22:42:41 +00:00
|
|
|
if ACol < DictionaryFrm.DictGrid.RowCount then
|
|
|
|
begin
|
|
|
|
missingCode := Trim(DictionaryFrm.DictGrid.Cells[6, ACol]);
|
|
|
|
value := Trim(AGrid.Cells[ACol, ARow]);
|
|
|
|
Result := (value = missingCode);
|
|
|
|
end else
|
|
|
|
Result := false;
|
2020-10-03 18:03:48 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-10-12 21:53:18 +00:00
|
|
|
{ Checks in the dictionary whether the variable in the specified grid column has
|
|
|
|
either data type float or integer. }
|
|
|
|
function IsNumericCol(AColIndex: Integer): Boolean;
|
|
|
|
var
|
|
|
|
typeCode: String;
|
|
|
|
begin
|
2020-10-17 22:42:41 +00:00
|
|
|
if AColIndex < DictionaryFrm.DictGrid.RowCount then
|
|
|
|
begin
|
|
|
|
typeCode := Trim(DictionaryFrm.DictGrid.Cells[4, AColIndex]);
|
|
|
|
Result := (typeCode = 'F') or (typeCode = 'I');
|
|
|
|
end else
|
|
|
|
Result := false;
|
2020-10-12 21:53:18 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-10-03 18:03:48 +00:00
|
|
|
{ Checks wheter the value in cell at the given column and row is a not-filtered,
|
|
|
|
non-empty number.
|
|
|
|
NOTE: non-numeric characters in a numeric field are not taken into account! }
|
|
|
|
function ValidValue(AGrid: TStringGrid; ARow, ACol: integer): boolean;
|
|
|
|
begin
|
|
|
|
Result := not (
|
|
|
|
IsFiltered(AGrid, ARow) or // filtering is active and row is marked to be excluded
|
|
|
|
IsEmptyNumericValue(AGrid, ARow, aCol) or // column is numeric, but cell is empty
|
|
|
|
IsMissingValueCode(AGrid, ARow, ACol) // cell contains the "missing value code"
|
|
|
|
);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
end.
|
|
|
|
|