LazStats: Fix bug in NormalDist. Nicer report layout in CompareDistUnit.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7724 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-09-30 21:52:40 +00:00
parent 10049ca6b0
commit 8092e5e9fa
4 changed files with 114 additions and 60 deletions

View File

@ -527,13 +527,13 @@ inherited CompareDistFrm: TCompareDistFrm
Height = 519 Height = 519
Top = 8 Top = 8
Width = 599 Width = 599
ActivePage = CumFreqChartPage ActivePage = ReportPage
Align = alClient Align = alClient
BorderSpacing.Left = 4 BorderSpacing.Left = 4
BorderSpacing.Top = 8 BorderSpacing.Top = 8
BorderSpacing.Right = 8 BorderSpacing.Right = 8
BorderSpacing.Bottom = 8 BorderSpacing.Bottom = 8
TabIndex = 1 TabIndex = 0
TabOrder = 1 TabOrder = 1
object ReportPage: TTabSheet object ReportPage: TTabSheet
Caption = 'Report' Caption = 'Report'

View File

@ -80,25 +80,39 @@ type
FAutoSized: Boolean; FAutoSized: Boolean;
CompareTo: TCompareTo; CompareTo: TCompareTo;
CompareDist: TCompareDist; CompareDist: TCompareDist;
procedure CalcFreq(XValues, FreqValues, CumFreqValues: DblDyneVec; procedure CalcFreq(XValues, FreqValues, CumFreqValues: DblDyneVec;
AMin, AIncrement: Double; ANumIntervals, AColIndex: Integer); AMin, AIncrement: Double; ANumIntervals, AColIndex: Integer);
procedure CalcFreq(XValues, FreqValues, CumFreqValues: DblDyneVec; procedure CalcFreq(XValues, FreqValues, CumFreqValues: DblDyneVec;
AMin, AMax: Double; ANumIntervals, ANumCases: Integer; AMin, AMax: Double; ANumIntervals, ANumCases: Integer;
ACompareDist: TCompareDist; DF1: Integer = -1; DF2: Integer = -1); ACompareDist: TCompareDist; DF1: Integer = -1; DF2: Integer = -1);
procedure CalcIntervals(var AMin, AMax, AIntervalsize: Double; procedure CalcIntervals(var AMin, AMax, AIntervalsize: Double;
out ANumIntervals: Integer); out ANumIntervals: Integer);
function CalcMinMax(out AMin, AMax: Double; AColIndex: Integer): Integer; function CalcMinMax(out AMin, AMax: Double; AColIndex: Integer): Integer;
procedure CalcTheoreticalDist(XValues, FreqValues, CumFreqValues: DblDyneVec; procedure CalcTheoreticalDist(XValues, FreqValues, CumFreqValues: DblDyneVec;
ANumIntervals, ANumCases: Integer; ACompareDist: TCompareDist; out AName: String); ANumIntervals, ANumCases: Integer; ACompareDist: TCompareDist; out AName: String);
procedure DisplayReport(XValue1, XValue2, FreqValues1, FreqValues2,
CumFreqValues1, CumFreqValues2: DblDyneVec; AName1, AName2: String;
ANumIntervals: Integer);
procedure Plot(AChartFrame: TChartFrame; Y1Values, Y2Values: DblDyneVec; procedure Plot(AChartFrame: TChartFrame; Y1Values, Y2Values: DblDyneVec;
ASeriesTitle1, ASeriesTitle2, AYTitle, ATitle: String); ASeriesTitle1, ASeriesTitle2, AYTitle, ATitle: String);
procedure UpdateBtnStates; procedure UpdateBtnStates;
procedure UpdateDF1; procedure UpdateDF1;
function Validate(ANumCases: Integer; function Validate(ANumCases: Integer;
out AMsg: String; out AControl: TWinControl): Boolean; out AMsg: String; out AControl: TWinControl): Boolean;
public public
procedure Reset; override; procedure Reset; override;
end; end;
@ -272,35 +286,6 @@ begin
end; end;
(*
if NormalDistChk.Checked then // normal distribution curve
begin
name2 := 'Normal';
min2 := -3.0;
max2 := 3.0;
range2 := max2 - min2;
incrsize2 := range2 / noints;
Xvalue2[0] := min2;
Xvalue2[noints] := max2;
for i := 1 to noInts do
begin
Xvalue2[i-1] := min2 + (i-1) * incrSize2;
Xvalue2[i] := min2 + (i) * incrSize2;
prob1 := probz(abs(Xvalue2[i-1]));
prob2 := probz(abs(Xvalue2[i]));
if prob1 > prob2 then
Var2Freq[i-1] := round((prob1 - prob2) * nCases)
else
Var2Freq[i-1] := round((prob2 - prob1) * nCases)
end;
Cumfreq2[0] := Var2Freq[0];
for i := 1 to noints do
Cumfreq2[i] := Cumfreq2[i-1] + Var2Freq[i];
end
end;
*)
procedure TCompareDistFrm.CloseBtnClick(Sender: TObject); procedure TCompareDistFrm.CloseBtnClick(Sender: TObject);
begin begin
Close; Close;
@ -326,10 +311,8 @@ var
min1, max1, min2, max2: double; min1, max1, min2, max2: double;
incrSize1: Double = 0.0; incrSize1: Double = 0.0;
incrSize2: Double = 0.0; incrSize2: Double = 0.0;
KS: double; name1, name2, msg: string;
cellVal, name1, name2, msg: string;
C: TWinControl; C: TWinControl;
lReport: TStrings;
begin begin
// Get columns of the variables // Get columns of the variables
col1 := 0; col1 := 0;
@ -409,29 +392,7 @@ begin
name1 := VarOneEdit.Text; name1 := VarOneEdit.Text;
// Print distributions to report // Print distributions to report
lReport := TStringList.Create; DisplayReport(xValue1, xValue2, var1Freq, var2Freq, cumfreq1, cumFreq2, name1, name2, noInts);
try
lReport.Add('DISTRIBUTION COMPARISON by Bill Miller');
lReport.Add('');
lReport.Add('%10s %10s %10s %10s %10s %10s', [
name1, name1, name1, name2, name2, name2
]);
lReport.Add('%10s %10s %10s %10s %10s %10s', [
'X1 Value', 'Frequency', 'Cum. Freq.', 'X2 Value', 'Frequency', 'Cum. Freq.'
]);
lReport.Add('---------- ---------- ---------- ---------- ---------- ----------');
for i := 1 to noints do
lReport.Add('%10.3f %10.0f %10.3f %10.3f %10.0f %10.3f', [
XValue1[i-1], Var1Freq[i-1], Cumfreq1[i-1], XValue2[i-1], Var2Freq[i-1], Cumfreq2[i-1]
]);
lReport.Add('');
KS := KolmogorovTest(noInts, Cumfreq1, noInts, Cumfreq2, 'D', lReport);
lReport.Add('Kolmogorov-Smirnov statistic: %5.3f', [KS]);
FReportFrame.DisplayReport(lReport);
finally
lReport.Free;
end;
// Plot the cumulative distributions // Plot the cumulative distributions
Plot(FCumFreqChartFrame, cumFreq1, cumFreq2, VarOneEdit.Text, name2, Plot(FCumFreqChartFrame, cumFreq1, cumFreq2, VarOneEdit.Text, name2,
@ -443,6 +404,75 @@ begin
'Frequency', 'Plot of Distributions'); 'Frequency', 'Plot of Distributions');
FreqChartPage.TabVisible := BothChk.Checked; FreqChartPage.TabVisible := BothChk.Checked;
(*
// Print distributions to report
name1 := CenterString(name1, 12);
name2 := CenterString(name2, 12);
lReport := TStringList.Create;
try
lReport.Add('DISTRIBUTION COMPARISON by Bill Miller');
lReport.Add('');
lReport.Add('%12s %12s %12s %12s %12s %12s', [
name1, name1, name1, name2, name2, name2
]);
lReport.Add('%12s %12s %12s %12s %12s %12s', [
CenterString('X1 Value', 12), CenterString('Frequency', 12), CenterString('Cum. Freq.', 12),
CenterString('X2 Value', 12), CenterString('Frequency', 12), CenterString('Cum. Freq.', 12)
]);
lReport.Add('------------ ------------ ------------ ------------ ------------ ------------');
for i := 1 to noints do
lReport.Add('%12.3f %12.0f %12.3f %12.3f %12.0f %12.3f', [
XValue1[i-1], Var1Freq[i-1], Cumfreq1[i-1], XValue2[i-1], Var2Freq[i-1], Cumfreq2[i-1]
]);
lReport.Add('');
KS := KolmogorovTest(noInts, Cumfreq1, noInts, Cumfreq2, '', lReport);
lReport.Add('Kolmogorov-Smirnov statistic: %5.3f', [KS]);
FReportFrame.DisplayReport(lReport);
finally
lReport.Free;
end;
*)
end;
procedure TCompareDistFrm.DisplayReport(XValue1, XValue2, FreqValues1, FreqValues2,
CumFreqValues1, CumFreqValues2: DblDyneVec; AName1, AName2: String;
ANumIntervals: Integer);
var
lReport: TStrings;
i: Integer;
KS: Double;
begin
AName1 := CenterString(AName1, 12);
AName2 := CenterString(AName2, 12);
lReport := TStringList.Create;
try
lReport.Add('DISTRIBUTION COMPARISON by Bill Miller');
lReport.Add('');
lReport.Add('%12s %12s %12s %12s %12s %12s', [
AName1, AName1, AName1, AName2, AName2, AName2
]);
lReport.Add('%12s %12s %12s %12s %12s %12s', [
CenterString('X1 Value', 12), CenterString('Frequency', 12), CenterString('Cum. Freq.', 12),
CenterString('X2 Value', 12), CenterString('Frequency', 12), CenterString('Cum. Freq.', 12)
]);
lReport.Add('------------ ------------ ------------ ------------ ------------ ------------');
for i := 0 to ANumIntervals-1 do
lReport.Add('%12.3f %12.0f %12.3f %12.3f %12.0f %12.3f', [
XValue1[i], FreqValues1[i], CumFreqValues1[i],
XValue2[i], FreqValues2[i], CumFreqValues2[i]
]);
lReport.Add('');
KS := KolmogorovTest(ANumIntervals, CumFreqValues1, ANumIntervals, CumFreqValues2, '', lReport);
lReport.Add('Kolmogorov-Smirnov statistic: %5.3f', [KS]);
FReportFrame.DisplayReport(lReport);
finally
lReport.Free;
end;
end; end;
@ -553,6 +583,7 @@ begin
AChartFrame.Clear; AChartFrame.Clear;
AChartFrame.SetTitle(ATitle); AChartFrame.SetTitle(ATitle);
AChartFrame.SetXTitle('Interval Index');
AChartFrame.SetYTitle(AYTitle); AChartFrame.SetYTitle(AYTitle);
if BarPlotBtn.Down then if BarPlotBtn.Down then

View File

@ -139,7 +139,7 @@ begin
if x < 0 then if x < 0 then
Result := (1.0 - erf(-x / SQRT2)) * 0.5 Result := (1.0 - erf(-x / SQRT2)) * 0.5
else else
Result := 0; Result := 0.5;
end; end;
function NormalDistDensity(x, AMean, AStdDev: Double): Double; function NormalDistDensity(x, AMean, AStdDev: Double): Double;

View File

@ -28,15 +28,16 @@ procedure Exchange(var a, b: String); overload;
procedure SortOnX(X: DblDyneVec; Y: DblDyneVec = nil; Z: DblDyneVec = nil); procedure SortOnX(X: DblDyneVec; Y: DblDyneVec = nil; Z: DblDyneVec = nil);
procedure SortOnX(X: DblDyneVec; Y: DblDyneMat); procedure SortOnX(X: DblDyneVec; Y: DblDyneMat);
procedure QuickSortOnX(X: DblDyneVec; Y: DblDyneVec = nil; Z: DblDyneVec = nil); procedure QuickSortOnX(X: DblDyneVec; Y: DblDyneVec = nil; Z: DblDyneVec = nil); // not 100% tested...
function CenterString(S: String; Width: Integer): String;
function IndexOfString(L: StrDyneVec; s: String): Integer; function IndexOfString(L: StrDyneVec; s: String): Integer;
implementation implementation
uses uses
Math, ToolWin; StrUtils, Math, ToolWin;
// https://stackoverflow.com/questions/4093595/create-ttoolbutton-runtime // https://stackoverflow.com/questions/4093595/create-ttoolbutton-runtime
procedure AddButtonToToolbar(AToolButton: TToolButton; AToolBar: TToolBar); procedure AddButtonToToolbar(AToolButton: TToolButton; AToolBar: TToolBar);
@ -225,6 +226,28 @@ begin
DoQuickSort(0, High(X)); DoQuickSort(0, High(X));
end; end;
function CenterString(S: String; Width: Integer): String;
var
n1, n2: Integer;
begin
n1 := Width - Length(S);
if n1 <= 0 then
begin
Result := S;
exit;
end;
n1 := n1 div 2;
if Length(S) + 2*n1 < Width then
n2 := n1+1
else
n2 := n1;
Result := DupeString(' ', n1) + S + DupeString(' ', n2);
end;
function IndexOfString(L: StrDyneVec; s: String): Integer; function IndexOfString(L: StrDyneVec; s: String): Integer;
var var
i: Integer; i: Integer;