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>
<Filename Value="forms\analysis\nonparametric\binomialunit.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="BinomialFrm"/>
<ComponentName Value="BinomialForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="BinomialUnit"/>

View File

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

View File

@ -5,174 +5,173 @@ unit BinomialUnit;
interface
uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls,
OutputUnit, FunctionsLib, GraphLib;
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
FunctionsLib, GraphLib, BasicStatsReportAndChartFormUnit;
type
{ TBinomialFrm }
{ TBinomialForm }
TBinomialFrm = class(TForm)
Bevel1: TBevel;
Panel1: TPanel;
PlotChk: TCheckBox;
ResetBtn: TButton;
ComputeBtn: TButton;
CloseBtn: TButton;
TBinomialForm = class(TBasicStatsReportAndChartForm)
FreqAEdit: TEdit;
FreqBEdit: TEdit;
PropAEdit: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
procedure ComputeBtnClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ResetBtnClick(Sender: TObject);
FreqALabel: TLabel;
FreqBLabel: TLabel;
PropALabel: TLabel;
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 declarations }
constructor Create(AOwner: TComponent); override;
procedure Reset; override;
end;
var
BinomialFrm: TBinomialFrm;
BinomialForm: TBinomialForm;
implementation
{$R *.lfm}
uses
Math;
Math,
TACustomSeries,
Globals, MathUnit, ChartFrameUnit;
{ TBinomialFrm }
{ TBinomialForm }
procedure TBinomialFrm.ResetBtnClick(Sender: TObject);
constructor TBinomialForm.Create(AOwner: TComponent);
begin
FreqAEdit.Text := '';
FreqBEdit.Text := '';
PropAEdit.Text := '';
FreqAEdit.SetFocus;
inherited;
FChartFrame.Chart.Margins.Bottom := 0;
FChartFrame.Chart.BottomAxis.AxisPen.Visible := true;
FChartFrame.Chart.BottomAxis.ZPosition := 1;
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
w: Integer;
begin
w := MaxValue([ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]);
ResetBtn.Constraints.MinWidth := w;
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;
P, Q, probability, z, correctedA, SumProb, mean, sigma: double;
A, B, N, i: integer;
xPts: DblDyneVec = nil;
yPts: DblDyneVec = nil;
lReport: TStrings;
msg: String;
C: TWinControl;
ser: TChartSeries;
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));
b := round(StrToFloat(FreqBEdit.Text));
p := StrToFloat(PropAEdit.Text);
N := A + b;
Q := 1.0 - p;
B := round(StrToFloat(FreqBEdit.Text));
P := StrToFloat(PropAEdit.Text);
N := A + B;
Q := 1.0 - P;
lReport := TStringList.Create;
try
lReport.Add('BINOMIAL PROBABILITY TEST');
lReport.Add('');
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('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('');
//Use normal distribution approximation
if (N > 35) then
if (N > N_MAX) then
begin
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);
lReport.Add('Z value for Normal Distribution approximation: %.3f', [z]);
Probability := probz(z);
// Use normal distribution approximation
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);
lReport.Add('Z value for normal nistribution approximation: %.3f', [z]);
Probability := NormalDist(z);
lReport.Add('Probability: %6.4f', [Probability]);
end else
//Use binomial fomula
begin
for X := 0 to A do
// Use binomial fomula
sumProb := 0;
for i := 0 to A do
begin
Probability := combos(X, N) * Power(p, X) * Power(Q, N - X);
lReport.Add('Probability of %2d: %6.4f', [X, Probability]);
SumProb := SumProb + Probability;
Probability := combos(i, N) * IntPower(P, i) * IntPower(Q, N-i);
lReport.Add('Probability of %d: %6.4f', [i, Probability]);
sumProb := sumProb + Probability;
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;
DisplayReport(lReport);
FReportFrame.DisplayReport(lReport);
finally
lReport.Free;
end;
if PlotChk.Checked then
// Plot the distribution
FChartFrame.Clear;
FChartFrame.SetXTitle('Values');
FChartFrame.SetYTitle('Probability');
if N > N_MAX then
begin
if N <= 35 then
begin
SetLength(GraphFrm.Xpoints,1,N+1);
SetLength(GraphFrm.Ypoints,1,N+1);
FChartFrame.SetTitle('Binomial Distribution' + LineEnding + '(approximated by Normal Distribution)');
mean := N*P;
sigma := sqrt(N * P * Q);
SetLength(xPts, N+1);
SetLength(yPts, N+1);
for i := 0 to N do
begin
GraphFrm.Xpoints[0,i] := i;
Probability := combos(i,N) * power(p,i) * power(Q,(N-i));
GraphFrm.Ypoints[0,i] := Probability;
xPts[i] := i;
yPts[i] := NormalDistDensity(i, mean, sigma);
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
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;
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;
function TBinomialFrm.Validate(out AMsg: String; out AControl: TWinControl): Boolean;
function TBinomialForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean;
var
x: Double;
n: Integer;
begin
Result := false;
if (FreqAEdit.Text = '') or (FreqBEdit.Text = '') or (PropAEdit.Text = '') then
@ -183,15 +182,15 @@ begin
if PropAEdit.Text = '' then AControl := PropAEdit;
exit;
end;
if not TryStrToFloat(FreqAEdit.Text, x) then
if not TryStrToInt(FreqAEdit.Text, n) then
begin
AMsg := 'No valid number.';
AMsg := 'No valid integer.';
AControl := FreqAEdit;
exit;
end;
if not TryStrToFloat(FreqBEdit.Text, x) then
if not TryStrToInt(FreqBEdit.Text, n) then
begin
AMsg := 'No valid number.';
AMsg := 'No valid integer.';
AControl := FreqBEdit;
exit;
end;
@ -201,12 +200,15 @@ begin
AControl := PropAEdit;
exit;
end;
if (x < 0) or (x > 1) then
begin
AMsg := 'Number between 0 and 1 expected.';
AControl := PropAEdit;
exit;
end;
Result := true;
end;
initialization
{$I binomialunit.lrs}
end.

View File

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

View File

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

View File

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