You've already forked lazarus-ccr
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7738 8e941d3f-bd1b-0410-a28a-d453659cc2b4
565 lines
12 KiB
ObjectPascal
565 lines
12 KiB
ObjectPascal
// Use file "twoway.laz" for testing
|
|
|
|
unit CrossTabUnit;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
|
|
StdCtrls, Buttons, ExtCtrls, ComCtrls,
|
|
Globals, MainUnit, DataProcs, MatrixLib, ContextHelpUnit,
|
|
BasicStatsFormUnit, ReportFrameUnit;
|
|
|
|
type
|
|
|
|
{ TCrossTabFrm }
|
|
|
|
TCrossTabFrm = class(TBasicStatsForm)
|
|
ComputeBtn: TButton;
|
|
PageControl: TPageControl;
|
|
ParamsPanel: TPanel;
|
|
ParamsSplitter: TSplitter;
|
|
ReportPage: TTabSheet;
|
|
VertCenterBevel: TBevel;
|
|
Bevel2: TBevel;
|
|
HelpBtn: TButton;
|
|
InBtn: TBitBtn;
|
|
OutBtn: TBitBtn;
|
|
ResetBtn: TButton;
|
|
CloseBtn: TButton;
|
|
Label1: TLabel;
|
|
Label2: TLabel;
|
|
VarList: TListBox;
|
|
SelList: TListBox;
|
|
procedure CloseBtnClick(Sender: TObject);
|
|
procedure ComputeBtnClick(Sender: TObject);
|
|
procedure FormActivate(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure HelpBtnClick(Sender: TObject);
|
|
procedure InBtnClick(Sender: TObject);
|
|
procedure OutBtnClick(Sender: TObject);
|
|
procedure ResetBtnClick(Sender: TObject);
|
|
procedure SelListDblClick(Sender: TObject);
|
|
procedure VarListDblClick(Sender: TObject);
|
|
procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean);
|
|
|
|
private
|
|
FReportFrame: TReportFrame;
|
|
FAutosized: Boolean;
|
|
procedure Prepare(out AVarList, AColNoSelected: IntDyneVec);
|
|
procedure UpdateBtnStates;
|
|
|
|
public
|
|
procedure Reset; override;
|
|
end;
|
|
|
|
var
|
|
CrossTabFrm: TCrossTabFrm;
|
|
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
uses
|
|
Math, Grids,
|
|
Utils, DictionaryUnit, GridProcs;
|
|
|
|
|
|
{ TCrossTabCalculator }
|
|
|
|
type
|
|
TCrossTabCalculator = class
|
|
private
|
|
FReport: TStrings;
|
|
FVarList: IntDyneVec;
|
|
FDataGrid: TStringGrid;
|
|
FColNoSelected: IntDyneVec;
|
|
|
|
grandsum, sum, index: integer;
|
|
no_in_list, length_array: integer;
|
|
min_value, levels, displace, subscript: IntDyneVec;
|
|
freq: IntDyneVec;
|
|
outgrid: DblDyneMat;
|
|
NoSelected: integer;
|
|
|
|
protected
|
|
procedure BreakDown;
|
|
procedure GetLevels;
|
|
function IndexPosition(x: IntDyneVec): integer;
|
|
procedure Tabulate;
|
|
|
|
public
|
|
procedure Execute(AReport: TStrings; ADataGrid: TStringGrid;
|
|
AVarList, AColNoSelected: IntDyneVec);
|
|
|
|
end;
|
|
|
|
|
|
procedure TCrossTabCalculator.BreakDown;
|
|
var
|
|
i, j, row, col, bigmax: integer;
|
|
ptr1, ptr2: Integer;
|
|
outline: string;
|
|
value: string;
|
|
title: String;
|
|
rowLabels: StrDyneVec = nil;
|
|
colLabels: StrDyneVec = nil;
|
|
begin
|
|
bigmax := -1;
|
|
for i := 0 to no_in_list-1 do
|
|
if Levels[i] > bigmax then bigmax := Levels[i];
|
|
|
|
SetLength(colLabels, bigmax);
|
|
SetLength(outgrid, length_array, bigmax);
|
|
SetLength(rowlabels, length_array);
|
|
|
|
outline := FDataGrid.Cells[FVarList[no_in_list-1], 0];
|
|
for col := 1 to Levels[no_in_list-1] do
|
|
collabels[col-1] := outline + Format(':%3d', [min_value[no_in_list-1] + col - 1]);
|
|
|
|
for row := 1 to length_array do
|
|
rowlabels[row-1] := '';
|
|
|
|
ptr1 := no_in_list - 1;
|
|
ptr2 := no_in_list;
|
|
for i := 1 to no_in_list do
|
|
subscript[i-1] := 1;
|
|
|
|
FReport.Add('');
|
|
FReport.Add('FREQUENCIES BY LEVEL:');
|
|
FReport.Add('');
|
|
|
|
sum := 0;
|
|
col := 1;
|
|
row := 1;
|
|
|
|
while true do
|
|
begin
|
|
index := IndexPosition(subscript);
|
|
outline := 'For cell levels: ';
|
|
for i := 1 to no_in_list do
|
|
begin
|
|
j := FVarList[i-1];
|
|
value := Format('%s:%3d ',[FDataGrid.Cells[j,0], min_value[i-1] + subscript[i-1] - 1]);
|
|
outline := outline + value;
|
|
end;
|
|
sum := sum + freq[index];
|
|
outgrid[row-1,col-1] := freq[index];
|
|
outline := outline + Format(' Frequency: %3d', [freq[index]]);
|
|
FReport.Add(outline);
|
|
|
|
subscript[ptr2-1] := subscript[ptr2-1] + 1;
|
|
col := col + 1;
|
|
if subscript[ptr2-1] <= levels[ptr2-1] then
|
|
continue;
|
|
|
|
FReport.Add('Sum across levels: %3d', [sum]);
|
|
FReport.Add('');
|
|
|
|
grandsum := grandsum + sum;
|
|
sum := 0;
|
|
row := row + 1;
|
|
|
|
if ptr1 < 1 then
|
|
break;
|
|
|
|
subscript[ptr1-1] := subscript[ptr1-1] + 1;
|
|
if subscript[ptr1-1] > levels[ptr1-1] then
|
|
begin
|
|
repeat
|
|
ptr1 := ptr1 - 1;
|
|
if ptr1 < 1 then
|
|
break;
|
|
until subscript[ptr1-1] < levels[ptr1-1];
|
|
if ptr1 < 1 then
|
|
break;
|
|
subscript[ptr1-1] := subscript[ptr1-1] + 1;
|
|
end;
|
|
|
|
for i := ptr1 + 1 to no_in_list do
|
|
subscript[i-1] := 1;
|
|
ptr1 := no_in_list - 1;
|
|
col := 1;
|
|
|
|
end;
|
|
|
|
title := 'CELL FREQUENCIES BY LEVELS';
|
|
for i := 1 to row - 1 do
|
|
begin
|
|
value := Format('Block %d',[i]);
|
|
rowlabels[i-1] := value;
|
|
end;
|
|
|
|
FReport.Add('');
|
|
MatPrint(outgrid, row-1, Levels[no_in_list-1], title, rowlabels, collabels, NoCases, FReport);
|
|
end;
|
|
|
|
|
|
procedure TCrossTabCalculator.Execute(AReport: TStrings; ADataGrid: TStringGrid;
|
|
AVarList, AColNoSelected: IntDyneVec);
|
|
begin
|
|
FReport := AReport;
|
|
FDataGrid := ADataGrid;
|
|
FVarList := AVarList;
|
|
|
|
no_in_list := Length(FVarList);
|
|
SetLength(min_value, no_in_list);
|
|
SetLength(levels, no_in_list);
|
|
SetLength(displace, no_in_list);
|
|
SetLength(subscript, no_in_list);
|
|
|
|
FColNoSelected := AColNoSelected;
|
|
NoSelected := Length(FColNoSelected);
|
|
|
|
GetLevels;
|
|
Tabulate;
|
|
BreakDown;
|
|
|
|
FReport.Add('');
|
|
FReport.Add('Grand sum across all categories: %d', [grandsum]);
|
|
end;
|
|
|
|
procedure TCrossTabCalculator.GetLevels;
|
|
var
|
|
i, j, k: integer;
|
|
value: double;
|
|
max_value: IntDyneVec = nil;
|
|
begin
|
|
SetLength(max_value, no_in_list);
|
|
for i := 1 to no_in_list do
|
|
begin
|
|
j := FVarList[i-1];
|
|
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, FColNoSelected) then continue;
|
|
value := StrToFloat(FDataGrid.Cells[j, k]);
|
|
if value < min_value[i-1] then
|
|
min_value[i-1] := round(value);
|
|
if value > max_value[i-1] then
|
|
max_value[i-1] := round(value);
|
|
end;
|
|
end;
|
|
|
|
for i := 1 to no_in_list do
|
|
begin
|
|
j := FVarList[i-1];
|
|
levels[i-1] := max_value[i-1] - min_value[i-1] + 1;
|
|
FReport.Add('%-10s min %3d, max %3d, number of levels %3d', [
|
|
FDataGrid.Cells[j,0]+':', min_value[i-1], max_value[i-1], levels[i-1]
|
|
]);
|
|
end;
|
|
FReport.Add('');
|
|
|
|
displace[no_in_list-1] := 1;
|
|
if no_in_list > 1 then
|
|
for i := (no_in_list - 1) downto 1 do
|
|
displace[i-1] := levels[i] * displace[i];
|
|
end;
|
|
|
|
|
|
function TCrossTabCalculator.IndexPosition(x: IntDyneVec): integer;
|
|
var
|
|
i: integer;
|
|
begin
|
|
Result := x[no_in_list-1];
|
|
if no_in_list > 1 then
|
|
begin
|
|
for i := 1 to no_in_list - 1 do
|
|
Result := Result + (x[i-1] -1) * displace[i-1];
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabCalculator.Tabulate;
|
|
var
|
|
i, j, k: integer;
|
|
value: double;
|
|
x: integer;
|
|
begin
|
|
length_array := 1;
|
|
for i := 1 to no_in_list do
|
|
length_array := length_array * levels[i-1];
|
|
SetLength(freq, length_array+1);
|
|
|
|
for i := 0 to length_array do
|
|
freq[i] := 0;
|
|
for i := 1 to NoCases do
|
|
begin
|
|
if IsFiltered(FDataGrid, i) then
|
|
continue;
|
|
for j := 1 to no_in_list do
|
|
begin
|
|
if not GoodRecord(FDataGrid, i, FColNoSelected) then continue;
|
|
k := FVarList[j-1];
|
|
value := StrToFloat(FDataGrid.Cells[k,i]);
|
|
x := round(value);
|
|
x := x - min_value[j-1] + 1;
|
|
subscript[j-1] := x;
|
|
end;
|
|
j := IndexPosition(subscript);
|
|
|
|
if (j < 1) or (j > length_array) then
|
|
continue
|
|
else
|
|
freq[j] := freq[j] + 1;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TCrossTabFrm }
|
|
|
|
procedure TCrossTabFrm.CloseBtnClick(Sender: TObject);
|
|
begin
|
|
Close;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.ComputeBtnClick(Sender: TObject);
|
|
var
|
|
lReport: TStringList;
|
|
calculator: TCrossTabCalculator;
|
|
var_list: IntDyneVec = nil;
|
|
colNoSelected: IntDyneVec = nil;
|
|
begin
|
|
if SelList.Items.Count = 0 then
|
|
begin
|
|
ErrorMsg('No variables selected for analysis.');
|
|
exit;
|
|
end;
|
|
|
|
lReport := TStringList.Create;
|
|
try
|
|
lReport.Add('CROSSTAB RESULTS');
|
|
lReport.Add('');
|
|
lReport.Add('Analyzed data is from file ' + OS3MainFrm.FileNameEdit.Text);
|
|
lReport.Add('');
|
|
|
|
calculator := TCrossTabCalculator.Create;
|
|
try
|
|
Prepare(var_list, colNoSelected);
|
|
calculator.Execute(lReport, OS3MainFrm.DataGrid, var_list, colNoSelected);
|
|
FReportFrame.DisplayReport(lReport);
|
|
finally
|
|
calculator.Free;
|
|
end;
|
|
finally
|
|
lReport.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.FormActivate(Sender: TObject);
|
|
var
|
|
w: Integer;
|
|
begin
|
|
if FAutoSized then
|
|
exit;
|
|
|
|
w := MaxValue([HelpBtn.Width, ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]);
|
|
HelpBtn.Constraints.MinWidth := w;
|
|
ResetBtn.Constraints.MinWidth := w;
|
|
ComputeBtn.Constraints.MinWidth := w;
|
|
CloseBtn.Constraints.MinWidth := w;
|
|
|
|
ParamsPanel.Constraints.MinWidth := Max(
|
|
4*w + 3*CloseBtn.BorderSpacing.Left,
|
|
Max(Label1.Width, Label2.Width) + InBtn.Width + VarList.BorderSpacing.Right * 2
|
|
);
|
|
ParamsPanel.Constraints.MinHeight := 200;
|
|
|
|
Constraints.MinWidth := ParamsPanel.Constraints.MinWidth + 300;;
|
|
Constraints.MinHeight := ParamsPanel.Constraints.MinHeight + 2*ParamsPanel.BorderSpacing.Top;
|
|
if Width < Constraints.MinWidth then Width := 1; // enforce constraints
|
|
if Height < Constraints.MinHeight then Height := 1;
|
|
|
|
Position := poDesigned;
|
|
FAutoSized := true;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.FormCreate(Sender: TObject);
|
|
begin
|
|
Assert(OS3MainFrm <> nil);
|
|
|
|
InitForm(self);
|
|
|
|
FReportFrame := TReportFrame.Create(self);
|
|
with FReportFrame do
|
|
begin
|
|
Parent := ReportPage;
|
|
Align := alClient;
|
|
end;
|
|
|
|
Reset;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.HelpBtnClick(Sender: TObject);
|
|
begin
|
|
if ContextHelpForm = nil then
|
|
Application.CreateForm(TContextHelpForm, ContextHelpForm);
|
|
ContextHelpForm.HelpMessage((Sender as TButton).Tag);
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.InBtnClick(Sender: TObject);
|
|
var
|
|
i: integer;
|
|
begin
|
|
i := 0;
|
|
while i < VarList.Items.Count do
|
|
begin
|
|
if VarList.Selected[i] then
|
|
begin
|
|
SelList.Items.Add(VarList.Items[i]);
|
|
VarList.Items.Delete(i);
|
|
i := 0;
|
|
end else
|
|
inc(i);
|
|
end;
|
|
UpdateBtnStates;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.OutBtnClick(Sender: TObject);
|
|
var
|
|
i: integer;
|
|
begin
|
|
i := 0;
|
|
while i < SelList.Items.Count do
|
|
begin
|
|
if SelList.Selected[i] then
|
|
begin
|
|
VarList.Items.Add(SelList.Items[i]);
|
|
SelList.Items.Delete(i);
|
|
i := 0;
|
|
end else
|
|
inc(i);
|
|
end;
|
|
UpdateBtnStates;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.Prepare(out AVarlist, AColNoSelected: IntDyneVec);
|
|
var
|
|
i, j: Integer;
|
|
noSelected: Integer;
|
|
cellValue: String;
|
|
begin
|
|
AVarList := nil; // Silence the compiler
|
|
AColNoSelected := nil;
|
|
|
|
SetLength(AVarList, SelList.Count);
|
|
SetLength(AColNoSelected, SelList.Count);
|
|
|
|
noSelected := 0;
|
|
for i := 0 to SelList.Items.Count-1 do
|
|
begin
|
|
for j := 1 to NoVariables do
|
|
begin
|
|
cellvalue := OS3MainFrm.DataGrid.Cells[j, 0];
|
|
if cellvalue = SelList.Items[i] then
|
|
begin
|
|
AVarList[i] := j;
|
|
AColNoSelected[i] := j;
|
|
inc(noSelected);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
SetLength(AVarList, noSelected);
|
|
Setlength(AColNoSelected, noSelected);
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.Reset;
|
|
var
|
|
i: integer;
|
|
begin
|
|
VarList.Clear;
|
|
SelList.Clear;
|
|
for i := 1 to NoVariables do
|
|
VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]);
|
|
UpdateBtnStates;
|
|
FReportFrame.Clear;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.ResetBtnClick(Sender: TObject);
|
|
begin
|
|
Reset;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.SelListDblClick(Sender: TObject);
|
|
var
|
|
index: Integer;
|
|
begin
|
|
index := SelList.ItemIndex;
|
|
if index > -1 then
|
|
begin
|
|
VarList.Items.Add(Sellist.Items[index]);
|
|
SelList.Items.Delete(index);
|
|
UpdateBtnStates;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.UpdateBtnStates;
|
|
var
|
|
lSelected: Boolean;
|
|
i: Integer;
|
|
begin
|
|
lSelected := false;
|
|
for i := 0 to VarList.Items.Count-1 do
|
|
if VarList.Selected[i] then
|
|
begin
|
|
lSelected := true;
|
|
break;
|
|
end;
|
|
InBtn.Enabled := lSelected;
|
|
|
|
lSelected := false;
|
|
for i := 0 to SelList.Items.Count-1 do
|
|
if SelList.Selected[i] then
|
|
begin
|
|
lSelected := true;
|
|
break;
|
|
end;
|
|
OutBtn.Enabled := lSelected;
|
|
|
|
FReportFrame.UpdateBtnStates;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.VarListDblClick(Sender: TObject);
|
|
var
|
|
index: Integer;
|
|
begin
|
|
index := VarList.ItemIndex;
|
|
if index > -1 then
|
|
begin
|
|
SelList.Items.Add(VarList.Items[index]);
|
|
Varlist.Items.Delete(index);
|
|
UpdateBtnStates;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TCrossTabFrm.VarListSelectionChange(Sender: TObject; User: boolean);
|
|
begin
|
|
UpdateBtnStates;
|
|
end;
|
|
|
|
|
|
end.
|
|
|