Files
lazarus-ccr/applications/lazstats/source/units/gridprocs.pas

226 lines
6.5 KiB
ObjectPascal
Raw Normal View History

unit GridProcs;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Grids,
Globals, DictionaryUnit;
function CollectVecValues(AGrid: TStringGrid; AColIndex: Integer;
AColCheck: IntDyneVec = nil): DblDyneVec;
function CollectMatValues(AGrid: TStringGrid; AColIndices: IntDyneVec): DblDyneMat;
procedure GetMinMax(AGrid: TStringGrid; AColIndex: Integer;
const AColCheck: IntDyneVec; out AMin, AMax: Double);
function GetVariableIndex(AGrid: TStringGrid; const AVarName: String): Integer;
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;
function IsNumericCol(AColIndex: Integer): Boolean;
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! }
function CollectVecValues(AGrid: TStringGrid; AColIndex: Integer; AColCheck: IntDyneVec): DblDyneVec;
var
row, n: Integer;
val: Double;
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;
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]);
inc(n);
end;
SetLength(Result, n);
end;
{ 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;
{ 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;
{ 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
if AVarName <> '' then
Result := AGrid.Rows[0].IndexOf(AVarName)
else
Result := -1;
end;
{ 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
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;
end;
{ 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
if AColIndex < DictionaryFrm.DictGrid.RowCount then
begin
typeCode := Trim(DictionaryFrm.DictGrid.Cells[4, AColIndex]);
Result := (typeCode = 'F') or (typeCode = 'I');
end else
Result := false;
end;
{ 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.