2020-03-30 18:01:44 +00:00
|
|
|
// Use file "BubblePlot2.laz" for testing
|
|
|
|
|
|
|
|
unit MultXvsYUnit;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2020-08-23 14:31:05 +00:00
|
|
|
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
|
2020-09-27 21:23:20 +00:00
|
|
|
StdCtrls, ExtCtrls, Buttons, Clipbrd, ComCtrls,
|
2020-10-07 21:25:10 +00:00
|
|
|
MainUnit, Globals, DataProcs, DictionaryUnit,
|
|
|
|
BasicStatsReportAndChartFormUnit, ReportFrameUnit, ChartFrameUnit;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
{ TMultXvsYFrm }
|
|
|
|
|
2020-10-07 21:25:10 +00:00
|
|
|
TMultXvsYFrm = class(TBasicStatsReportAndChartForm)
|
2020-03-30 18:01:44 +00:00
|
|
|
XInBtn: TBitBtn;
|
|
|
|
XOutBtn: TBitBtn;
|
|
|
|
YInBtn: TBitBtn;
|
|
|
|
YOutBtn: TBitBtn;
|
|
|
|
GroupInBtn: TBitBtn;
|
|
|
|
GroupOutBtn: TBitBtn;
|
|
|
|
LinesChk: TCheckBox;
|
|
|
|
XEdit: TEdit;
|
|
|
|
YEdit: TEdit;
|
|
|
|
GroupEdit: TEdit;
|
2020-09-27 21:23:20 +00:00
|
|
|
OptionsGroup: TGroupBox;
|
2020-03-30 18:01:44 +00:00
|
|
|
LabelEdit: TEdit;
|
|
|
|
Label1: TLabel;
|
|
|
|
Label2: TLabel;
|
|
|
|
Label3: TLabel;
|
|
|
|
Label4: TLabel;
|
|
|
|
Label5: TLabel;
|
|
|
|
VarList: TListBox;
|
|
|
|
procedure GroupInBtnClick(Sender: TObject);
|
|
|
|
procedure GroupOutBtnClick(Sender: TObject);
|
2020-09-27 21:23:20 +00:00
|
|
|
procedure VarListDblClick(Sender: TObject);
|
2020-08-23 14:31:05 +00:00
|
|
|
procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean);
|
2020-03-30 18:01:44 +00:00
|
|
|
procedure XInBtnClick(Sender: TObject);
|
|
|
|
procedure XOutBtnClick(Sender: TObject);
|
|
|
|
procedure YInBtnClick(Sender: TObject);
|
|
|
|
procedure YOutBtnClick(Sender: TObject);
|
2020-10-07 21:25:10 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
private
|
2020-09-27 21:23:20 +00:00
|
|
|
procedure PlotXY(const XValues, YValues: DblDyneMat; const Groups: StrDyneVec);
|
2020-10-07 21:25:10 +00:00
|
|
|
|
|
|
|
protected
|
|
|
|
procedure AdjustConstraints; override;
|
|
|
|
procedure Compute; override;
|
|
|
|
procedure UpdateBtnStates; override;
|
|
|
|
procedure WriteToReport(const AMeans, AStdDevs: DblDyneVec);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
public
|
2020-10-07 21:25:10 +00:00
|
|
|
constructor Create(AOwner: TComponent); override;
|
2020-09-27 17:59:39 +00:00
|
|
|
procedure Reset; override;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
MultXvsYFrm: TMultXvsYFrm;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
2020-08-23 14:31:05 +00:00
|
|
|
{$R *.lfm}
|
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
uses
|
2020-08-23 14:31:05 +00:00
|
|
|
TATypes,
|
2020-10-10 11:02:59 +00:00
|
|
|
Math, Utils, MathUnit;
|
2020-08-23 14:31:05 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
{ TMultXvsYFrm }
|
|
|
|
|
2020-10-07 21:25:10 +00:00
|
|
|
constructor TMultXvsYFrm.Create(AOwner: TComponent);
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMultXvsYFrm.AdjustConstraints;
|
|
|
|
begin
|
|
|
|
ParamsPanel.Constraints.MinWidth := Max(
|
|
|
|
4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left,
|
|
|
|
OptionsGroup.Width - XInBtn.Width div 2);
|
|
|
|
ParamsPanel.Constraints.MinHeight := GroupOutBtn.Top + GroupOutBtn.Height +
|
|
|
|
OptionsGroup.BorderSpacing.Top + OptionsGroup.Height + OptionsGroup.BorderSpacing.Bottom +
|
|
|
|
LabelEdit.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMultXvsYFrm.Compute;
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-09-27 21:23:20 +00:00
|
|
|
i, N, xCol, yCol, grpCol, grp, numGrps: integer;
|
|
|
|
grpName: String;
|
|
|
|
//minX, maxX, minY, maxY,
|
|
|
|
X, Y: double;
|
2020-08-23 14:31:05 +00:00
|
|
|
cellstring: string;
|
2020-09-27 21:23:20 +00:00
|
|
|
maxGrpSize: integer = 0;
|
|
|
|
numInGrp: IntDyneVec = nil;
|
|
|
|
xValues: DblDyneMat = nil;
|
|
|
|
yValues: DblDyneMat = nil;
|
|
|
|
grps: StrDyneVec = nil;
|
|
|
|
means: array[0..1] of Double = (0.0, 0.0);
|
|
|
|
stdDevs: array[0..1] of Double = (0.0, 0.0);
|
2020-10-01 16:03:56 +00:00
|
|
|
selected: array of Integer = nil;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-08-23 14:31:05 +00:00
|
|
|
// Get selected variables
|
2020-09-27 21:23:20 +00:00
|
|
|
xCol := 0;
|
|
|
|
yCol := 0;
|
|
|
|
grpCol := 0;
|
2020-03-30 18:01:44 +00:00
|
|
|
for i := 1 to NoVariables do
|
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
cellstring := OS3MainFrm.DataGrid.Cells[i, 0];
|
|
|
|
if (cellstring = XEdit.Text) then xCol := i;
|
|
|
|
if (cellstring = YEdit.Text) then yCol := i;
|
|
|
|
if (cellstring = GroupEdit.Text) then grpCol := i;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
2020-09-27 21:23:20 +00:00
|
|
|
if (xCol = 0) or (yCol = 0) or (grpCol = 0) then
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-08-23 14:31:05 +00:00
|
|
|
ErrorMsg('No variable selected.');
|
2020-03-30 18:01:44 +00:00
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
2020-10-01 16:03:56 +00:00
|
|
|
SetLength(selected, 3);
|
2020-09-27 21:23:20 +00:00
|
|
|
selected[0] := xCol;
|
|
|
|
selected[1] := yCol;
|
|
|
|
selected[2] := grpCol;
|
|
|
|
|
|
|
|
// Get groups
|
|
|
|
// minGrp := MaxInt;
|
|
|
|
// maxGrp := -MaxInt;
|
|
|
|
SetLength(grps, NoCases);
|
|
|
|
numGrps := 0;
|
2020-03-30 18:01:44 +00:00
|
|
|
for i := 1 to NoCases do
|
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
grpName := Trim(OS3MainFrm.DataGrid.Cells[grpCol, i]);
|
|
|
|
if IndexOfString(grps, grpName) = -1 then
|
|
|
|
begin
|
|
|
|
grps[numGrps] := grpName;
|
|
|
|
inc(numGrps);
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
SetLength(XValues, numGrps, NoCases); // NoCases is over-dimensioned and will be trimmed later.
|
|
|
|
SetLength(YValues, numGrps, NoCases); // dto.
|
|
|
|
SetLength(numInGrp, numGrps);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-08-23 14:31:05 +00:00
|
|
|
N := 0;
|
|
|
|
for i := 1 to NoCases do
|
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
if not GoodRecord(i, Length(selected), selected) then continue;
|
2020-08-23 14:31:05 +00:00
|
|
|
inc(N);
|
|
|
|
|
|
|
|
X := StrToFloat(OS3MainFrm.DataGrid.Cells[XCol, i]);
|
|
|
|
Y := StrToFloat(OS3MainFrm.DataGrid.Cells[YCol, i]);
|
2020-09-27 21:23:20 +00:00
|
|
|
grpName := Trim(OS3MainFrm.DataGrid.Cells[grpCol, i]);
|
|
|
|
grp := IndexOfString(grps, grpName);
|
|
|
|
|
|
|
|
xValues[grp, numInGrp[grp]] := X;
|
|
|
|
yValues[grp, numInGrp[grp]] := Y;
|
|
|
|
inc(numInGrp[grp]);
|
|
|
|
|
|
|
|
Means[0] := Means[0] + X;
|
|
|
|
StdDevs[0] := StdDevs[0] + sqr(X);
|
|
|
|
Means[1] := Means[1] + Y;
|
|
|
|
StdDevs[1] := StdDevs[1] + sqr(Y);
|
2020-08-23 14:31:05 +00:00
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-08-23 14:31:05 +00:00
|
|
|
// Trim XValues and YValues to correct dimension.
|
2020-09-27 21:23:20 +00:00
|
|
|
SetLength(xValues, numGrps);
|
|
|
|
SetLength(yValues, numGrps);
|
|
|
|
for grp := 0 to numGrps-1 do
|
2020-08-23 14:31:05 +00:00
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
SetLength(xValues[grp], numInGrp[grp]);
|
|
|
|
SetLength(yValues[grp], numInGrp[grp]);
|
2020-08-23 14:31:05 +00:00
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
// Get descriptive data
|
|
|
|
for i := 0 to 1 do
|
2020-08-23 14:31:05 +00:00
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
stdDevs[i] := stdDevs[i] - sqr(means[i]) / N;
|
2020-10-07 21:25:10 +00:00
|
|
|
stdDevs[i] := sqrt(stdDevs[i] / (N - 1));
|
2020-09-27 21:23:20 +00:00
|
|
|
means[i] := means[i] / N;
|
|
|
|
end;
|
2020-08-23 14:31:05 +00:00
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
// Print out descriptive data to report frame
|
2020-10-07 21:25:10 +00:00
|
|
|
WriteToReport(means, stdDevs);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-08-23 14:31:05 +00:00
|
|
|
// sort on X
|
2020-09-27 21:23:20 +00:00
|
|
|
for i := 0 to numGrps - 1 do
|
2020-08-23 14:31:05 +00:00
|
|
|
SortOnX(XValues[i], YValues[i]);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-08-23 14:31:05 +00:00
|
|
|
// Plot data
|
2020-09-27 21:23:20 +00:00
|
|
|
PlotXY(XValues, YValues, grps);
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-08-23 14:31:05 +00:00
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
procedure TMultXvsYFrm.GroupInBtnClick(Sender: TObject);
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
i := VarList.ItemIndex;
|
2020-09-27 21:23:20 +00:00
|
|
|
if (i > -1) and (GroupEdit.Text = '') then
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
GroupEdit.Text := VarList.Items[i];
|
2020-03-30 18:01:44 +00:00
|
|
|
VarList.Items.Delete(i);
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
2020-08-23 14:31:05 +00:00
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
procedure TMultXvsYFrm.GroupOutBtnClick(Sender: TObject);
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
if GroupEdit.Text <> '' then
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
VarList.Items.Add(GroupEdit.Text);
|
|
|
|
GroupEdit.Text := '';
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
2020-08-23 14:31:05 +00:00
|
|
|
|
|
|
|
// Routine to plot X versus multiple Y values for several groups
|
2020-09-27 21:23:20 +00:00
|
|
|
// Layout of X and Y matrices:
|
|
|
|
// 1st index: group index, 2nd index: point index within group
|
|
|
|
procedure TMultXvsYFrm.PlotXY(const XValues, YValues: DblDyneMat;
|
|
|
|
const Groups: StrDyneVec);
|
2020-08-23 14:31:05 +00:00
|
|
|
var
|
|
|
|
pt: TPlotType;
|
|
|
|
grp: Integer;
|
|
|
|
clr: TColor;
|
|
|
|
grpName: String;
|
|
|
|
sym: TSeriesPointerStyle;
|
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
FChartFrame.Clear;
|
2020-08-23 14:31:05 +00:00
|
|
|
|
|
|
|
// Titles
|
2020-09-27 21:23:20 +00:00
|
|
|
FChartFrame.SetTitle(LabelEdit.Text);
|
|
|
|
FChartFrame.SetXTitle(XEdit.Text);
|
|
|
|
FChartFrame.SetYTitle(YEdit.Text);
|
2020-08-23 14:31:05 +00:00
|
|
|
|
|
|
|
if LinesChk.Checked then pt := ptLinesAndSymbols else pt := ptSymbols;
|
|
|
|
|
|
|
|
for grp := 0 to Length(XValues)-1 do
|
|
|
|
begin
|
|
|
|
clr := DATA_COLORS[grp mod Length(DATA_COLORS)];
|
|
|
|
sym := DATA_SYMBOLS[grp mod Length(DATA_SYMBOLS)];
|
2020-09-27 21:23:20 +00:00
|
|
|
grpName := Format('%s %s', [GroupEdit.Text, Groups[grp]]);
|
|
|
|
FChartFrame.PlotXY(pt, XValues[grp], YValues[grp], nil, nil, grpName, clr, sym);
|
2020-08-23 14:31:05 +00:00
|
|
|
end;
|
|
|
|
end;
|
2020-09-27 21:23:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
procedure TMultXvsYFrm.Reset;
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-10-07 21:25:10 +00:00
|
|
|
i: integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-10-07 21:25:10 +00:00
|
|
|
inherited;
|
2020-10-10 15:05:32 +00:00
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
VarList.Clear;
|
|
|
|
for i := 1 to NoVariables do
|
|
|
|
VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]);
|
|
|
|
XEdit.Text := '';
|
|
|
|
YEdit.Text := '';
|
|
|
|
GroupEdit.Text := '';
|
|
|
|
LinesChk.Checked := false;
|
2020-10-07 21:25:10 +00:00
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
procedure TMultXvsYFrm.VarListDblClick(Sender: TObject);
|
|
|
|
var
|
|
|
|
index: Integer;
|
|
|
|
begin
|
|
|
|
index := VarList.ItemIndex;
|
|
|
|
if index > -1 then
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-09-27 21:23:20 +00:00
|
|
|
if XEdit.Text = '' then
|
|
|
|
XEdit.Text := VarList.Items[index]
|
|
|
|
else if YEdit.Text = '' then
|
|
|
|
YEdit.Text := VarList.Items[index]
|
|
|
|
else if GroupEdit.Text = '' then
|
|
|
|
GroupEdit.Text := VarList.Items[index];
|
|
|
|
VarList.Items.Delete(index);
|
|
|
|
UpdateBtnStates;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
end;
|
2020-08-23 14:31:05 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
procedure TMultXvsYFrm.UpdateBtnStates;
|
|
|
|
var
|
|
|
|
lSelected: Boolean;
|
|
|
|
i: Integer;
|
|
|
|
begin
|
2020-10-10 15:05:32 +00:00
|
|
|
inherited;
|
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
lSelected := false;
|
|
|
|
for i:=0 to VarList.Items.Count-1 do
|
|
|
|
if VarList.Selected[i] then
|
|
|
|
begin
|
|
|
|
lSelected := true;
|
|
|
|
break;
|
|
|
|
end;
|
|
|
|
|
|
|
|
XInBtn.Enabled := lSelected and (XEdit.Text = '');
|
|
|
|
YInBtn.Enabled := lSelected and (YEdit.Text = '');
|
|
|
|
GroupInBtn.Enabled := lSelected and (GroupEdit.Text = '');
|
|
|
|
XOutBtn.Enabled := (XEdit.Text <> '');
|
|
|
|
YOutBtn.Enabled := (YEdit.Text <> '');
|
|
|
|
GroupOutBtn.Enabled := (GroupEdit.Text <> '');
|
2020-09-27 21:23:20 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMultXvsYFrm.VarListSelectionChange(Sender: TObject; User: boolean);
|
|
|
|
begin
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-10-07 21:25:10 +00:00
|
|
|
procedure TMultXVsYFrm.WriteToReport(const AMeans, AStdDevs: DblDyneVec);
|
|
|
|
var
|
|
|
|
lReport: TStrings;
|
|
|
|
begin
|
|
|
|
lReport := TStringList.Create;
|
|
|
|
try
|
|
|
|
lReport.Add('X VERSUS Y FOR GROUPS PLOT');
|
|
|
|
lReport.Add('');
|
|
|
|
lReport.Add('X variable: ' + XEdit.Text);
|
|
|
|
lReport.Add('Y variable: ' + YEdit.Text);
|
|
|
|
lReport.Add('Group variable: ' + GroupEdit.Text);
|
|
|
|
lReport.Add('');
|
|
|
|
|
|
|
|
lReport.Add('VARIABLE MEAN STANDARD DEVIATION');
|
|
|
|
lReport.Add('-------- -------- ------------------');
|
|
|
|
lReport.Add(' X %8.3f %14.3f', [AMeans[0], AStdDevs[0]]);
|
|
|
|
lReport.Add(' Y %8.3f %14.3f', [AMeans[1], AStdDevs[1]]);
|
|
|
|
lReport.Add('');
|
|
|
|
|
|
|
|
FReportFrame.DisplayReport(lReport);
|
|
|
|
finally
|
|
|
|
lReport.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-09-27 21:23:20 +00:00
|
|
|
procedure TMultXvsYFrm.XInBtnClick(Sender: TObject);
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
i := VarList.ItemIndex;
|
|
|
|
if (i > -1) and (XEdit.Text = '') then
|
|
|
|
begin
|
|
|
|
XEdit.Text := VarList.Items[i];
|
|
|
|
VarList.Items.Delete(i);
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMultXvsYFrm.XOutBtnClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if XEdit.Text <> '' then
|
|
|
|
begin
|
|
|
|
VarList.Items.Add(XEdit.Text);
|
|
|
|
XEdit.Text := '';
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMultXvsYFrm.YInBtnClick(Sender: TObject);
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
i := VarList.ItemIndex;
|
|
|
|
if (i > -1) and (YEdit.Text = '') then
|
|
|
|
begin
|
|
|
|
YEdit.Text := VarList.Items[i];
|
|
|
|
VarList.Items.Delete(i);
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMultXvsYFrm.YOutBtnClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if YEdit.Text <> '' then
|
|
|
|
begin
|
|
|
|
VarList.Items.Add(YEdit.Text);
|
|
|
|
YEdit.Text := '';
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
end.
|
|
|
|
|