LazStats: Inherit BinomialUnit from TBasicStatsReportAndChartFrame

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7817 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-10-27 17:14:52 +00:00
parent 677bb5d75f
commit ffd5f1cc88
6 changed files with 208 additions and 262 deletions

View File

@ -785,7 +785,7 @@
<Unit85> <Unit85>
<Filename Value="forms\analysis\nonparametric\binomialunit.pas"/> <Filename Value="forms\analysis\nonparametric\binomialunit.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<ComponentName Value="BinomialFrm"/> <ComponentName Value="BinomialForm"/>
<HasResources Value="True"/> <HasResources Value="True"/>
<ResourceBaseClass Value="Form"/> <ResourceBaseClass Value="Form"/>
<UnitName Value="BinomialUnit"/> <UnitName Value="BinomialUnit"/>

View File

@ -1,109 +1,46 @@
object BinomialFrm: TBinomialFrm inherited BinomialForm: TBinomialForm
Left = 802 Left = 802
Height = 175 Height = 409
Top = 338 Top = 338
Width = 340 Width = 657
HelpType = htKeyword HelpType = htKeyword
HelpKeyword = 'html/ProbabilityofaBinomialEvent.htm' HelpKeyword = 'html/ProbabilityofaBinomialEvent.htm'
AutoSize = True
BorderStyle = bsSingle
Caption = 'Binomial Probability Calculator' Caption = 'Binomial Probability Calculator'
ClientHeight = 175 ClientHeight = 409
ClientWidth = 340 ClientWidth = 657
OnActivate = FormActivate inherited ParamsPanel: TPanel
OnCreate = FormCreate Height = 393
OnShow = FormShow Width = 299
Position = poMainFormCenter ClientHeight = 393
LCLVersion = '2.1.0.0' ClientWidth = 299
object ResetBtn: TButton inherited CloseBtn: TButton
AnchorSideTop.Control = Bevel1 Left = 244
AnchorSideTop.Side = asrBottom Top = 368
AnchorSideRight.Control = ComputeBtn TabOrder = 6
Left = 127
Height = 25
Top = 145
Width = 54
Anchors = [akTop, akRight]
AutoSize = True
BorderSpacing.Left = 12
BorderSpacing.Top = 8
BorderSpacing.Right = 8
BorderSpacing.Bottom = 8
Caption = 'Reset'
OnClick = ResetBtnClick
TabOrder = 0
end end
object ComputeBtn: TButton inherited ComputeBtn: TButton
AnchorSideTop.Control = Bevel1 Left = 160
AnchorSideTop.Side = asrBottom Top = 368
AnchorSideRight.Control = CloseBtn TabOrder = 5
Left = 189
Height = 25
Top = 145
Width = 76
Anchors = [akTop, akRight]
AutoSize = True
BorderSpacing.Left = 8
BorderSpacing.Top = 8
BorderSpacing.Right = 8
BorderSpacing.Bottom = 8
Caption = 'Compute'
OnClick = ComputeBtnClick
TabOrder = 1
end end
object CloseBtn: TButton inherited ResetBtn: TButton
AnchorSideTop.Control = Bevel1 Left = 98
AnchorSideTop.Side = asrBottom Top = 368
AnchorSideRight.Control = Owner TabOrder = 4
AnchorSideRight.Side = asrBottom
Left = 273
Height = 25
Top = 145
Width = 55
Anchors = [akTop, akRight]
AutoSize = True
BorderSpacing.Left = 8
BorderSpacing.Top = 8
BorderSpacing.Right = 12
BorderSpacing.Bottom = 8
Caption = 'Close'
ModalResult = 11
TabOrder = 2
end end
object Bevel1: TBevel inherited HelpBtn: TButton
AnchorSideLeft.Control = Owner Left = 39
AnchorSideTop.Control = Panel1 Top = 368
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = Owner
AnchorSideRight.Side = asrBottom
Left = 0
Height = 9
Top = 128
Width = 340
Anchors = [akTop, akLeft, akRight]
Shape = bsBottomLine
end end
object Panel1: TPanel inherited ButtonBevel: TBevel
AnchorSideLeft.Control = Owner Top = 352
AnchorSideTop.Control = Owner Width = 299
Left = 12 end
Height = 120 object FreqALabel: TLabel[5]
Top = 8
Width = 292
Alignment = taRightJustify
AutoSize = True
BorderSpacing.Left = 12
BorderSpacing.Top = 8
BorderSpacing.Right = 12
BevelOuter = bvNone
ClientHeight = 120
ClientWidth = 292
TabOrder = 3
object Label1: TLabel
AnchorSideTop.Control = FreqAEdit AnchorSideTop.Control = FreqAEdit
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
AnchorSideRight.Control = FreqAEdit AnchorSideRight.Control = FreqAEdit
Left = 2 Left = -3
Height = 15 Height = 15
Top = 12 Top = 12
Width = 239 Width = 239
@ -112,85 +49,87 @@ object BinomialFrm: TBinomialFrm
Caption = 'Frequency of events observed in category ''A'':' Caption = 'Frequency of events observed in category ''A'':'
ParentColor = False ParentColor = False
end end
object Label2: TLabel object FreqBLabel: TLabel[6]
AnchorSideTop.Control = FreqBEdit AnchorSideTop.Control = FreqBEdit
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
AnchorSideRight.Control = FreqBEdit AnchorSideRight.Control = FreqBEdit
Left = 3 Left = -2
Height = 15 Height = 15
Top = 39 Top = 43
Width = 238 Width = 238
Anchors = [akTop, akRight] Anchors = [akTop, akRight]
BorderSpacing.Right = 8 BorderSpacing.Right = 8
Caption = 'Frequency of events observed in category ''B'':' Caption = 'Frequency of events observed in category ''B'':'
ParentColor = False ParentColor = False
end end
object Label3: TLabel object PropALabel: TLabel[7]
AnchorSideLeft.Control = Panel1
AnchorSideTop.Control = PropAEdit AnchorSideTop.Control = PropAEdit
AnchorSideTop.Side = asrCenter AnchorSideTop.Side = asrCenter
Left = 0 AnchorSideRight.Control = PropAEdit
Left = -5
Height = 15 Height = 15
Top = 66 Top = 74
Width = 241 Width = 241
Anchors = [akTop, akRight]
Caption = 'Proportion of events expected in category ''A'':' Caption = 'Proportion of events expected in category ''A'':'
ParentColor = False ParentColor = False
end end
object FreqAEdit: TEdit object FreqAEdit: TEdit[8]
AnchorSideLeft.Control = PropAEdit AnchorSideLeft.Control = PropAEdit
AnchorSideTop.Control = Panel1 AnchorSideTop.Control = ParamsPanel
Left = 249 AnchorSideRight.Control = PropAEdit
AnchorSideRight.Side = asrBottom
Left = 244
Height = 23 Height = 23
Top = 8 Top = 8
Width = 43 Width = 55
Alignment = taRightJustify Alignment = taRightJustify
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Top = 8 BorderSpacing.Top = 8
TabOrder = 0 TabOrder = 0
Text = 'FreqAEdit' Text = 'FreqAEdit'
end end
object FreqBEdit: TEdit object FreqBEdit: TEdit[9]
AnchorSideLeft.Control = PropAEdit AnchorSideLeft.Control = PropAEdit
AnchorSideTop.Control = FreqAEdit AnchorSideTop.Control = FreqAEdit
AnchorSideTop.Side = asrBottom AnchorSideTop.Side = asrBottom
Left = 249 AnchorSideRight.Control = PropAEdit
AnchorSideRight.Side = asrBottom
Left = 244
Height = 23 Height = 23
Top = 35 Top = 39
Width = 43 Width = 55
Alignment = taRightJustify Alignment = taRightJustify
BorderSpacing.Top = 4 Anchors = [akTop, akLeft, akRight]
BorderSpacing.Top = 8
TabOrder = 1 TabOrder = 1
Text = 'FreqBEdit' Text = 'FreqBEdit'
end end
object PropAEdit: TEdit object PropAEdit: TEdit[10]
AnchorSideLeft.Control = Label3
AnchorSideLeft.Side = asrBottom AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = FreqBEdit AnchorSideTop.Control = FreqBEdit
AnchorSideTop.Side = asrBottom AnchorSideTop.Side = asrBottom
Left = 249 AnchorSideRight.Control = ParamsPanel
AnchorSideRight.Side = asrBottom
Left = 244
Height = 23 Height = 23
Top = 62 Top = 70
Width = 43 Width = 55
Alignment = taRightJustify Alignment = taRightJustify
Anchors = [akTop, akRight]
BorderSpacing.Left = 8 BorderSpacing.Left = 8
BorderSpacing.Top = 4 BorderSpacing.Top = 8
TabOrder = 2 TabOrder = 2
Text = 'ProbAEdit' Text = 'ProbAEdit'
end end
object PlotChk: TCheckBox
AnchorSideLeft.Control = Panel1
AnchorSideTop.Control = PropAEdit
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = Panel1
AnchorSideRight.Side = asrBottom
Left = 167
Height = 19
Top = 101
Width = 125
Alignment = taLeftJustify
Anchors = [akTop, akRight]
BorderSpacing.Top = 16
Caption = 'Plot the distribution'
TabOrder = 3
end end
inherited ParamsSplitter: TSplitter
Left = 311
Height = 409
end
inherited PageControl: TPageControl
Left = 320
Height = 393
Width = 329
end end
end end

View File

@ -5,174 +5,173 @@ unit BinomialUnit;
interface interface
uses uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
StdCtrls, ExtCtrls, FunctionsLib, GraphLib, BasicStatsReportAndChartFormUnit;
OutputUnit, FunctionsLib, GraphLib;
type type
{ TBinomialFrm } { TBinomialForm }
TBinomialFrm = class(TForm) TBinomialForm = class(TBasicStatsReportAndChartForm)
Bevel1: TBevel;
Panel1: TPanel;
PlotChk: TCheckBox;
ResetBtn: TButton;
ComputeBtn: TButton;
CloseBtn: TButton;
FreqAEdit: TEdit; FreqAEdit: TEdit;
FreqBEdit: TEdit; FreqBEdit: TEdit;
PropAEdit: TEdit; PropAEdit: TEdit;
Label1: TLabel; FreqALabel: TLabel;
Label2: TLabel; FreqBLabel: TLabel;
Label3: TLabel; PropALabel: TLabel;
procedure ComputeBtnClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ResetBtnClick(Sender: TObject);
private private
function Validate(out AMsg: String; out AControl: TWinControl): Boolean;
{ private declarations } protected
procedure AdjustConstraints; override;
procedure Compute; override;
function Validate(out AMsg: String; out AControl: TWinControl): Boolean; override;
public public
{ public declarations } constructor Create(AOwner: TComponent); override;
procedure Reset; override;
end; end;
var var
BinomialFrm: TBinomialFrm; BinomialForm: TBinomialForm;
implementation implementation
{$R *.lfm}
uses uses
Math; Math,
TACustomSeries,
Globals, MathUnit, ChartFrameUnit;
{ TBinomialFrm } { TBinomialForm }
procedure TBinomialFrm.ResetBtnClick(Sender: TObject); constructor TBinomialForm.Create(AOwner: TComponent);
begin begin
FreqAEdit.Text := ''; inherited;
FreqBEdit.Text := ''; FChartFrame.Chart.Margins.Bottom := 0;
PropAEdit.Text := ''; FChartFrame.Chart.BottomAxis.AxisPen.Visible := true;
FreqAEdit.SetFocus; FChartFrame.Chart.BottomAxis.ZPosition := 1;
end; end;
procedure TBinomialFrm.FormActivate(Sender: TObject);
procedure TBinomialForm.AdjustConstraints;
begin
inherited;
ParamsPanel.Constraints.MinWidth := Max(
PropALabel.Width + PropAEdit.Width + PropAEdit.BorderSpacing.Left,
4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left
);
ParamsPanel.Constraints.MinHeight := PropAEdit.Top + PropAEdit.Height
end;
procedure TBinomialForm.Reset;
begin
inherited;
FreqAEdit.Clear;
FreqBEdit.Clear;
PropAEdit.Clear;
end;
procedure TBinomialForm.Compute;
const
N_MAX = 35;
var var
w: Integer; P, Q, probability, z, correctedA, SumProb, mean, sigma: double;
begin A, B, N, i: integer;
w := MaxValue([ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]); xPts: DblDyneVec = nil;
ResetBtn.Constraints.MinWidth := w; yPts: DblDyneVec = nil;
ComputeBtn.Constraints.MinWidth := w;
CloseBtn.Constraints.MinWidth := w;
end;
procedure TBinomialFrm.FormCreate(Sender: TObject);
begin
if GraphFrm = nil then
Application.CreateForm(TGraphFrm, GraphFrm);
end;
procedure TBinomialFrm.FormShow(Sender: TObject);
begin
ResetBtnClick(self);
end;
procedure TBinomialFrm.ComputeBtnClick(Sender: TObject);
var
p, Q, Probability, z, CorrectedA, SumProb : double;
A, b, N, X, i: integer;
lReport: TStrings; lReport: TStrings;
msg: String; ser: TChartSeries;
C: TWinControl;
begin begin
if not Validate(msg, C) then begin
C.SetFocus;
MessageDlg(msg, mtError,[mbOK], 0);
ModalResult := mrNone;
exit;
end;
SumProb := 0.0;
A := round(StrToFloat(FreqAEdit.Text)); A := round(StrToFloat(FreqAEdit.Text));
b := round(StrToFloat(FreqBEdit.Text)); B := round(StrToFloat(FreqBEdit.Text));
p := StrToFloat(PropAEdit.Text); P := StrToFloat(PropAEdit.Text);
N := A + b; N := A + B;
Q := 1.0 - p; Q := 1.0 - P;
lReport := TStringList.Create; lReport := TStringList.Create;
try try
lReport.Add('BINOMIAL PROBABILITY TEST'); lReport.Add('BINOMIAL PROBABILITY TEST');
lReport.Add(''); lReport.Add('');
lReport.Add('Frequency of %d out of %d observed', [A, N]); lReport.Add('Frequency of %d out of %d observed', [A, N]);
lReport.Add('The theoretical proportion expected in category A is %.f', [p]); lReport.Add('The theoretical proportion expected in category A is %.3f', [P]);
lReport.Add(''); lReport.Add('');
lReport.Add('The test is for the probability of a value in category A as small or smaller'); lReport.Add('The test is for the probability of a value in category A as small or smaller');
lReport.Add('than that observed given the expected proportion.'); lReport.Add('than that observed given the expected proportion.');
lReport.Add('');
//Use normal distribution approximation if (N > N_MAX) then
if (N > 35) then
begin begin
CorrectedA := A; // Use normal distribution approximation
if A < N * p then CorrectedA := A + 0.5; correctedA := A;
if A > N * p then CorrectedA := A - 0.5; if A < N * P then correctedA := A + 0.5;
z := (CorrectedA - N * p) / sqrt(N * p * Q); if A > N * P then correctedA := A - 0.5;
lReport.Add('Z value for Normal Distribution approximation: %.3f', [z]); z := (correctedA - N * P) / sqrt(N * P * Q);
Probability := probz(z); lReport.Add('Z value for normal nistribution approximation: %.3f', [z]);
Probability := NormalDist(z);
lReport.Add('Probability: %6.4f', [Probability]); lReport.Add('Probability: %6.4f', [Probability]);
end else end else
//Use binomial fomula
begin begin
for X := 0 to A do // Use binomial fomula
sumProb := 0;
for i := 0 to A do
begin begin
Probability := combos(X, N) * Power(p, X) * Power(Q, N - X); Probability := combos(i, N) * IntPower(P, i) * IntPower(Q, N-i);
lReport.Add('Probability of %2d: %6.4f', [X, Probability]); lReport.Add('Probability of %d: %6.4f', [i, Probability]);
SumProb := SumProb + Probability; sumProb := sumProb + Probability;
end; end;
lReport.Add('Binomial Probability of %d or less out of %d: %.4f', [A, N, SumProb]); lReport.Add('');
lReport.Add('Binomial Probability of %d or less out of %d: %.4f', [A, N, sumProb]);
end; end;
DisplayReport(lReport); FReportFrame.DisplayReport(lReport);
finally finally
lReport.Free; lReport.Free;
end; end;
if PlotChk.Checked then // Plot the distribution
FChartFrame.Clear;
FChartFrame.SetXTitle('Values');
FChartFrame.SetYTitle('Probability');
if N > N_MAX then
begin begin
if N <= 35 then FChartFrame.SetTitle('Binomial Distribution' + LineEnding + '(approximated by Normal Distribution)');
begin mean := N*P;
SetLength(GraphFrm.Xpoints,1,N+1); sigma := sqrt(N * P * Q);
SetLength(GraphFrm.Ypoints,1,N+1); SetLength(xPts, N+1);
SetLength(yPts, N+1);
for i := 0 to N do for i := 0 to N do
begin begin
GraphFrm.Xpoints[0,i] := i; xPts[i] := i;
Probability := combos(i,N) * power(p,i) * power(Q,(N-i)); yPts[i] := NormalDistDensity(i, mean, sigma);
GraphFrm.Ypoints[0,i] := Probability;
end; end;
GraphFrm.GraphType := 2;
GraphFrm.nosets := 1;
GraphFrm.nbars := N;
GraphFrm.BackColor := clCream;
GraphFrm.WallColor := clDkGray;
GraphFrm.FloorColor := clGray;
GraphFrm.Heading := 'Binomial Distribution';
GraphFrm.XTitle := 'Values';
GraphFrm.YTitle := 'Probability';
GraphFrm.barwideprop := 0.5;
GraphFrm.AutoScaled := true;
GraphFrm.ShowLeftWall := true;
GraphFrm.ShowRightWall := true;
GraphFrm.ShowBottomWall := true;
GraphFrm.ShowModal;
GraphFrm.Xpoints := nil;
GraphFrm.Ypoints := nil;
end else end else
MessageDlg('Cannot plot for N > 35', mtInformation, [mbOK], 0); begin
FChartFrame.SetTitle('Binomial Distribution');
SetLength(xPts, N+1);
SetLength(yPts, N+1);
for i := 0 to N do
begin
xPts[i] := i;
yPts[i] := Combos(i, N) * IntPower(p, i) * IntPower(Q, N-i);
end; end;
end;
if N < 50 then
ser := FChartFrame.PlotXY(ptBars, xPts, yPts, nil, nil, '', DATA_COLORS[0])
else
ser := FChartFrame.PlotXY(ptArea, xPts, yPts, nil, nil, '', DATA_COLORS[0]);
ser.ZPosition := 2;
FChartFrame.Chart.Legend.Visible := false;
end; end;
function TBinomialFrm.Validate(out AMsg: String; out AControl: TWinControl): Boolean;
function TBinomialForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean;
var var
x: Double; x: Double;
n: Integer;
begin begin
Result := false; Result := false;
if (FreqAEdit.Text = '') or (FreqBEdit.Text = '') or (PropAEdit.Text = '') then if (FreqAEdit.Text = '') or (FreqBEdit.Text = '') or (PropAEdit.Text = '') then
@ -183,15 +182,15 @@ begin
if PropAEdit.Text = '' then AControl := PropAEdit; if PropAEdit.Text = '' then AControl := PropAEdit;
exit; exit;
end; end;
if not TryStrToFloat(FreqAEdit.Text, x) then if not TryStrToInt(FreqAEdit.Text, n) then
begin begin
AMsg := 'No valid number.'; AMsg := 'No valid integer.';
AControl := FreqAEdit; AControl := FreqAEdit;
exit; exit;
end; end;
if not TryStrToFloat(FreqBEdit.Text, x) then if not TryStrToInt(FreqBEdit.Text, n) then
begin begin
AMsg := 'No valid number.'; AMsg := 'No valid integer.';
AControl := FreqBEdit; AControl := FreqBEdit;
exit; exit;
end; end;
@ -201,12 +200,15 @@ begin
AControl := PropAEdit; AControl := PropAEdit;
exit; exit;
end; end;
if (x < 0) or (x > 1) then
begin
AMsg := 'Number between 0 and 1 expected.';
AControl := PropAEdit;
exit;
end;
Result := true; Result := true;
end; end;
initialization
{$I binomialunit.lrs}
end. end.

View File

@ -1896,9 +1896,9 @@ end;
// Menu "Analysis" > "Nonparametric" > "Probability of a Binomial Event" // Menu "Analysis" > "Nonparametric" > "Probability of a Binomial Event"
procedure TOS3MainFrm.mnuAnalysisNonPar_BinomialClick(Sender: TObject); procedure TOS3MainFrm.mnuAnalysisNonPar_BinomialClick(Sender: TObject);
begin begin
if BinomialFrm = nil then if BinomialForm = nil then
Application.CreateForm(TBinomialFrm, BinomialFrm); Application.CreateForm(TBinomialForm, BinomialForm);
BinomialFrm.ShowModal; BinomialForm.Show;
end; end;
// Menu "Analysis" > "Nonparametric" > Kendall's Tau and Partial Tau" // Menu "Analysis" > "Nonparametric" > Kendall's Tau and Partial Tau"

View File

@ -208,7 +208,11 @@ begin
TBarSeries(Result).BarBrush.Color := AColor; TBarSeries(Result).BarBrush.Color := AColor;
end; end;
ptArea: ptArea:
begin
Result := TAreaSeries.Create(self); Result := TAreaSeries.Create(self);
TAreaSeries(Result).AreaBrush.Color := AColor;
TAreaSeries(Result).AreaLinesPen.Style := psClear;
end;
else else
raise Exception.Create('Unknown plot type.'); raise Exception.Create('Unknown plot type.');
end; end;

View File

@ -461,6 +461,7 @@ function probz(z : double) : double;
begin begin
Result := 0.5 + simpsonintegral(0.0,z); Result := 0.5 + simpsonintegral(0.0,z);
// wp: a faster code is in unit MathUnit, NormalDist(z).
end; end;
//----------------------------------------------------------------------- //-----------------------------------------------------------------------