LazStats: Simplification of Compute() method in PlotXYUnit

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7741 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-10-04 16:50:11 +00:00
parent 7edc03ffc8
commit d36f794dee
3 changed files with 185 additions and 212 deletions

View File

@ -48,7 +48,7 @@ inherited PlotXYFrm: TPlotXYFrm
AnchorSideLeft.Control = ParamsPanel AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = Label1 AnchorSideTop.Control = Label1
AnchorSideTop.Side = asrBottom AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = XinBtn AnchorSideRight.Control = XInBtn
AnchorSideBottom.Control = ButtonBevel AnchorSideBottom.Control = ButtonBevel
AnchorSideBottom.Side = asrBottom AnchorSideBottom.Side = asrBottom
Left = 0 Left = 0
@ -88,7 +88,7 @@ inherited PlotXYFrm: TPlotXYFrm
Caption = 'Y Axis Variable' Caption = 'Y Axis Variable'
ParentColor = False ParentColor = False
end end
object XinBtn: TBitBtn[9] object XInBtn: TBitBtn[9]
AnchorSideLeft.Control = ParamsPanel AnchorSideLeft.Control = ParamsPanel
AnchorSideLeft.Side = asrCenter AnchorSideLeft.Side = asrCenter
AnchorSideTop.Control = VarList AnchorSideTop.Control = VarList
@ -100,14 +100,14 @@ inherited PlotXYFrm: TPlotXYFrm
BorderSpacing.Right = 8 BorderSpacing.Right = 8
Images = MainDataModule.ImageList Images = MainDataModule.ImageList
ImageIndex = 1 ImageIndex = 1
OnClick = XinBtnClick OnClick = XInBtnClick
Spacing = 0 Spacing = 0
TabOrder = 4 TabOrder = 4
end end
object XOutBtn: TBitBtn[10] object XOutBtn: TBitBtn[10]
AnchorSideLeft.Control = ParamsPanel AnchorSideLeft.Control = ParamsPanel
AnchorSideLeft.Side = asrCenter AnchorSideLeft.Side = asrCenter
AnchorSideTop.Control = XinBtn AnchorSideTop.Control = XInBtn
AnchorSideTop.Side = asrBottom AnchorSideTop.Side = asrBottom
Left = 175 Left = 175
Height = 26 Height = 26
@ -157,7 +157,7 @@ inherited PlotXYFrm: TPlotXYFrm
TabOrder = 7 TabOrder = 7
end end
object XEdit: TEdit[13] object XEdit: TEdit[13]
AnchorSideLeft.Control = XinBtn AnchorSideLeft.Control = XInBtn
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideRight.Control = ParamsPanel AnchorSideRight.Control = ParamsPanel
AnchorSideRight.Side = asrBottom AnchorSideRight.Side = asrBottom
@ -193,7 +193,7 @@ inherited PlotXYFrm: TPlotXYFrm
Text = 'YEdit' Text = 'YEdit'
end end
object OptionsGroup: TGroupBox[15] object OptionsGroup: TGroupBox[15]
AnchorSideLeft.Control = XinBtn AnchorSideLeft.Control = XInBtn
AnchorSideTop.Control = YEdit AnchorSideTop.Control = YEdit
AnchorSideTop.Side = asrBottom AnchorSideTop.Side = asrBottom
AnchorSideRight.Side = asrBottom AnchorSideRight.Side = asrBottom

View File

@ -9,7 +9,7 @@ interface
uses uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls, Buttons, ComCtrls, StdCtrls, ExtCtrls, Buttons, ComCtrls,
MainUnit, Globals, FunctionsLib, DataProcs, BasicStatsReportAndChartFormUnit, MainUnit, Globals, FunctionsLib, BasicStatsReportAndChartFormUnit,
ReportFrameUnit, ChartFrameUnit; ReportFrameUnit, ChartFrameUnit;
type type
@ -27,7 +27,7 @@ type
Label3: TLabel; Label3: TLabel;
XEdit: TEdit; XEdit: TEdit;
Label2: TLabel; Label2: TLabel;
XinBtn: TBitBtn; XInBtn: TBitBtn;
XOutBtn: TBitBtn; XOutBtn: TBitBtn;
YInBtn: TBitBtn; YInBtn: TBitBtn;
YOutBtn: TBitBtn; YOutBtn: TBitBtn;
@ -35,7 +35,7 @@ type
VarList: TListBox; VarList: TListBox;
procedure VarListDblClick(Sender: TObject); procedure VarListDblClick(Sender: TObject);
procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean);
procedure XinBtnClick(Sender: TObject); procedure XInBtnClick(Sender: TObject);
procedure XOutBtnClick(Sender: TObject); procedure XOutBtnClick(Sender: TObject);
procedure YInBtnClick(Sender: TObject); procedure YInBtnClick(Sender: TObject);
procedure YOutBtnClick(Sender: TObject); procedure YOutBtnClick(Sender: TObject);
@ -64,7 +64,7 @@ implementation
uses uses
TAChartUtils, TAChartAxisUtils, TALegend, TASources, TACustomSeries, TASeries, TAChartUtils, TAChartAxisUtils, TALegend, TASources, TACustomSeries, TASeries,
Math, Utils; MathUnit, GridProcs, Utils;
{ TPlotXYFrm } { TPlotXYFrm }
@ -90,127 +90,40 @@ begin
Marks.Style := smsLabel; Marks.Style := smsLabel;
Grid.Visible := false; Grid.Visible := false;
end; end;
PageControl.ActivePage := ChartPage;
end; end;
procedure TPlotXYfrm.Reset; procedure TPlotXYFrm.AdjustConstraints;
var
i: integer;
begin begin
XEdit.Text := ''; ParamsPanel.Constraints.MinHeight := OptionsGroup.Top + OptionsGroup.Height +
YEdit.Text := ''; OptionsGroup.BorderSpacing.Bottom + ButtonBevel.Height +
ConfEdit.Text := FormatFloat('0.0', DEFAULT_CONFIDENCE_LEVEL_PERCENT); CloseBtn.Height + CloseBtn.BorderSpacing.Top;
LineChk.Checked := false; ParamsPanel.Constraints.MinWidth := OptionsGroup.Width * 2 - XInBtn.Width div 2 - XInBtn.BorderSpacing.Left;
MeansChk.Checked := false;
ConfChk.Checked := false;
VarList.Items.Clear;
for i := 1 to NoVariables do
VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]);
if Assigned(FChartFrame) then Constraints.MinHeight := ParamsPanel.Constraints.MinHeight + ParamsPanel.BorderSpacing.Around*2;
FChartFrame.Clear; Constraints.MinWidth := ParamsPanel.Constraints.MinWidth + 200;
if Assigned(FReportFrame) then
FReportFrame.Clear;
UpdateBtnStates;
end; end;
procedure TPlotXYFrm.VarListDblClick(Sender: TObject);
var
index: integer;
begin
index := VarList.ItemIndex;
if index > -1 then
begin
if XEdit.Text = '' then
XEdit.Text := VarList.Items[index]
else
YEdit.Text := VarList.Items[index];
VarList.Items.Delete(index);
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.XinBtnClick(Sender: TObject);
var
index: integer;
begin
index := VarList.ItemIndex;
if (index > -1) and (XEdit.Text = '') then
begin
XEdit.Text := VarList.Items[index];
VarList.Items.Delete(index);
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.XOutBtnClick(Sender: TObject);
begin
if XEdit.Text <> '' then
begin
VarList.Items.Add(XEdit.Text);
XEdit.Text := '';
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.YInBtnClick(Sender: TObject);
var
index: integer;
begin
index := VarList.ItemIndex;
if (index > -1) and (YEdit.Text = '') then
begin
YEdit.Text := VarList.Items[index];
VarList.Items.Delete(index);
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.YOutBtnClick(Sender: TObject);
begin
if YEdit.Text <> '' then
begin
VarList.Items.Add(YEdit.Text);
YEdit.Text := '';
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.Compute; procedure TPlotXYFrm.Compute;
var var
Xmin, Xmax, Ymin, Ymax, SSx, SSY, t, DF: double; xMean, yMean, xVariance, yVariance, xStddev, yStddev: double;
Xmean, Ymean, Xvariance, Yvariance, Xstddev, Ystddev, ConfBand: double; SXX, SXY, SYY, R, slope, intercept, t, confBand: Double;
X, Y, R, SEPred, Slope, Intercept, predicted, sedata: double; sePred, predicted, sedata: double;
i: integer; i, xCol, yCol, N, DF: integer;
Xcol, Ycol, N, NoSelected: integer; xValues: DblDyneVec = nil;
Xpoints: DblDyneVec = nil; yValues: DblDyneVec = nil;
Ypoints: DblDyneVec = nil;
UpConf: DblDyneVec = nil; UpConf: DblDyneVec = nil;
lowConf: DblDyneVec = nil; lowConf: DblDyneVec = nil;
cellstring: string;
ColNoSelected: IntDyneVec= nil; ColNoSelected: IntDyneVec= nil;
C: TWinControl; C: TWinControl;
msg: String; msg: String;
lReport: TStrings; lReport: TStrings;
begin begin
SetLength(Xpoints, NoCases); xCol := OS3MainFrm.DataGrid.Rows[0].IndexOf(XEdit.Text);
SetLength(Ypoints, NoCases); yCol := OS3MainFrm.DataGrid.Rows[0].IndexOf(YEdit.Text);
SetLength(UpConf, NoCases);
SetLength(lowConf, NoCases);
SetLength(ColNoSelected, NoVariables);
Xcol := 0;
Ycol := 0;
for i := 1 to NoVariables do
begin
cellstring := OS3MainFrm.DataGrid.Cells[i,0];
if cellstring = XEdit.Text then Xcol := i;
if cellstring = YEdit.Text then Ycol := i;
end;
// Validation // Validation
if not Validate(msg, C, Xcol, Ycol) then if not Validate(msg, C, Xcol, Ycol) then
@ -221,76 +134,36 @@ begin
exit; exit;
end; end;
NoSelected := 2; SetLength(ColNoSelected, 2);
ColNoSelected[0] := Xcol; ColNoSelected[0] := Xcol;
ColNoSelected[1] := Ycol; ColNoSelected[1] := Ycol;
Xmax := -Infinity;
Xmin := Infinity;
Ymax := -Infinity;
Ymin := Infinity;
Xmean := 0.0;
Ymean := 0.0;
XVariance := 0.0;
YVariance := 0.0;
SSX := 0.0;
SSY := 0.0;
R := 0.0;
N := 0; xValues := CollectValues(OS3MainFrm.DataGrid, xCol, ColNoSelected);
for i := 1 to NoCases do yValues := CollectValues(OS3MainFrm.DataGrid, yCol, ColNoSelected);
if (Length(yValues) = 0) then
begin begin
if not GoodRecord(i, NoSelected, ColNoSelected) then continue; ErrorMsg('No y data');
inc(N);
X := StrToFloat(OS3MainFrm.DataGrid.Cells[Xcol,i]);
Y := StrToFloat(OS3MainFrm.DataGrid.Cells[Ycol,i]);
XPoints[N-1] := X;
YPoints[N-1] := Y;
XMax := Max(X, XMax);
XMin := Min(X, XMin);
YMax := Max(Y, YMax);
YMin := Min(Y, YMin);
XMean := XMean + X;
YMean := YMean + Y;
SSX := SSX + sqr(X);
SSY := SSY + sqr(Y);
R := R + X * Y;
end;
if N < 1 then
begin
ErrorMsg('No data values.');
exit; exit;
end; end;
if (Length(xValues) <> Length(yValues)) then
begin
ErrorMsg('Different count of x and y values.');
exit;
end;
N := Length(yValues);
// Trim array lengths Calc_MeanVarStddevSS(xValues, xMean, xVariance, xStdDev, SXX);
SetLength(Xpoints, NoCases); Calc_MeanVarStddevSS(yValues, yMean, yVariance, yStdDev, SYY);
SetLength(Ypoints, NoCases); SXY := 0;
SetLength(UpConf, NoCases); for i := 0 to N-1 do
SetLength(lowConf, NoCases); SXY := SXY + xValues[i] * yValues[i];
// sort on X R := (SXY - xMean * yMean * N) / ((N - 1) * xStdDev * yStdDev);
SortOnX(XPoints, YPoints); sePred := sqrt(1.0 - sqr(R)) * yStdDev * sqrt((N - 1) / (N - 2));
slope := R * yStdDev / xStdDev;
intercept := yMean - slope * xMean;
// calculate statistics // Print the descriptive statistics to the output frame
XVariance := SSX - sqr(XMean) / N;
XVariance := XVariance / (N - 1);
XStdDev := sqrt(XVariance);
YVariance := SSY - sqr(YMean) / N;
YVariance := YVariance / (N - 1);
YStdDev := sqrt(YVariance);
R := R - Xmean * Ymean / N;
R := R / (N - 1);
R := R / (XStdDev * YStdDev);
SEPred := sqrt(1.0 - sqr(R)) * YStdDev;
SEPred := SEPred * sqrt((N - 1) / (N - 2));
XMean := XMean / N;
YMean := YMean / N;
Slope := R * YStdDev / XStdDev;
Intercept := YMean - Slope * XMean;
// Now, print the descriptive statistics to the output form if requested
lReport := TStringList.Create; lReport := TStringList.Create;
try try
lReport.Add('X vs. Y PLOT'); lReport.Add('X vs. Y PLOT');
@ -308,9 +181,9 @@ begin
lReport.Add(''); lReport.Add('');
lReport.Add('Regression:'); lReport.Add('Regression:');
lReport.Add(' Correlation: %8.3f', [R]); lReport.Add(' Correlation: %8.3f', [R]);
lReport.Add(' Slope: %8.3f', [Slope]); lReport.Add(' Slope: %8.3f', [slope]);
lReport.Add(' Intercept: %8.3f', [Intercept]); lReport.Add(' Intercept: %8.3f', [intercept]);
lReport.Add(' Standard Error of Estimate: %8.3f', [SEPred]); lReport.Add(' Standard Error of Estimate: %8.3f', [sePred]);
lReport.Add(' Number of good cases: %8d', [N]); lReport.Add(' Number of good cases: %8d', [N]);
FReportFrame.DisplayReport(lReport); FReportFrame.DisplayReport(lReport);
@ -321,51 +194,35 @@ begin
// Get upper and lower confidence points for each X value // Get upper and lower confidence points for each X value
if ConfChk.Checked then if ConfChk.Checked then
begin begin
ConfBand := StrToFloat(ConfEdit.Text) / 100.0; SortOnX(xValues, yValues);
SetLength(UpConf, N);
SetLength(lowConf, N);
confBand := StrToFloat(ConfEdit.Text) / 100.0;
DF := N - 2; DF := N - 2;
t := InverseT(ConfBand, DF); t := InverseT(confBand, DF);
for i := 0 to N-1 do for i := 0 to N-1 do
begin begin
X := XPoints[i]; predicted := slope * xValues[i] + intercept;
predicted := slope * X + intercept; seData := sePred * sqrt(1.0 + 1/N + sqr(xValues[i] - XMean) / SXX);
sedata := SEPred * sqrt(1.0 + (1.0 / N) + (sqr(X - XMean) / SSx)); upConf[i] := predicted + t * seData;
UpConf[i] := predicted + (t * sedata); lowConf[i] := predicted - t * seData;
lowConf[i] := predicted - (t * sedata);
YMax := Max(YMax, UpConf[i]);
YMin := Min(YMin, LowConf[i]);
end; end;
end end
else else
ConfBand := 0.0; confBand := 0.0;
// Plot the values (and optional line and confidence band if elected) // Plot the values (and optional line and confidence band if elected)
PlotXY(Xpoints, Ypoints, UpConf, LowConf, XMean, YMean, R, Slope, Intercept); PlotXY(xValues, yValues, upConf, lowConf, xMean, yMean, R, slope, intercept);
// cleanup
ColNoSelected := nil;
lowConf := nil;
UpConf := nil;
Ypoints := nil;
Xpoints := nil;
end;
procedure TPlotXYFrm.AdjustConstraints;
begin
ParamsPanel.Constraints.MinHeight := OptionsGroup.Top + OptionsGroup.Height +
OptionsGroup.BorderSpacing.Bottom + ButtonBevel.Height +
CloseBtn.Height + CloseBtn.BorderSpacing.Top;
ParamsPanel.Constraints.MinWidth := OptionsGroup.Width * 2 - XInBtn.Width div 2 - XInBtn.BorderSpacing.Left;
Constraints.MinHeight := ParamsPanel.Constraints.MinHeight + ParamsPanel.BorderSpacing.Around*2;
Constraints.MinWidth := ParamsPanel.Constraints.MinWidth + 200;
end; end;
procedure TPlotXYFrm.PlotXY(XPoints, YPoints, UpConf, LowConf: DblDyneVec; procedure TPlotXYFrm.PlotXY(XPoints, YPoints, UpConf, LowConf: DblDyneVec;
XMean, YMean, R, Slope, Intercept: Double); XMean, YMean, R, Slope, Intercept: Double);
var var
tmpX, tmpY: array of Double; tmpX: array of Double = nil;
tmpY: array of Double = nil;
xmin, xmax, ymin, ymax: Double; xmin, xmax, ymin, ymax: Double;
rightLabels: TListChartSource; rightLabels: TListChartSource;
topLabels: TListChartSource; topLabels: TListChartSource;
@ -429,9 +286,32 @@ begin
end; end;
procedure TPlotXYfrm.Reset;
var
i: integer;
begin
XEdit.Text := '';
YEdit.Text := '';
ConfEdit.Text := FormatFloat('0.0', DEFAULT_CONFIDENCE_LEVEL_PERCENT);
LineChk.Checked := false;
MeansChk.Checked := false;
ConfChk.Checked := false;
VarList.Items.Clear;
for i := 1 to NoVariables do
VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]);
if Assigned(FChartFrame) then
FChartFrame.Clear;
if Assigned(FReportFrame) then
FReportFrame.Clear;
UpdateBtnStates;
end;
procedure TPlotXYFrm.UpdateBtnStates; procedure TPlotXYFrm.UpdateBtnStates;
begin begin
XinBtn.Enabled := (VarList.ItemIndex > -1) and (XEdit.Text = ''); XInBtn.Enabled := (VarList.ItemIndex > -1) and (XEdit.Text = '');
XoutBtn.Enabled := (XEdit.Text <> ''); XoutBtn.Enabled := (XEdit.Text <> '');
YinBtn.Enabled := (VarList.ItemIndex > -1) and (YEdit.Text = ''); YinBtn.Enabled := (VarList.ItemIndex > -1) and (YEdit.Text = '');
@ -449,14 +329,14 @@ function TPlotXYFrm.Validate(out AMsg: String; out AControl: TWinControl;
begin begin
Result := false; Result := false;
if (Xcol = 0) then if (Xcol < 0) then
begin begin
AControl := XEdit; AControl := XEdit;
AMsg := 'No case selected for X.'; AMsg := 'No case selected for X.';
exit; exit;
end; end;
if (Ycol = 0) then if (Ycol < 0) then
begin begin
AControl := YEdit; AControl := YEdit;
AMsg := 'No case selected for Y.'; AMsg := 'No case selected for Y.';
@ -466,11 +346,78 @@ begin
end; end;
procedure TPlotXYFrm.VarListDblClick(Sender: TObject);
var
index: integer;
begin
index := VarList.ItemIndex;
if index > -1 then
begin
if XEdit.Text = '' then
XEdit.Text := VarList.Items[index]
else
YEdit.Text := VarList.Items[index];
VarList.Items.Delete(index);
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.VarListSelectionChange(Sender: TObject; User: boolean); procedure TPlotXYFrm.VarListSelectionChange(Sender: TObject; User: boolean);
begin begin
UpdateBtnStates; UpdateBtnStates;
end; end;
procedure TPlotXYFrm.XInBtnClick(Sender: TObject);
var
index: integer;
begin
index := VarList.ItemIndex;
if (index > -1) and (XEdit.Text = '') then
begin
XEdit.Text := VarList.Items[index];
VarList.Items.Delete(index);
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.XOutBtnClick(Sender: TObject);
begin
if XEdit.Text <> '' then
begin
VarList.Items.Add(XEdit.Text);
XEdit.Text := '';
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.YInBtnClick(Sender: TObject);
var
index: integer;
begin
index := VarList.ItemIndex;
if (index > -1) and (YEdit.Text = '') then
begin
YEdit.Text := VarList.Items[index];
VarList.Items.Delete(index);
UpdateBtnStates;
end;
end;
procedure TPlotXYFrm.YOutBtnClick(Sender: TObject);
begin
if YEdit.Text <> '' then
begin
VarList.Items.Add(YEdit.Text);
YEdit.Text := '';
UpdateBtnStates;
end;
end;
end. end.

View File

@ -48,6 +48,7 @@ function PoissonCDF(n: Integer; a: double): Double;
procedure Calc_MaxMin(const AData: DblDyneVec; out AMax, AMin: Double); procedure Calc_MaxMin(const AData: DblDyneVec; out AMax, AMin: Double);
procedure Calc_MeanStdDev(const AData: DblDyneVec; out AMean, AStdDev: Double); procedure Calc_MeanStdDev(const AData: DblDyneVec; out AMean, AStdDev: Double);
procedure Calc_MeanVarStdDev(const AData: DblDyneVec; out AMean, AVariance, AStdDev: Double); procedure Calc_MeanVarStdDev(const AData: DblDyneVec; out AMean, AVariance, AStdDev: Double);
procedure Calc_MeanVarStdDevSS(const AData: DblDyneVec; out AMean, AVariance, AStdDev, ASumOfSquares: Double);
procedure Calc_SumSS(const AData: DblDyneVec; out Sum, SS: Double); procedure Calc_SumSS(const AData: DblDyneVec; out Sum, SS: Double);
@ -532,6 +533,31 @@ begin
end; end;
procedure Calc_MeanVarStdDevSS(const AData: DblDyneVec;
out AMean, AVariance, AStdDev, ASumOfSquares: Double);
var
sum: Double;
n: Integer;
begin
AMean := NaN;
AVariance := NaN;
AStdDev := NaN;
n := Length(AData);
if n = 0 then
exit;
Calc_SumSS(AData, sum, ASumOfSquares);
AMean := sum / n;
if n = 1 then
exit;
AVariance := (ASumOfSquares - sqr(sum) / n) / (n - 1);
AStdDev := sqrt(AVariance);
end;
procedure Calc_SumSS(const AData: DblDyneVec; out Sum, SS: Double); procedure Calc_SumSS(const AData: DblDyneVec; out Sum, SS: Double);
var var
i: Integer; i: Integer;