diff --git a/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.lfm b/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.lfm index a53e3f39d..c6f28e89e 100644 --- a/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.lfm +++ b/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.lfm @@ -527,13 +527,13 @@ inherited CompareDistFrm: TCompareDistFrm Height = 519 Top = 8 Width = 599 - ActivePage = CumFreqChartPage + ActivePage = ReportPage Align = alClient BorderSpacing.Left = 4 BorderSpacing.Top = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 - TabIndex = 1 + TabIndex = 0 TabOrder = 1 object ReportPage: TTabSheet Caption = 'Report' diff --git a/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.pas b/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.pas index 7e457b7f5..2a0a304bd 100644 --- a/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.pas +++ b/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.pas @@ -80,25 +80,39 @@ type FAutoSized: Boolean; CompareTo: TCompareTo; CompareDist: TCompareDist; + procedure CalcFreq(XValues, FreqValues, CumFreqValues: DblDyneVec; AMin, AIncrement: Double; ANumIntervals, AColIndex: Integer); + procedure CalcFreq(XValues, FreqValues, CumFreqValues: DblDyneVec; AMin, AMax: Double; ANumIntervals, ANumCases: Integer; ACompareDist: TCompareDist; DF1: Integer = -1; DF2: Integer = -1); + procedure CalcIntervals(var AMin, AMax, AIntervalsize: Double; out ANumIntervals: Integer); + function CalcMinMax(out AMin, AMax: Double; AColIndex: Integer): Integer; + procedure CalcTheoreticalDist(XValues, FreqValues, CumFreqValues: DblDyneVec; 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; ASeriesTitle1, ASeriesTitle2, AYTitle, ATitle: String); + procedure UpdateBtnStates; + procedure UpdateDF1; + function Validate(ANumCases: Integer; out AMsg: String; out AControl: TWinControl): Boolean; public procedure Reset; override; + end; @@ -272,35 +286,6 @@ begin 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); begin Close; @@ -326,10 +311,8 @@ var min1, max1, min2, max2: double; incrSize1: Double = 0.0; incrSize2: Double = 0.0; - KS: double; - cellVal, name1, name2, msg: string; + name1, name2, msg: string; C: TWinControl; - lReport: TStrings; begin // Get columns of the variables col1 := 0; @@ -409,29 +392,7 @@ begin name1 := VarOneEdit.Text; // Print distributions to report - lReport := TStringList.Create; - 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; + DisplayReport(xValue1, xValue2, var1Freq, var2Freq, cumfreq1, cumFreq2, name1, name2, noInts); // Plot the cumulative distributions Plot(FCumFreqChartFrame, cumFreq1, cumFreq2, VarOneEdit.Text, name2, @@ -443,6 +404,75 @@ begin 'Frequency', 'Plot of Distributions'); 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; @@ -553,6 +583,7 @@ begin AChartFrame.Clear; AChartFrame.SetTitle(ATitle); + AChartFrame.SetXTitle('Interval Index'); AChartFrame.SetYTitle(AYTitle); if BarPlotBtn.Down then diff --git a/applications/lazstats/source/units/mathunit.pas b/applications/lazstats/source/units/mathunit.pas index 6acd86f1d..85fde44bb 100644 --- a/applications/lazstats/source/units/mathunit.pas +++ b/applications/lazstats/source/units/mathunit.pas @@ -139,7 +139,7 @@ begin if x < 0 then Result := (1.0 - erf(-x / SQRT2)) * 0.5 else - Result := 0; + Result := 0.5; end; function NormalDistDensity(x, AMean, AStdDev: Double): Double; diff --git a/applications/lazstats/source/units/utils.pas b/applications/lazstats/source/units/utils.pas index 96ec4ea2f..ee46632c9 100644 --- a/applications/lazstats/source/units/utils.pas +++ b/applications/lazstats/source/units/utils.pas @@ -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: 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; implementation uses - Math, ToolWin; + StrUtils, Math, ToolWin; // https://stackoverflow.com/questions/4093595/create-ttoolbutton-runtime procedure AddButtonToToolbar(AToolButton: TToolButton; AToolBar: TToolBar); @@ -225,6 +226,28 @@ begin DoQuickSort(0, High(X)); 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; var i: Integer;