LazStats: Extract and add some general purpose grid-related routines to the new unit GridProcs.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7738 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-10-03 18:03:48 +00:00
parent bf2850f8a1
commit a38794b4f8
5 changed files with 182 additions and 56 deletions

View File

@ -122,7 +122,7 @@
<PackageName Value="LCL"/>
</Item7>
</RequiredPackages>
<Units Count="180">
<Units Count="181">
<Unit0>
<Filename Value="LazStats.lpr"/>
<IsPartOfProject Value="True"/>
@ -1534,6 +1534,11 @@
<ResourceBaseClass Value="Form"/>
<UnitName Value="BasicStatsReportAndChartFormUnit"/>
</Unit179>
<Unit180>
<Filename Value="units\gridprocs.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="GridProcs"/>
</Unit180>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -8,7 +8,7 @@ uses
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, tachartlazaruspkg, tachartprint, lhelpcontrolpkg, Globals, LicenseUnit,
OptionsUnit, MainDM, MainUnit;
OptionsUnit, MainDM, MainUnit, GridProcs;
{$R LazStats.res}

View File

@ -65,7 +65,7 @@ implementation
uses
Math, Grids,
Utils, DictionaryUnit;
Utils, DictionaryUnit, GridProcs;
{ TCrossTabCalculator }
@ -232,13 +232,13 @@ begin
for i := 1 to no_in_list do
begin
j := FVarList[i-1];
if not GoodRecord(FDataGrid, 1, NoSelected, FColNoSelected) then continue;
if not GoodRecord(FDataGrid, 1, FColNoSelected) then continue;
value := StrToFloat(FDataGrid.Cells[j, 1]);
min_value[i-1] := round(value);
max_value[i-1] := round(value);
for k := 2 to NoCases do
begin
if not GoodRecord(FDataGrid, k, NoSelected, FColNoSelected) then continue;
if not GoodRecord(FDataGrid, k, FColNoSelected) then continue;
value := StrToFloat(FDataGrid.Cells[j, k]);
if value < min_value[i-1] then
min_value[i-1] := round(value);
@ -292,11 +292,11 @@ begin
freq[i] := 0;
for i := 1 to NoCases do
begin
if IsFiltered(i) then
if IsFiltered(FDataGrid, i) then
continue;
for j := 1 to no_in_list do
begin
if not GoodRecord(i, NoSelected, FColNoSelected) then continue;
if not GoodRecord(FDataGrid, i, FColNoSelected) then continue;
k := FVarList[j-1];
value := StrToFloat(FDataGrid.Cells[k,i]);
x := round(value);
@ -453,6 +453,9 @@ var
noSelected: Integer;
cellValue: String;
begin
AVarList := nil; // Silence the compiler
AColNoSelected := nil;
SetLength(AVarList, SelList.Count);
SetLength(AColNoSelected, SelList.Count);

View File

@ -7,10 +7,11 @@ interface
uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls, Clipbrd, Grids,
Globals, OptionsUnit, DictionaryUnit;
Globals, OptionsUnit, DictionaryUnit, GridProcs;
Function GoodRecord(Row, NoVars: integer; const GridPos: IntDyneVec): boolean; overload;
Function GoodRecord(AGrid: TStringGrid; Row, NoVars: integer; const GridPos: IntDyneVec): boolean; overload;
function GoodRecord(Row, NoVars: integer; const GridPos: IntDyneVec): boolean;
function IsFiltered(GridRow: integer): boolean; overload;
function ValidValue(row, col: integer): boolean;
procedure FormatCell(Col, Row : integer);
procedure FormatGrid;
@ -29,10 +30,6 @@ procedure CopyRow;
procedure PasteRow;
procedure PrintDict(AReport: TStrings);
procedure PrintData(AReport: TStrings);
function ValidValue(row, col: integer): boolean; overload;
function ValidValue(AGrid: TStringGrid; row, col: integer): boolean; overload;
function IsFiltered(GridRow: integer): boolean; overload;
function IsFiltered(AGrid: TStringGrid; GridRow: integer): boolean; overload;
procedure MatRead(const a: DblDyneMat; out NoRows, NoCols: integer;
const Means, StdDevs: DblDyneVec; out NCases: integer;
@ -66,25 +63,21 @@ implementation
uses
Utils, MainUnit;
function GoodRecord(Row, NoVars: Integer; const GridPos: IntDyneVec): boolean;
begin
Result := GoodRecord(OS3MainFrm.DataGrid, Row, NoVars, GridPos);
end;
function GoodRecord(AGrid: TStringGrid; Row, NoVars: integer;
const GridPos: IntDyneVec): boolean;
// NOTE: Do not call GridProcs.GoodRecord here because this old function may
// use an over-dimensioned GridPos array.
function GoodRecord(Row, NoVars: Integer; const GridPos: IntDyneVec): boolean;
var
i, j: integer;
begin
Result := true;
for i := 0 to NoVars-1 do
for i := 0 to NoVars - 1 do
begin
j := GridPos[i];
if not ValidValue(AGrid, Row, j) then
if not ValidValue(Row, j) then
Result := false;
end;
end;
//-------------------------------------------------------------------
procedure FormatCell(Col, Row: integer);
var
@ -802,33 +795,7 @@ end;
function ValidValue(row, col: Integer): Boolean;
begin
Result := ValidValue(OS3MainFrm.DataGrid, row, col);
end;
function ValidValue(AGrid: TStringGrid; row, col: integer): boolean;
var
valid: boolean;
xvalue: string;
cellstring: string;
begin
valid := true;
if FilterOn then
begin
cellstring := Trim(AGrid.Cells[FilterCol, row]);
if cellstring = 'NO' then valid := false;
Result := valid;
exit;
end;
xvalue := Trim(AGrid.Cells[col,row]);
if (xvalue = '') and (DictionaryFrm.DictGrid.Cells[4, col] <> 'S') then
valid := false;
if valid then // check for user-defined missing value
begin
if Trim(DictionaryFrm.DictGrid.Cells[6, col]) = xvalue then
valid := false;
end;
Result := valid;
Result := GridProcs.ValidValue(OS3MainFrm.DataGrid, row, col);
end;
function IsFiltered(GridRow: Integer): Boolean;
@ -836,12 +803,6 @@ begin
Result := IsFiltered(OS3MainFrm.DataGrid, GridRow);
end;
function IsFiltered(AGrid: TStringGrid; GridRow: integer): boolean;
begin
Result := FilterOn and (Trim(AGrid.Cells[FilterCol,GridRow]) = 'NO');
end;
procedure MatRead(const a: DblDyneMat; out NoRows, NoCols: integer;
const means, stddevs: DblDyneVec; out NCases: integer;
const RowLabels, ColLabels: StrDyneVec; const AFileName: string);

View File

@ -0,0 +1,157 @@
unit GridProcs;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Grids,
Globals, DictionaryUnit;
function CollectValues(AGrid: TStringGrid; AColIndex: Integer;
AColCheck: IntDyneVec): DblDyneVec;
procedure GetMinMax(AGrid: TStringGrid; AColIndex: Integer;
const AColCheck: IntDyneVec; out AMin, AMax: Double);
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 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 CollectValues(AGrid: TStringGrid; AColIndex: Integer; AColCheck: IntDyneVec): DblDyneVec;
var
row, n: Integer;
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;
Result[n] := StrToFloat(trim(AGrid.Cells[AColIndex, row]));
inc(n);
end;
SetLength(Result, n);
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;
{ 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
missingCode := Trim(DictionaryFrm.DictGrid.Cells[6, ACol]);
value := Trim(AGrid.Cells[ACol, ARow]);
Result := (value = missingCode);
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.