Files
lazarus-ccr/applications/lazstats/source/forms/analysis/descriptive/comparedistunit.pas
2020-09-30 21:52:40 +00:00

850 lines
23 KiB
ObjectPascal

// Use file "cansas.laz" for testing
unit CompareDistUnit;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
StdCtrls, Buttons, ExtCtrls, ComCtrls, Spin,
FunctionsLib, Globals, GraphLib, DataProcs, MainDM, MainUnit,
BasicStatsFormUnit, ReportFrameUnit, ChartFrameUnit;
type
TCompareTo = (ctTheoreticalDistrib, ctVariable);
TCompareDist = (cd_Normal, cd_t, cd_ChiSq, cd_F, cd_Poisson);
{ TCompareDistFrm }
TCompareDistFrm = class(TBasicStatsForm)
Bevel1: TBevel;
DF1Edit: TEdit;
DF2Edit: TEdit;
DistGroup: TGroupBox;
DF1Label: TLabel;
DF2Label: TLabel;
NoIntervalsEdit: TSpinEdit;
NoIntervalsLabel: TLabel;
PageControl1: TPageControl;
ParamsSplitter: TSplitter;
NormalDistChk: TRadioButton;
FreqChartPage: TTabSheet;
tDistChk: TRadioButton;
ChiSqDistChk: TRadioButton;
FDistChk: TRadioButton;
PoissonDistChk: TRadioButton;
ReportPage: TTabSheet;
CumFreqChartPage: TTabSheet;
Notebook: TNotebook;
BarPlotBtn: TSpeedButton;
LinePlotBtn: TSpeedButton;
TheoreticalDistPage: TPage;
VariablePage: TPage;
ParamsPanel: TPanel;
BothChk: TCheckBox;
OptionsGroup: TGroupBox;
ResetBtn: TButton;
ComputeBtn: TButton;
CloseBtn: TButton;
CompareGroup: TRadioGroup;
VarOneEdit: TEdit;
VarTwoEdit: TEdit;
Label2: TLabel;
Label3: TLabel;
Var1InBtn: TBitBtn;
Var1OutBtn: TBitBtn;
Var2InBtn: TBitBtn;
Var2OutBtn: TBitBtn;
Label1: TLabel;
VarList: TListBox;
procedure CloseBtnClick(Sender: TObject);
procedure CompareGroupClick(Sender: TObject);
procedure ComputeBtnClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure DistChange(Sender: TObject);
procedure ResetBtnClick(Sender: TObject);
procedure Var1InBtnClick(Sender: TObject);
procedure Var1OutBtnClick(Sender: TObject);
procedure Var2InBtnClick(Sender: TObject);
procedure Var2OutBtnClick(Sender: TObject);
procedure VarListDblClick(Sender: TObject);
procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean);
private
FReportFrame: TReportFrame;
FCumFreqChartFrame: TChartFrame;
FFreqChartFrame: TChartFrame;
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;
var
CompareDistFrm: TCompareDistFrm;
implementation
{$R *.lfm}
uses
Math,
TACustomSeries, TASeries,
Utils, MathUnit;
{ TCompareDistFrm }
{ Get frequency and cumulative frequency of cases in each interval }
procedure TCompareDistFrm.CalcFreq(XValues, FreqValues, CumFreqValues: DblDyneVec;
AMin, AIncrement: Double; ANumIntervals, AColIndex: Integer);
var
j, k: Integer;
value: Double;
begin
// Get border points of the intervals
for j := 0 to ANumIntervals do // no "-1" because last point is needed
XValues[j] := AMin + j*AIncrement;
// count values in these intervals
for j := 1 to NoCases do
begin
if not ValidValue(j, AColIndex) then continue;
value := StrToFloat(OS3MainFrm.DataGrid.Cells[AColIndex, j]);
for k := 0 to ANumIntervals-1 do
begin
if (value >= XValues[k]) and (value < XValues[k+1]) then
FreqValues[k] := FreqValues[k] + 1;
end;
end;
// Calculate cumulative frequencies
CumFreqValues[0] := FreqValues[0];
for j := 1 to ANumIntervals-1 do
CumFreqValues[j] := CumFreqValues[j-1] + FreqValues[j];
end;
{ Calculate frequencies and cumulative frequencies for a theoretical distribution }
procedure TCompareDistFrm.CalcFreq(XValues, FreqValues, CumFreqValues: DblDyneVec;
AMin, AMax: Double; ANumIntervals, ANumCases: Integer; ACompareDist: TCompareDist;
DF1: Integer = -1; DF2: Integer = -1);
var
dx: Double;
i: Integer;
procedure Calc(AProb1, AProb2: Double);
begin
FreqValues[i] := abs(AProb2 - AProb1) * ANumCases;
end;
begin
dx := (AMax - AMin) / ANumIntervals;
for i := 0 to ANumIntervals do
XValues[i] := AMin + i * dx;
for i := 0 to ANumIntervals - 1 do
case ACompareDist of
cd_Normal:
Calc(NormalDist(XValues[i]), NormalDist(XValues[i+1]));
cd_t:
Calc(0.5 * ProbT(XValues[i], DF1), 0.5 * ProbT(XValues[i+1], DF1));
cd_ChiSq:
Calc(ChiSquaredProb(XValues[i], DF1), ChiSquaredProb(XValues[i+1], DF1));
cd_F:
Calc(ProbF(XValues[i], DF1, DF2), ProbF(XValues[i+1], DF1, DF2));
cd_Poisson:
Calc(PoissonCDF(round(XValues[i]), DF1), PoissonCDF(round(XValues[i+1]), DF1));
end;
CumFreqValues[0] := FreqValues[0];
for i := 1 to ANumIntervals - 1 do
CumFreqValues[i] := CumFreqValues[i-1] + FreqValues[i];
end;
procedure TCompareDistFrm.CalcIntervals(var AMin, AMax, AIntervalsize: Double;
out ANumIntervals: Integer);
var
intervalSize: Double;
m: Double;
e: Integer;
begin
intervalSize := (AMax - AMin) / NoIntervalsEdit.Value;
if intervalSize = 0 then intervalSize := 1;
MantisseAndExponent(intervalSize, m, e);
m := round(m);
AIntervalSize := m * IntPower(10, e);
AMin := floor(AMin / AIntervalSize) * AIntervalSize;
AMax := ceil(AMax / AIntervalSize) * AIntervalSize;
ANumIntervals := round((AMax - AMin) / AIntervalSize);
end;
{ Calculates minimum and maximum of the values in the given column.
Also, counts the valid numbers in this column and returns it as function result }
function TCompareDistFrm.CalcMinMax(out AMin, AMax: Double; AColIndex: Integer): Integer;
var
j: Integer;
value: Double;
numCases: Integer;
begin
AMin := Infinity;
AMax := -Infinity;
numCases := 0;
for j := 1 to NoCases do
begin
if not ValidValue(j, AColIndex) then continue;
value := StrToFloat(OS3MainFrm.DataGrid.Cells[AColIndex, j]);
if value > AMax then AMax := value;
if value < AMin then AMin := value;
inc(numCases);
end;
Result := numCases;
end;
procedure TCompareDistFrm.CalcTheoreticalDist(XValues, FreqValues, CumFreqValues: DblDyneVec;
ANumIntervals, ANumCases: Integer; ACompareDist: TCompareDist; out AName: String);
var
min, max: Double;
DF1: Integer = -1;
DF2: Integer = -1;
a: Double;
begin
if TryStrToFloat(DF1Edit.Text, a) then
DF1 := round(a);
if TryStrToFloat(DF2Edit.Text, a) then
DF2 := round(a);
case ACompareDist of
cd_Normal:
begin
min := -3.0;
max := 3.0;
AName := 'Normal dist';
end;
cd_t:
begin
min := -3.0;
max := 3.0;
AName := 't dist';
end;
cd_ChiSq:
begin
min := 0.0;
max := 20.0;
AName := 'Chi-sq dist';
end;
cd_F:
begin
min := 0.0;
max := 2.0;
AName := 'F dist';
end;
cd_Poisson:
; // will be handled separately
end;
CalcFreq(XValues, FreqValues, CumFreqValues, min, max, ANumIntervals, ANumCases, ACompareDist, DF1, DF2);
end;
procedure TCompareDistFrm.CloseBtnClick(Sender: TObject);
begin
Close;
end;
procedure TCompareDistFrm.CompareGroupClick(Sender: TObject);
begin
CompareTo := TCompareTo(CompareGroup.ItemIndex);
Notebook.PageIndex := CompareGroup.ItemIndex;
end;
procedure TCompareDistFrm.ComputeBtnClick(Sender: TObject);
var
var1Freq: DblDyneVec = nil; // could be IntDyneVec, but simpler charting this way
var2Freq: DblDyneVec = nil;
xValue1: DblDyneVec = nil;
xValue2: DblDyneVec = nil;
cumfreq1: DblDyneVec = nil;
cumfreq2: DblDyneVec = nil;
i, col1, col2, nCases, noInts: integer;
min1, max1, min2, max2: double;
incrSize1: Double = 0.0;
incrSize2: Double = 0.0;
name1, name2, msg: string;
C: TWinControl;
begin
// Get columns of the variables
col1 := 0;
for i := 1 to NoVariables do
if VarOneEdit.Text = OS3MainFrm.DataGrid.Cells[i, 0] then col1 := i;
col2 := 0;
if CompareTo = ctVariable then
for i := 1 to NoVariables do
if VarTwoEdit.Text = OS3MainFrm.DataGrid.Cells[i, 0] then col2 := i;
// Check existence of required variables
msg := '';
case CompareTo of
ctTheoreticalDistrib:
if col1 = 0 then
msg := 'Variable not specified.';
ctVariable:
if col1 = 0 then
msg := 'Variable One is not specified.'
else if col2 = 0 then
msg := 'Variable Two is not specified.';
end;
if msg <> '' then
begin
ErrorMsg(msg);
exit;
end;
// Get min and max values for variable in col1, as well as true number of cases
nCases := CalcMinMax(min1, max1, col1);
// Validate edit controls
if not Validate(nCases, msg, C) then begin
C.SetFocus;
ErrorMsg(msg);
end;
// Get number of intervals
CalcIntervals(min1, max1, incrSize1, noInts);
// Get mem for the arrays
SetLength(var1Freq, noInts);
Setlength(cumFreq1, noInts);
SetLength(xValue1, noInts + 1); // Border points of the intervals, one more than intervals
SetLength(var2Freq, noInts);
SetLength(cumFreq2, noInts);
Setlength(xValue2, noInts + 1);
// Repeat for variable 2 (if Compare To is selected as "Another variable")
if CompareTo = ctVariable then
begin
CalcMinMax(min2, max2, col2);
incrSize2 := (max2 - min2) / noInts;
name2 := VarTwoEdit.Text;
end;
// Get frequency of cases in each interval
CalcFreq(xValue1, var1Freq, cumFreq1, min1, incrSize1, noInts, col1);
// Repeat for 2nd variable, if required
if CompareTo = ctVariable then
CalcFreq(xValue2, var2Freq, cumFreq2, min2, incrSize2, noInts, col2);
// Get theoretical distribution frequencies for selected distribution, if required.
if CompareTo = ctTheoreticalDistrib then
begin
if CompareDist = cd_Poisson then
begin
CalcFreq(xValue2, var2Freq, cumFreq2, min1, min2, noInts, nCases, compareDist, StrToInt(DF1Edit.Text));
name2 := 'Poisson';
end
else
CalcTheoreticalDist(xValue2, var2Freq, cumFreq2, noInts, nCases, compareDist, name2);
end;
name1 := VarOneEdit.Text;
// Print distributions to report
DisplayReport(xValue1, xValue2, var1Freq, var2Freq, cumfreq1, cumFreq2, name1, name2, noInts);
// Plot the cumulative distributions
Plot(FCumFreqChartFrame, cumFreq1, cumFreq2, VarOneEdit.Text, name2,
'Cumulative Frequency', 'Plot of Cumulative Distributions');
// Plot the frequency distrigutions, if requested.
if BothChk.Checked then
Plot(FFreqChartFrame, var1Freq, var2Freq, VarOneEdit.Text, name2,
'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;
procedure TCompareDistFrm.DistChange(Sender: TObject);
begin
DF1Edit.Visible := CompareDist <> cd_Normal;
DF1Label.Visible := DF1Edit.Visible;
DF1Label.Caption := 'D.F.';
DF2Edit.Visible := CompareDist = cd_F;
DF2Label.Visible := DF2Edit.Visible;
if NormalDistChk.Checked then
CompareDist := cd_Normal
else if tDistChk.Checked then
begin
CompareDist := cd_t;
UpdateDF1;
end
else if ChiSqDistChk.Checked then
CompareDist := cd_ChiSq
else if FDistChk.Checked then
begin
CompareDist := cd_F;
DF1Label.Caption := 'D.F. 1';
end
else if PoissonDistChk.Checked then
begin
CompareDist := cd_Poisson;
DF1Label.Caption := 'Mean';
UpdateDF1;
end else
raise Exception.Create('Distribution not supported.');
end;
procedure TCompareDistFrm.FormActivate(Sender: TObject);
var
w: Integer;
begin
if FAutoSized then
exit;
w := MaxValue([ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]);
ResetBtn.Constraints.MinWidth := w;
ComputeBtn.Constraints.MinWidth := w;
CloseBtn.Constraints.MinWidth := w;
Notebook.AutoSize := true;
ParamsPanel.Constraints.MinWidth := Max(
3*w + 2*CloseBtn.BorderSpacing.Left,
Min(
CompareGroup.Width * 2 - Var1InBtn.Width + VarList.BorderSpacing.Right,
OptionsGroup.Width)
);
ParamsPanel.Constraints.MinHeight := Notebook.Top + Notebook.Height +
OptionsGroup.BorderSpacing.Top + OptionsGroup.Height + Bevel1.Height +
CloseBtn.Height + CloseBtn.BorderSpacing.Top;
Constraints.MinWidth := ParamsPanel.Constraints.MinWidth + 300;
Constraints.MinHeight := ParamsPanel.Constraints.MinHeight + ParamsPanel.BorderSpacing.Top * 2;
if Width < Constraints.MinWidth then Width := 1;
if Height < Constraints.MinHeight then Height := 1;
// Notebook.AutoSize := false;
Position := poDesigned;
FAutoSized := true;
end;
procedure TCompareDistFrm.FormCreate(Sender: TObject);
begin
Assert(OS3MainFrm <> nil);
if GraphFrm = nil then Application.CreateForm(TGraphFrm, GraphFrm);
InitForm(self);
FReportFrame := TReportFrame.Create(self);
FReportFrame.Parent := ReportPage;
FReportFrame.Align := alClient;
FCumFreqChartFrame := TChartFrame.Create(self);
FCumFreqChartFrame.Parent := CumFreqChartPage;
FCumFreqChartFrame.Align := alClient;
FCumFreqChartFrame.Chart.BottomAxis.Intervals.MaxLength := 80;
FCumFreqChartFrame.Chart.BottomAxis.Intervals.MinLength := 30;
FCumFreqChartFrame.SetYTitle('Cumulative frequency');
FCumFreqChartFrame.SetTitle('Plot of Cumulative Distributions');
FFreqChartFrame := TChartFrame.Create(self);
FFreqChartFrame.Parent := FreqChartPage;
FFreqChartFrame.Align := alClient;
FFreqChartFrame.Chart.BottomAxis.Intervals.MaxLength := 80;
FFreqChartFrame.Chart.BottomAxis.Intervals.MinLength := 30;
FFreqChartFrame.SetYTitle('Frequency');
FFreqChartFrame.SetTitle('Plot of Distributions');
Reset;
end;
procedure TCompareDistFrm.Plot(AChartFrame: TChartFrame; Y1Values, Y2Values: DblDyneVec;
ASeriesTitle1, ASeriesTitle2, AYTitle, ATitle: String);
var
ser1, ser2: TChartSeries;
plotType: TPlotType;
begin
AChartFrame.Clear;
AChartFrame.SetTitle(ATitle);
AChartFrame.SetXTitle('Interval Index');
AChartFrame.SetYTitle(AYTitle);
if BarPlotBtn.Down then
plotType := ptBars
else
plotType := ptLines;
ser1 := AChartFrame.PlotXY(plotType, nil, Y1Values, nil, nil, ASeriesTitle1, DATA_COLORS[0]);
ser2 := AChartFrame.PlotXY(plotType, nil, Y2Values, nil, nil, ASeriesTitle2, DATA_Colors[1]);
if (ser1 is TBarSeries) then
begin
with ser1 as TBarSeries do
begin
BarWidthPercent := 40;
BarOffsetPercent := -20;
end;
with ser2 as TBarSeries do
begin
BarWidthPercent := 40;
BarOffsetPercent := +20;
end;
end;
end;
procedure TCompareDistFrm.Reset;
var
i: integer;
begin
VarList.Clear;
VarOneEdit.Text := '';
VarTwoEdit.Text := '';
DF1Edit.Text := '';
DF2Edit.Text := '';
for i := 1 to NoVariables do
VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]);
CompareGroup.ItemIndex := 0;
CompareGroupClick(nil);
NormalDistChk.Checked := true;
DistChange(nil);
FReportFrame.Clear;
FCumFreqChartFrame.Clear;
FFreqChartFrame.Clear;
FreqChartPage.TabVisible := false;
end;
procedure TCompareDistFrm.ResetBtnClick(Sender: TObject);
begin
Reset;
end;
procedure TCompareDistFrm.UpdateBtnStates;
begin
Var1InBtn.Enabled := (VarList.ItemIndex > -1) and (VarOneEdit.Text = '');
Var2InBtn.Enabled := (VarList.ItemIndex > -1) and (VarTwoEdit.Text = '');
Var1OutBtn.Enabled := VarOneEdit.Text <> '';
Var2OutBtn.Enabled := VarTwoEdit.Text <> '';
FReportFrame.UpdateBtnStates;
FCumFreqChartFrame.UpdateBtnStates;
FFreqChartFrame.UpdateBtnStates;
end;
procedure TCompareDistFrm.UpdateDF1;
procedure DFCandidates(AVarName: String; out AMean: Double; out ANumCases: Integer);
var
col, i: Integer;
begin
for col := 1 to NoVariables do
if AVarName = OS3MainFrm.DataGrid.Cells[col, 0] then
begin
AMean := 0;
ANumCases := 0;
for i := 1 to NoCases do
if ValidValue(i, col) then
begin
AMean := AMean + StrToFloat(OS3MainFrm.DataGrid.Cells[col, i]);
inc(ANumCases);
end;
if ANumCases > 1 then
AMean := AMean / ANumCases
else
AMean := NaN;
exit;
end;
AMean := NaN;
ANumCases := -1;
end;
var
m: Double;
n: Integer;
begin
if (CompareTo = ctTheoreticalDistrib) then
begin
DFCandidates(VarOneEdit.Text, m, n);
case CompareDist of
cd_t :
if n > 0 then
DF1Edit.Text := IntToStr(n-1);
cd_Poisson:
if not IsNaN(m) then
DF1Edit.Text := FormatFloat('0', m);
end;
end;
end;
function TCompareDistFrm.Validate(ANumCases: Integer;
out AMsg: String; out AControl: TWinControl): Boolean;
var
n: Integer = 0;
begin
Result := false;
if CompareDist <> cd_Normal then
begin
if DF1Edit.Text = '' then
begin
AMsg := 'This control cannot be empty.';
AControl := DF1Edit;
exit;
end;
if not TryStrToInt(DF1Edit.Text, n) or (n < 0) then
begin
AMsg := 'Positive integer value required.';
AControl := DF1Edit;
exit;
end;
if (n >= ANumCases) and (CompareDist <> cd_Poisson) then
begin
AMsg := 'Degrees of freedom cannot be greater than the number of cases.';
AControl := DF1Edit;
exit;
end;
end;
if CompareDist = cd_F then
begin
if DF2Edit.Text = '' then
begin
AMsg := 'This control cannot be empty.';
AControl := DF2Edit;
exit;
end;
if not TryStrToInt(DF2Edit.Text, n) or (n < 0) then
begin
AMsg := 'Positive integer value required.';
AControl := DF2Edit;
exit;
end;
if n >= ANumCases then
begin
AMsg := 'Degrees of freedom cannot be greater than the number of cases.';
AControl := DF2Edit;
exit;
end;
end;
Result := true;
end;
procedure TCompareDistFrm.Var1InBtnClick(Sender: TObject);
var
i: integer;
begin
i := 0;
while (VarOneEdit.Text = '') and (i < VarList.Items.Count) do
begin
if VarList.Selected[i] then
begin
VarOneEdit.Text := VarList.Items[i];
VarList.Items.Delete(i);
i := 0;
end else
inc(i);
end;
UpdateBtnStates;
UpdateDF1;
end;
procedure TCompareDistFrm.Var1OutBtnClick(Sender: TObject);
begin
if VarOneEdit.Text <> '' then
begin
VarList.Items.Add(VarOneEdit.Text);
VarOneEdit.Text := '';
end;
UpdateBtnStates;
end;
procedure TCompareDistFrm.Var2InBtnClick(Sender: TObject);
var
i: integer;
begin
i := 0;
while (VarTwoEdit.Text = '') and (i < VarList.Items.Count) do
begin
if VarList.Selected[i] then
begin
VarTwoEdit.Text := VarList.Items[i];
VarList.Items.Delete(i);
i := 0;
end else
inc(i);
end;
UpdateBtnStates;
end;
procedure TCompareDistFrm.Var2OutBtnClick(Sender: TObject);
begin
if VarTwoEdit.Text <> '' then
begin
VarList.Items.Add(VarTwoEdit.Text);
VarTwoEdit.Text := '';
end;
UpdateBtnStates;
end;
procedure TCompareDistFrm.VarListDblClick(Sender: TObject);
var
index: Integer;
begin
index := VarList.ItemIndex;
if (index > -1) then
begin
if VarOneEdit.Text = '' then
begin
VarOneEdit.Text := VarList.Items[index];
VarList.Items.Delete(index);
end;
if CompareGroup.ItemIndex = 1 then // Compare to another variable
begin
VarTwoEdit.Text := VarList.Items[index];
VarList.Items.Delete(index);
end;
UpdateBtnStates;
end;
end;
procedure TCompareDistFrm.VarListSelectionChange(Sender: TObject; User: boolean);
begin
UpdateBtnStates;
end;
end.