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

View File

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

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_MeanStdDev(const AData: DblDyneVec; out AMean, 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);
@ -532,6 +533,31 @@ begin
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);
var
i: Integer;