Files
lazarus-ccr/applications/lazstats/source/forms/analysis/comparisons/ttestunit.pas
wp_xxyyzz 2f33dc9f7b LazStats: initial commit.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7345 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2020-03-30 18:01:44 +00:00

625 lines
18 KiB
ObjectPascal

unit TTestUnit;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
ExtCtrls, StdCtrls,
MainUnit, Globals, FunctionsLib, OutputUnit, DataProcs;
type
{ TTtestFrm }
TTtestFrm = class(TForm)
Bevel1: TBevel;
Bevel2: TBevel;
Bevel3: TBevel;
Bevel4: TBevel;
Bevel5: TBevel;
GroupBox1: TGroupBox;
GroupCodeBtn: TCheckBox;
Grp1Code: TEdit;
Grp2Code: TEdit;
GrpCodeLabel1: TLabel;
GrpCodeLabel2: TLabel;
Memo1: TLabel;
Notebook1: TNotebook;
Page1: TPage;
Page2: TPage;
RadioGroup3: TRadioGroup;
ResetBtn: TButton;
ComputeBtn: TButton;
CloseBtn: TButton;
CorBetweenLabel: TLabel;
Cor12: TEdit;
CInterval: TEdit;
Grp: TEdit;
Label1: TLabel;
Var2: TEdit;
Var1: TEdit;
FirstVarLabel: TLabel;
GrpLabel: TLabel;
SecdVarLabel: TLabel;
ListBox1: TListBox;
SelVarLabel: TLabel;
N2: TEdit;
N1: TEdit;
SampSize2Label: TLabel;
SampSize1Label: TLabel;
SD2: TEdit;
SD1: TEdit;
SD2Label: TLabel;
SD1Label: TLabel;
Mean2: TEdit;
Mean1: TEdit;
Mean2Label: TLabel;
Mean1Label: TLabel;
Panel2: TPanel;
RadioGroup1: TRadioGroup;
RadioGroup2: TRadioGroup;
procedure ComputeBtnClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure GroupCodeBtnChange(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure RadioGroup1Click(Sender: TObject);
procedure RadioGroup2Click(Sender: TObject);
procedure ResetBtnClick(Sender: TObject);
private
{ private declarations }
FAutoSized: Boolean;
independent: boolean;
griddata: boolean;
function Validate(out AMsg: String; out AControl: TWinControl): Boolean;
public
{ public declarations }
end;
var
TtestFrm: TTtestFrm;
implementation
uses
Math;
{ TTtestFrm }
procedure TTtestFrm.ResetBtnClick(Sender: TObject);
var
i: integer;
begin
CInterval.Text := FormatFloat('0.0', DEFAULT_CONFIDENCE_LEVEL_PERCENT);
RadioGroup1.ItemIndex := 0;
RadioGroup2.ItemIndex := 0;
Notebook1.PageIndex := RadioGroup1.ItemIndex;
ListBox1.Clear;
Var1.Text := '';
Var2.Text := '';
Mean1.Text := '';
Mean2.Text := '';
SD1.Text := '';
SD2.Text := '';
N1.Text := '';
N2.Text := '';
Cor12.Text := '';
independent := true;
griddata := false;
GroupCodeBtn.Checked := false;
Grp.Text := '';
for i := 1 to NoVariables do
ListBox1.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]);
Grp.Text := '';
Grp1Code.Text := '';
Grp2Code.Text := '';
end;
procedure TTtestFrm.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;
Constraints.MinWidth := CInterval.Left + CInterval.Width + (Width - ResetBtn.Left) + ResetBtn.BorderSpacing.Left;
Bevel5.Width := SecdVarLabel.Canvas.TextWidth(SecdVarlabel.Caption);
//ListBox1.Constraints.MinHeight := Grp2Code.Top + Grp2Code.Height - Listbox1.Top - Var2.Height - Var2.BorderSpacing.Top;
//Constraints.MinHeight := ListBox1.Top + ListBox1.Constraints.MinHeight + Bevel2.Height + CloseBtn.Height + CloseBtn.BorderSpacing.Top*2;
FAutoSized := true;
end;
procedure TTtestFrm.FormCreate(Sender: TObject);
begin
Assert(OS3MainFrm <> nil);
end;
procedure TTtestFrm.FormShow(Sender: TObject);
begin
ResetBtnClick(self);
end;
procedure TTtestFrm.GroupCodeBtnChange(Sender: TObject);
begin
Grp1Code.Enabled := GroupCodeBtn.Checked;
Grp2Code.Enabled := GroupCodeBtn.Checked;
GrpCodeLabel1.Enabled := GroupCodeBtn.Checked;
GrpCodeLabel2.Enabled := GroupCodeBtn.Checked;
end;
procedure TTtestFrm.ComputeBtnClick(Sender: TObject);
var
M1, M2, Dif, stddev1, stddev2, r12, stderr1, stderr2: double;
tequal, tunequal, cov12, lowci, hici, F, Fp, df1, df2: double;
tprobability, value1, value2: double;
variance1, variance2, pooled, sedif, df, ConfInt, tconfint: double;
i, v1, v2, ncases1, ncases2, NoSelected: integer;
group, min, max: integer;
ColNoSelected: IntDyneVec;
label1Str, label2Str: string;
msg: String;
C: TWinControl;
lReport: TStrings;
begin
if not Validate(msg, C) then
begin
C.SetFocus;
MessageDlg(msg, mtError, [mbOK], 0);
ModalResult := mrNone;
exit;
end;
SetLength(ColNoSelected,NoVariables);
ncases1 := 0;
ncases2 := 0;
variance1 := 0.0;
variance2 := 0.0;
M1 := 0.0;
M2 := 0.0;
Dif := 0.0;
r12 := 0.0;
v1 := 0;
v2 := 0;
stddev1 := 0.0;
stddev2 := 0.0;
ConfInt := (100.0 - StrToFloat(CInterval.Text)) / 2.0 ;
ConfInt := (100.0 - ConfInt) / 100.0; // one tail
if independent then
Var2.Text := Grp.Text;
// data read from grid
if griddata then
begin
for i := 1 to NoVariables do
begin
if Var1.Text = OS3MainFrm.DataGrid.Cells[i,0] then
begin
v1 := i;
ColNoSelected[0] := i;
label1Str := Var1.Text;
end;
if Var2.Text = OS3MainFrm.DataGrid.Cells[i,0] then
begin
v2 := i;
ColNoSelected[1] := i;
label2Str := Var2.Text;
end;
end; // next variable
ncases1 := 0;
ncases2 := 0;
NoSelected := 2;
M1 := 0.0;
M2 := 0.0;
variance1 := 0.0;
variance2 := 0.0;
r12 := 0.0;
if not independent then // correlated data
begin
for i := 1 to NoCases do
begin
if not GoodRecord(i,NoSelected,ColNoSelected) then continue;
ncases1 := ncases1 + 1;
value1 := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[v1,i]));
value2 := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[v2,i]));
M1 := M1 + value1;
M2 := M2 + value2;
variance1 := variance1 + value1 * value1;
variance2 := variance2 + value2 * value2;
r12 := r12 + value1 * value2;
end;
ncases2 := ncases1;
variance1 := variance1 - (M1 * M1 / ncases1);
variance1 := variance1 / (ncases1 - 1);
stddev1 := sqrt(variance1);
variance2 := variance2 - (M2 * M2 / ncases2);
variance2 := variance2 / (ncases2 - 1);
stddev2 := sqrt(variance2);
r12 := r12 - (M1 * M2 / ncases1);
r12 := r12 / (ncases1 - 1);
cov12 := r12;
r12 := r12 / (stddev1 * stddev2);
M1 := M1 / ncases1;
M2 := M2 / ncases2;
Dif := M1 - M2;
end; //if not independent
if independent then
begin
if GroupCodeBtn.Checked then
begin
min := StrToInt(Grp1Code.Text);
max := StrToInt(Grp2Code.Text);
{
response := InputBox('Group 1','Enter the code for group 1','1');
min := StrToInt(response);
response := InputBox('Group 2','Enter the code for group 2','2');
max := StrToInt(response);
}
end else
begin
min := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[v2,1])));
max := min;
end;
for i := 2 to NoCases do
begin
if not GoodRecord(i,NoSelected,ColNoSelected) then continue;
group := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[v2,i])));
if GroupCodeBtn.Checked = false then
begin
if group < min then min := group;
if group > max then max := group;
end;
end;
for i := 1 to NoCases do
begin
if not GoodRecord(i,NoSelected,ColNoSelected) then continue;
value1 := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[v1,i]));
value2 := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[v2,i]));
group := round(value2);
if group = min then
begin
M1 := M1 + value1;
variance1 := variance1 + (value1 * value1);
ncases1 := ncases1 + 1;
end else if group = max then
begin
M2 := M2 + value1;
variance2 := variance2 + (value1 * value1);
ncases2 := Ncases2 + 1;
end;
end; // next case
variance1 := variance1 - ((M1 * M1) / ncases1);
variance1 := variance1 / (ncases1 - 1);
stddev1 := sqrt(variance1);
variance2 := variance2 - ((M2 * M2) / ncases2);
variance2 := variance2 / (ncases2 - 1);
stddev2 := sqrt(variance2);
M1 := M1 / ncases1;
M2 := M2 / ncases2;
Dif := M1 - M2;
Label1Str := format('Group %d',[min]);
Label2Str := format('Group %d',[max]);
end; // if independent data
end; // if reading grid data
if not griddata then // data read from form
begin
M1 := StrToFloat(Mean1.Text);
M2 := StrToFloat(Mean2.Text);
stddev1 := StrToFloat(SD1.Text);
stddev2 := StrToFloat(SD2.Text);
ncases1 := round(StrToFloat(N1.Text));
ncases2 := round(StrToFloat(N2.Text));
variance1 := stddev1 * stddev1;
variance2 := stddev2 * stddev2;
Label1Str := 'Group 1';
Label2Str := 'Group 2';
Dif := M1 - M2;
if not independent then
begin
r12 := StrToFloat(Cor12.Text);
cov12 := r12 * stddev1 * stddev2;
end;
end;
// Initialize output form
lReport := TStringList.Create;
try
lReport.Add('COMPARISON OF TWO MEANS');
lReport.Add('');
// Calculate pooled and independent t and z values and test statistic
if independent then
begin
stderr1 := sqrt(variance1 / ncases1);
Stderr2 := sqrt(variance2 / ncases2);
lReport.Add('Variable Mean Variance Std.Dev. S.E.Mean N');
lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label1Str, M1, variance1, stddev1, stderr1, ncases1]);
lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label2Str, M2, variance2, stddev2, stderr2, ncases2]);
lReport.Add('');
pooled := ((ncases1-1) * variance1) + ((ncases2-1) * variance2);
pooled := pooled / (ncases1 + ncases2 - 2);
pooled := pooled * ( 1.0 / ncases1 + 1.0 / ncases2);
sedif := sqrt(pooled);
tequal := dif / sedif;
df := ncases1 + ncases2 - 2;
tprobability := probt(tequal,df);
if RadioGroup3.ItemIndex = 1 then tprobability := 0.5 * tprobability;
lReport.Add('Assuming equal variances, t = %.3f with probability = %.4f and %.0f degrees of freedom', [
tequal, tprobability, df
]);
lReport.Add('Difference = %.2f and Standard Error of difference = %.2f', [dif, sedif]);
tconfint := inverset(ConfInt,df);
lowci := dif - tconfint * sedif;
hici := dif + tconfint * sedif;
lReport.Add('Confidence interval = (%.2f ... %.2f)', [lowci, hici]);
// now for unequal variances
sedif := sqrt((variance1 / ncases1) + (variance2 / ncases2));
tunequal := dif / sedif;
df := sqr((variance1 / ncases1) + (variance2 / ncases2));
df := df / (sqr(variance1 / ncases1) / (ncases1 - 1) + sqr(variance2 / ncases2) / (ncases2 - 1) );
tprobability := probt(tequal,df);
if RadioGroup3.ItemIndex = 1 then tprobability := 0.5 * tprobability;
lReport.Add('Assuming unequal variances, t = %.3f with probability = %.4f and %.0f degrees of freedom', [
tunequal, tprobability, df
]);
lReport.Add('Difference = %.2f and Standard Error of difference = %.2f', [dif, sedif]);
tconfint := inverset(ConfInt,df);
lowci := dif - tconfint * sedif;
hici := dif + tconfint * sedif;
lReport.Add('Confidence interval = (%.2f ... %.2f)', [lowci, hici]);
df1 := ncases1 - 1;
df2 := ncases2 - 1;
if variance1 > variance2 then
begin
F := variance1 / variance2;
Fp := probf(F,df1,df2);
end else
begin
F := variance2 / variance1;
Fp := probf(F,df2,df1);
end;
lReport.Add('F test for equal variances = %.3f, Probability = %.4f', [F, fp]);
end
else
// dependent t test
begin
stderr1 := sqrt(variance1 / ncases1);
Stderr2 := sqrt(variance2 / ncases2);
lReport.Add('Variable Mean Variance Std.Dev. S.E.Mean N');
lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label1Str, M1, variance1, stddev1, stderr1, ncases1]);
lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label2Str,M2, variance2, stddev2, stderr2, ncases2]);
lReport.Add('');
sedif := variance1 + variance2 - (2.0 * cov12);
sedif := sqrt(sedif / ncases1);
tequal := Dif / sedif;
df := ncases1 - 1;
tprobability := probt(tequal,df);
lReport.Add('Assuming dependent samples, t = %.3f with probability = %.4f and %.0f degrees of freedom', [
tequal, tprobability, df
]);
lReport.Add('Correlation between %s and %s = %.3f', [Label1Str, Label2Str, r12]);
lReport.Add('Difference = %.2f and Standard Error of difference = %.2f', [dif, sedif]);
tconfint := inverset(ConfInt,df);
lowci := dif - tconfint * sedif;
hici := dif + tconfint * sedif;
lReport.Add('Confidence interval = (%.2f ... %.2f)', [lowci, hici]);
tequal := variance1 - variance2;
tequal := tequal / sqrt( (4 * variance1 * variance2)/(ncases1 - 2) * (1.0 - sqr(r12)) );
df := ncases1 - 2;
tprobability := probt(tequal,df);
lReport.Add('t for test of equal variances = %.3f with probability = %.4f', [tequal, tprobability]);
end;
DisplayReport(lReport);
finally
lReport.Free;
ColNoSelected := nil;
end;
end;
procedure TTtestFrm.ListBox1Click(Sender: TObject);
VAR index : integer;
begin
index := ListBox1.ItemIndex;
if not independent then
begin
if Var1.Text <> '' then Var2.Text := ListBox1.Items.Strings[index]
else Var1.Text := ListBox1.Items.Strings[index];
end;
if independent then
begin
if Var1.Text <> '' then Grp.Text := ListBox1.Items.Strings[index]
else Var1.Text := ListBox1.Items.Strings[index];
end;
end;
procedure TTtestFrm.RadioGroup1Click(Sender: TObject);
VAR
index : integer;
begin
index := RadioGroup1.ItemIndex;
Notebook1.PageIndex := index;
if index = 0 then
begin
// Panel2.Visible := true;
// Panel1.Visible := false;
griddata := false;
end
else
begin
// Panel1.Visible := true;
// Panel2.Visible := false;
griddata := true;
if RadioGroup2.ItemIndex = 1 then
begin
SecdVarLabel.Visible := true;
Var2.Visible := true;
Grp.Visible := false;
GrpLabel.Visible := false;
end
else
begin
SecdVarLabel.Visible := false;
Var2.Visible := false;
Grp.Visible := true;
GrpLabel.Visible := true;
end;
end;
end;
procedure TTtestFrm.RadioGroup2Click(Sender: TObject);
var
index: integer;
begin
index := RadioGroup2.ItemIndex;
independent := (index = 0);
Grp.Visible := independent;
GrpLabel.Visible := independent;
GroupCodeBtn.Visible := independent;
GroupBox1.Visible := independent;
SecdVarLabel.Visible := not independent;
Var2.Visible := not independent;
{
if index = 0 then
begin
independent := true;
CorBetweenLabel.Visible := false;
Cor12.Visible := false;
Grp.Visible := true;
GrpLabel.Visible := true;
GroupCodeBtn.Visible := true;
Groupbxo1.Visible := true;
SecdVarLabel.Visible := false;
Var2.Visible := false;
end
else
begin
independent := false;
CorBetweenLabel.Visible := true;
Cor12.Visible := true;
GrpLabel.Visible := false;
Grp.Visible := false;
GroupCodeBtn.Visible := false;
SecdVarLabel.Visible := true;
Var2.Visible := true;
end;
}
end;
function TTtestFrm.Validate(out AMsg: String; out AControl: TWinControl): Boolean;
var
n: Integer;
x: Double;
begin
Result := false;
AControl := nil;
AMsg := '';
if Notebook1.PageIndex = 0 then
begin
if (Mean1.Text = '') or not TryStrToFloat(Mean1.Text, x) then
begin
AControl := Mean1;
AMsg := 'Invalid input for the mean of sample 1';
exit;
end;
if (SD1.Text = '') or not TryStrToFloat(SD1.Text, x) or (x <= 0) then
begin
AControl := SD1;
AMsg := 'Invald input for the standard deviation of sample 1';
exit;
end;
if (N1.Text = '') or not TryStrToInt(N1.Text, n) or (n <= 0) then
begin
AControl := N1;
AMsg := 'Invald input for the size of sample 1';
exit;
end;
if (Mean2.Text = '') or not TryStrToFloat(Mean2.Text, x) then
begin
AControl := Mean2;
AMsg := 'Invalid input for the mean of sample 2';
exit;
end;
if (SD2.Text = '') or not TryStrToFloat(SD2.Text, x) or (x <= 0) then
begin
AControl := SD2;
AMsg := 'Invald input for the standard deviation of sample 2';
exit;
end;
if (N2.Text = '') or not TryStrToInt(N2.Text, n) or (n <= 0) then
begin
AControl := N2;
AMsg := 'Invald input for the size of sample 2';
exit;
end;
end else
if Notebook1.PageIndex = 1 then
begin
if (Var1.Text = '') then
begin
AControl := Var1;
AMsg := 'Variable 1 not specified.';
exit;
end;
if Var2.Visible and (Var2.Text = '') then
begin
AControl := Var2;
AMsg := 'Variable 2 not specified.';
exit;
end;
if Grp.Visible and (Grp.Text = '') then
begin
AControl := Grp;
AMsg := 'Group variable not specified.';
exit;
end;
if Grp1Code.Visible and ((Grp1Code.Text = '') or not TryStrToInt(Grp1Code.Text, n))then
begin
AControl := Grp1Code;
AMsg := 'Code for group 1 missing.';
exit;
end;
if Grp2Code.Visible and ((Grp2Code.Text = '') or not TryStrToInt(Grp2Code.Text, n))then
begin
AControl := Grp2Code;
AMsg := 'Code for group 2 missing.';
exit;
end;
end;
Result := true;
end;
initialization
{$I ttestunit.lrs}
end.