unit BinomialUnit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, FunctionsLib, GraphLib, BasicStatsReportAndChartFormUnit; type { TBinomialForm } TBinomialForm = class(TBasicStatsReportAndChartForm) FreqAEdit: TEdit; FreqBEdit: TEdit; PropAEdit: TEdit; FreqALabel: TLabel; FreqBLabel: TLabel; PropALabel: TLabel; private protected procedure AdjustConstraints; override; procedure Compute; override; function Validate(out AMsg: String; out AControl: TWinControl): Boolean; override; public constructor Create(AOwner: TComponent); override; procedure Reset; override; end; var BinomialForm: TBinomialForm; implementation {$R *.lfm} uses Math, TACustomSeries, Globals, MathUnit, ChartFrameUnit; { TBinomialForm } constructor TBinomialForm.Create(AOwner: TComponent); begin inherited; FChartFrame.Chart.Margins.Bottom := 0; FChartFrame.Chart.BottomAxis.AxisPen.Visible := true; FChartFrame.Chart.BottomAxis.ZPosition := 1; end; 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 P, Q, probability, z, correctedA, SumProb, mean, sigma: double; A, B, N, i: integer; xPts: DblDyneVec = nil; yPts: DblDyneVec = nil; lReport: TStrings; ser: TChartSeries; begin A := round(StrToFloat(FreqAEdit.Text)); 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 %.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(''); if (N > N_MAX) then begin // 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 begin // Use binomial fomula sumProb := 0; for i := 0 to A do begin 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(''); lReport.Add('Binomial Probability of %d or less out of %d: %.4f', [A, N, sumProb]); end; FReportFrame.DisplayReport(lReport); finally lReport.Free; end; // Plot the distribution FChartFrame.Clear; FChartFrame.SetXTitle('Values'); FChartFrame.SetYTitle('Probability'); if N > N_MAX then begin 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 xPts[i] := i; yPts[i] := NormalDistDensity(i, mean, sigma); end; end else 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 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 begin AMsg := 'Value not specified.'; if FreqAEdit.Text = '' then AControl := FreqAEdit; if FreqBEdit.Text = '' then AControl := FreqBEdit; if PropAEdit.Text = '' then AControl := PropAEdit; exit; end; if not TryStrToInt(FreqAEdit.Text, n) then begin AMsg := 'No valid integer.'; AControl := FreqAEdit; exit; end; if not TryStrToInt(FreqBEdit.Text, n) then begin AMsg := 'No valid integer.'; AControl := FreqBEdit; exit; end; if not TryStrToFloat(PropAEdit.Text, x) then begin AMsg := 'No valid number.'; 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; end.