LazStats: Refactor two-way ANOVA in BlkAnovaUnit. Use TAChart.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7853 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-11-09 21:52:26 +00:00
parent 6ce0fc13bd
commit bc5987793a
2 changed files with 615 additions and 346 deletions

View File

@ -7,8 +7,8 @@ unit BlkANOVAUnit;
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
StdCtrls, Buttons, ExtCtrls, ComCtrls,
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, StdCtrls, ExtCtrls,
ComCtrls, Buttons, Dialogs, LCLVersion,
TACustomSeries, TAStyles,
MainUnit, Globals, FunctionsLib, GraphLib,
ANOVATestsUnit, ReportFrameUnit, BasicStatsReportAndChartFormUnit;
@ -102,7 +102,7 @@ type
SlcSums: DblDyneVec; // 3 way slice sums
SlcCount: IntDyneVec; // 3 way slice counts
NoGrpsA, NoGrpsB, NoGrpsC: integer;
OrdMeansA, OrdMeansB, OrdMeansC: DblDyneVec; // reordered means for f1, f2, f3
// OrdMeansA, OrdMeansB, OrdMeansC: DblDyneVec; // reordered means for f1, f2, f3
AllAlpha, PostHocAlpha: double; // alphas for tests
// wsum : array[1..20,1..20,1..20] of double; // sums for 3 way
// ncnt : array[1..20,1..20,1..20] of integer; // n in 3 way cells
@ -120,10 +120,11 @@ type
procedure OneWayPostHoc;
procedure OneWayPlot;
function Calc2Way: Boolean;
procedure TwoWayTable(AReport: TStrings);
procedure Init2Way;
function Calc2Way(const DepValues, F1Values, F2Values: DblDyneVec): Boolean;
procedure TwoWayTable;
procedure TwoWayPlot;
procedure TwoWayContrasts(AReport: TStrings);
procedure TwoWayContrasts;
function Calc3Way: Boolean;
procedure ThreeWayTable(AReport: TStrings);
@ -139,6 +140,8 @@ type
FSeries: TChartSeries;
FChartCombobox: TCombobox;
FStyles: TChartStyles;
procedure PopulateChartCombobox(ThreeWay: Boolean);
procedure SelectTwoWayPlot(Sender: TObject);
protected
procedure AdjustConstraints; override;
@ -161,7 +164,7 @@ implementation
uses
Math,
TAChartUtils, TASeries,
TAChartUtils, TACustomSource, TASeries, TALegend,
Utils, MathUnit, MatrixUnit, ChartFrameUnit, GridProcs;
{ TBlksAnovaForm }
@ -335,17 +338,17 @@ begin
1 : // Single factor anova
begin
Init1Way;
if not Calc1Way(DepValues, F1Values) then
exit;
if Calc1Way(DepValues, F1Values) then
begin
OneWayTable;
OneWayPostHoc;
OneWayPlot;
exit;
end;
end;
2 : // Rwo-way anova
begin
{
SetLength(counts,Nf1cells,Nf2cells); // matrix for 2-way containing cell sizes
SetLength(sums,Nf1cells,Nf2cells); // matrix for 2-way containing cell sums
SetLength(vars,Nf1cells,Nf2cells); // matrix for 2-way containing sums of squares
@ -353,17 +356,17 @@ begin
SetLength(ColSums,Nf2cells); // 2 way col sums
SetLength(RowCount,Nf1cells); // 2 way row count
SetLength(ColCount,Nf2cells); // 2 way col count
SetLength(OrdMeansA,Nf1cells); // ordered means for factor 1
SetLength(OrdMeansB,Nf2cells); // ordered means for factor 2
if not Calc2Way then
exit;
TwoWayTable(lReport);
TwoWayContrasts(lReport);
FReportFrame.DisplayReport(lReport);
// SetLength(OrdMeansA,Nf1cells); // ordered means for factor 1
// SetLength(OrdMeansB,Nf2cells); // ordered means for factor 2
}
Init2Way;
if Calc2Way(DepValues, F1Values, F2Values) then
begin
TwoWayTable;
TwoWayContrasts;
TwoWayPlot;
end;
end;
3 : // three way anova
begin
@ -373,9 +376,9 @@ begin
SetLength(ColCount, Nf2cells); // 2 way col count
SetLength(SlcSums, Nf3cells); // 3 way slice sums
SetLength(SlcCount, Nf3cells); // 3 way slice counts
SetLength(OrdMeansA, Nf1cells); // ordered means for factor 1
SetLength(OrdMeansB, Nf2cells); // ordered means for factor 2
SetLength(OrdMeansC, Nf3cells); // ordered means for factor 3
// SetLength(OrdMeansA, Nf1cells); // ordered means for factor 1
// SetLength(OrdMeansB, Nf2cells); // ordered means for factor 2
// SetLength(OrdMeansC, Nf3cells); // ordered means for factor 3
SetLength(wsum, Nf1cells, Nf2cells, Nf3cells);
SetLength(wx2, Nf1cells, Nf2cells, Nf3cells);
SetLength(ncnt, Nf1cells, Nf2cells, Nf3cells);
@ -865,44 +868,70 @@ begin
end;
*)
function TBlksAnovaForm.Calc2Way: Boolean;
procedure TBlksAnovaForm.Init2Way;
begin
RowSums := nil;
ColSums := nil;
RowCount := nil;
ColCount := nil;
counts := nil;
sums := nil;
vars := nil;
SetLength(RowSums, NF1Cells); // 2-way row sums
SetLength(ColSums, NF2Cells); // 2-way column sums
SetLength(RowCount, NF1Cells); // 2-way row counts
SetLength(ColCount, NF2Cells); // 2-way column counts
SetLength(counts, NF1Cells, NF2Cells); // matrix for 2-way containing cell sizes
SetLength(sums, NF1Cells, NF2Cells); // matrix for 2-way containing cell sums
SetLength(vars, NF1Cells, NF2Cells); // matrix for 2-way containing sums of squares
end;
function TBlksAnovaForm.Calc2Way(const DepValues, F1Values, F2Values: DblDyneVec): Boolean;
var
i, j: integer;
grpA, grpB: integer;
Constant, RowsTotCnt, ColsTotCnt, SSCells : double;
X, X2: Double;
constant, rowsTotCnt, colsTotCnt, SSCells: double;
X, Xsq: Double;
begin
// initialize matrix values
NoGrpsA := maxf1 - minf1 + 1;
NoGrpsB := maxf2 - minf2 + 1;
for i := 0 to NoGrpsA-1 do
begin
RowSums[i] := 0.0;
RowCount[i] := 0;
for j := 0 to NoGrpsB-1 do
begin
counts[i, j] := 0;
sums[i, j] := 0.0;
vars[i, j] := 0.0;
end;
end;
NoGrpsA := MaxF1 - MinF1 + 1;
NoGrpsB := MaxF2 - MinF2 + 1;
for i := 0 to NoGrpsB-1 do
begin
ColCount[i] := 0;
ColSums[i] := 0.0;
end;
{
RowSums := Default(DblDyneVec);
ColSums := Default(DblDyneVec);
RowCount := Default(IntDyneVec);
ColCount := Default(IntDyneVec);
Counts := Default(IntDyneMat);
Sums := Default(DblDyneMat);
Vars := Default(DblDyneMat);
}
// Get working totals
N := 0;
MeanDep := 0.0;
SSDep := 0.0;
SSCells := 0.0;
RowsTotCnt := 0.0;
ColsTotCnt := 0.0;
// get working totals
for i := 1 to NoCases do
for i := 0 to High(DepValues) do
begin
grpA := round(F1Values[i]) - MinF1;
grpB := round(F2Values[i]) - MinF2;
X := DepValues[i];
Xsq := X * X;
Counts[grpA, grpB] := Counts[grpA, grpB] + 1;
Sums[grpA, grpB] := Sums[grpA, grpB] + X;
Vars[grpA, grpB] := Vars[grpA, grpB] + Xsq;
RowSums[grpA] := RowSums[grpA] + X;
ColSums[grpB] := ColSums[grpB] + X;
RowCount[grpA] := RowCount[grpA] + 1;
ColCount[grpB] := ColCount[grpB] + 1;
MeanDep := MeanDep + X;
SSDep := SSDep + Xsq;
N := N + 1;
{
if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue;
grpA := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i])));
grpB := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[2], i])));
@ -910,6 +939,7 @@ begin
X2 := X*X;
grpA := grpA - minf1 + 1;
grpB := grpB - minf2 + 1;
counts[grpA-1,grpB-1] := counts[grpA-1,grpB-1] + 1;
sums[grpA-1,grpB-1] := sums[grpA-1,grpB-1] + X;
vars[grpA-1,grpB-1] := vars[grpA-1,grpB-1] + X2;
@ -920,34 +950,42 @@ begin
MeanDep := MeanDep + X;
SSDep := SSDep + X2;
N := N + 1;
}
end;
// Calculate results
SSF1 := 0;
RowsTotCnt := 0;
for i := 0 to NoGrpsA-1 do
begin
SSF1 := SSF1 + ((RowSums[i] * RowSums[i]) / RowCount[i]);
SSF1 := SSF1 + sqr(RowSums[i]) / RowCount[i];
RowsTotCnt := RowsTotCnt + RowCount[i];
end;
SSF2 := 0;
ColsTotCnt := 0;
for j := 0 to NoGrpsB-1 do
begin
SSF2 := SSF2 + ((ColSums[j] * ColSums[j]) / ColCount[j]);
SSF2 := SSF2 + sqr(ColSums[j]) / ColCount[j];
ColsTotCnt := ColsTotCnt + ColCount[j];
end;
SSCells := 0;
for i := 0 to NoGrpsA-1 do
begin
for j := 0 to NoGrpsB-1 do
if counts[i,j] > 0 then
SSCells := SSCells + ((sums[i,j] * sums[i,j]) / counts[i,j]);
end;
if Counts[i, j] > 0 then
SSCells := SSCells + sqr(sums[i, j]) / Counts[i, j];
if N > 0 then Constant := (MeanDep * MeanDep) / N else Constant := 0.0;
SSF1 := SSF1 - Constant;
SSF2 := SSF2 - Constant;
SSF1F2 := SSCells - SSF1 - SSF2 - Constant;
if N > 0 then
constant := sqr(MeanDep) / N
else
constant := 0.0;
SSF1 := SSF1 - constant;
SSF2 := SSF2 - constant;
SSF1F2 := SSCells - SSF1 - SSF2 - constant;
SSErr := SSDep - SSCells;
SSDep := SSDep - Constant;
SSDep := SSDep - constant; // Must be after SSErr!
if (SSF1F2 < 0) or (SSF1 < 0) or (SSF2 < 0) then
begin
@ -961,15 +999,18 @@ begin
DFF2 := NoGrpsB - 1;
DFF1F2 := DFF1 * DFF2;
DFErr := DFTot - DFF1 - DFF2 - DFF1F2;
MSF1 := SSF1 / DFF1;
MSF2 := SSF2 / DFF2;
MSF1F2 := SSF1F2 / DFF1F2;
MSErr := SSErr / DFErr;
MSDep := SSDep / DFTot;
OmegaF1 := (SSF1 - DFF1 * MSErr) / (SSDep + MSErr);
OmegaF2 := (SSF2 - DFF2 * MSErr) / (SSDep + MSErr);
OmegaF1F2 := (SSF1F2 - DFF1F2 * MSErr) / (SSDep + MSErr);
Omega := OmegaF1 + OmegaF2 + OmegaF1F2;
MeanDep := MeanDep / N;
// F tests for fixed effects
@ -978,42 +1019,42 @@ begin
FF1 := abs(MSF1 / MSErr);
FF2 := abs(MSF2 / MSErr);
FF1F2 := abs(MSF1F2 / MSErr);
ProbF1 := probf(FF1,DFF1,DFErr);
ProbF2 := probf(FF2,DFF2,DFErr);
ProbF1F2 := probf(FF1F2,DFF1F2,DFErr);
end;
ProbF1 := ProbF(FF1, DFF1, DFErr);
ProbF2 := ProbF(FF2, DFF2, DFErr);
ProbF1F2 := ProbF(FF1F2, DFF1F2, DFErr);
end
else
// F tests if both factors are random
if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 1) then
begin
FF1 := abs(MSF1 / MSF1F2);
FF2 := abs(MSF2 / MSF1F2);
FF1F2 := abs(MSF1F2 / MSErr);
ProbF1 := probf(FF1,DFF1,DFF1F2);
ProbF2 := probf(FF2,DFF2,DFF1F2);
ProbF3 := probf(FF1F2,DFF1F2,DFErr);
end;
// F test if factor A is random
ProbF1 := ProbF(FF1, DFF1, DFF1F2);
ProbF2 := ProbF(FF2, DFF2, DFF1F2);
ProbF3 := ProbF(FF1F2, DFF1F2, DFErr);
end
else
// F test if factor A is random and factor B is fixed
if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 0) then
begin
FF1 := abs(MSF1 / MSErr);
FF2 := abs(MSF2 / MSF1F2);
FF1F2 := abs(MSF1F2 / MSErr);
ProbF1 := probf(FF1,DFF1,DFErr);
ProbF2 := probf(FF2,DFF2,DFF1F2);
ProbF3 := probf(FF1F2,DFF1F2,DFErr);
end;
// F test if factor b is random
ProbF1 := ProbF(FF1, DFF1, DFErr);
ProbF2 := ProbF(FF2, DFF2, DFF1F2);
ProbF3 := ProbF(FF1F2, DFF1F2, DFErr);
end
else
// F test if factor A is fixed and factor B is random
if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 1) then
begin
FF1 := abs(MSF1 / MSF1F2);
FF2 := abs(MSF2 / MSErr);
FF1F2 := abs(MSF1F2 / MSErr);
ProbF1 := probf(FF1,DFF1,DFF1F2);
ProbF2 := probf(FF2,DFF2,DFErr);
ProbF3 := probf(FF1F2,DFF1F2,DFErr);
ProbF1 := ProbF(FF1, DFF1, DFF1F2);
ProbF2 := ProbF(FF2, DFF2, DFErr);
ProbF3 := ProbF(FF1F2, DFF1F2, DFErr);
end;
if (ProbF1 > 1.0) then ProbF1 := 1.0;
@ -1029,62 +1070,60 @@ begin
Result := true;
end;
procedure TBlksAnovaForm.TwoWayTable(AReport: TStrings);
procedure TBlksAnovaForm.TwoWayTable;
const
FIXED_RANDOM: array[0..1] of string = ('fixed', 'random');
var
lReport: TStrings;
groupsize: integer;
MinVar, MaxVar, sumvars, sumDFrecip: double;
i, j: integer;
XBar, V, S, RowSS, ColSS: double;
sumfreqlogvar, c, bartlett, cochran, hartley, chiprob: double;
begin
AReport.Add('Two Way Analysis of Variance');
AReport.Add('');
AReport.Add('Variable analyzed: %s', [DepVarEdit.Text]);
AReport.Add('');
lReport := TStringList.Create;
try
lReport.Add('TWO-WAY ANALYSIS OF VARIANCE');
lReport.Add('');
lReport.Add('Variable analyzed: %s', [DepVarEdit.Text]);
outline := format('Factor A (rows) variable: %s',[Factor1Edit.Text]);
if Fact1Combo.ItemIndex = 0 then
outline := outline + ' (Fixed Levels)'
else
outline := outline + ' (Random Levels)';
AReport.Add(outline);
lReport.Add('Factor A (rows) variable: %s (%s levels)',[
Factor1Edit.Text, FIXED_RANDOM[Fact1Combo.ItemIndex]
]);
lReport.Add('Factor B (columns) variable: %s (%s levels)', [
Factor2Edit.Text, FIXED_RANDOM[Fact2Combo.ItemIndex]
]);
lReport.Add('');
outline := format('Factor B (columns) variable: %s',[Factor2Edit.Text]);
if Fact2Combo.ItemIndex = 0 then
outline := outline + ' (Fixed Levels)'
else
outline := outline + ' (Random Levels)';
AReport.Add(outline);
AReport.Add('');
AReport.Add('SOURCE D.F. SS MS F PROB.> F Omega Squared');
AReport.Add('');
AReport.Add('Among Rows %4.0f %8.3f %8.3f %8.3f %6.3f %6.3f', [DFF1, SSF1, MSF1, FF1, ProbF1, OmegaF1]);
AReport.Add('Among Columns %4.0f %8.3f %8.3f %8.3f %6.3f %6.3f', [DFF2, SSF2, MSF2, FF2, ProbF2, OmegaF2]);
AReport.Add('Interaction %4.0f %8.3f %8.3f %8.3f %6.3f %6.3f', [DFF1F2, SSF1F2, MSF1F2, FF1F2, ProbF1F2, OmegaF1F2]);
AReport.Add('Within Groups %4.0f %8.3f %8.3f', [DFErr, SSErr, MSErr]);
AReport.Add('Total %4.0f %8.3f %8.3f', [DFTot, SSDep, MSDep]);
AReport.Add('');
AReport.Add('Omega squared for combined effects = %8.3f', [Omega]);
AReport.Add('');
lReport.Add('SOURCE D.F. SS MS F PROB.> F Omega Squared');
lReport.Add('');
lReport.Add('Among Rows %4.0f %8.3f %8.3f %8.3f %6.3f %6.3f', [DFF1, SSF1, MSF1, FF1, ProbF1, OmegaF1]);
lReport.Add('Among Columns %4.0f %8.3f %8.3f %8.3f %6.3f %6.3f', [DFF2, SSF2, MSF2, FF2, ProbF2, OmegaF2]);
lReport.Add('Interaction %4.0f %8.3f %8.3f %8.3f %6.3f %6.3f', [DFF1F2, SSF1F2, MSF1F2, FF1F2, ProbF1F2, OmegaF1F2]);
lReport.Add('Within Groups %4.0f %8.3f %8.3f', [DFErr, SSErr, MSErr]);
lReport.Add('Total %4.0f %8.3f %8.3f', [DFTot, SSDep, MSDep]);
lReport.Add('');
lReport.Add('Omega squared for combined effects = %8.3f', [Omega]);
lReport.Add('');
if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) then
AReport.Add('Note: Denominator of F ratio is MSErr');
lReport.Add('Note: Denominator of F ratio is MSErr');
if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 1) then
AReport.Add('Note: Denominator of F ratio is MSAxB');
lReport.Add('Note: Denominator of F ratio is MSAxB');
if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 1) then
begin
AReport.Add('Note: Denominator of F ratio for A is MSAxB');
AReport.Add('and denominator for B and AxB is MSErr');
lReport.Add('Note: Denominator of F ratio for A is MSAxB');
lReport.Add(' and denominator for B and AxB is MSErr');
end;
if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 0) then
begin
AReport.Add('Note: Denominator of F ratio for B is MSAxB');
AReport.Add('and denominator for A and AxB is MSErr');
lReport.Add('Note: Denominator of F ratio for B is MSAxB');
lReport.Add('and denominator for A and AxB is MSErr');
end;
AReport.Add('');
AReport.Add('Descriptive Statistics');
AReport.Add('');
AReport.Add('GROUP Row Col. N MEAN VARIANCE STD.DEV.');
lReport.Add('');
lReport.Add('');
lReport.Add('DESCRIPTIVE STATISTICS');
lReport.Add('');
lReport.Add('GROUP Row Col. N MEAN VARIANCE STD.DEV.');
groupsize := counts[0, 0];
equal_grp := true;
MaxVar := 0.0;
@ -1114,7 +1153,7 @@ begin
sumfreqlogvar := sumfreqlogvar + ((counts[i,j] - 1.0) * ln(V));
if counts[i,j] <> groupsize then equal_grp := false;
end;
AReport.Add('Cell %3d %3d %3d %8.3f %8.3f %8.3f', [minf1+i, minf2+j, counts[i,j], XBar, V, S]);
lReport.Add('Cell %3d %3d %3d %8.3f %8.3f %8.3f', [minf1+i, minf2+j, counts[i,j], XBar, V, S]);
end;
end;
@ -1122,31 +1161,31 @@ begin
for i := 0 to NoGrpsA-1 do
begin
XBar := RowSums[i] / RowCount[i];
OrdMeansA[i] := XBar;
// OrdMeansA[i] := XBar;
RowSS := 0.0;
for j := 0 to NoGrpsB-1 do RowSS := RowSS + vars[i,j];
V := RowSS - (RowSums[i] * RowSums[i] / RowCount[i]);
V := V / (RowCount[i] - 1.0);
S := sqrt(V);
AReport.Add('Row %3d %3d %8.3f %8.3f %8.3f', [minf1+i, RowCount[i], XBar, V, S]);
lReport.Add('Row %3d %3d %8.3f %8.3f %8.3f', [minf1+i, RowCount[i], XBar, V, S]);
end;
//Display means, variances and standard deviations for columns
for j := 0 to NoGrpsB-1 do
begin
XBar := ColSums[j] / ColCount[j];
OrdMeansB[j] := XBar;
// OrdMeansB[j] := XBar;
ColSS := 0.0;
for i := 0 to NoGrpsA-1 do ColSS := ColSS + vars[i,j];
if ColCount[j] > 0 then V := ColSS - (ColSums[j] * ColSums[j] / ColCount[j]);
if ColCount[j] > 1 then V := V / (ColCount[j] - 1.0);
if V > 0.0 then S := sqrt(V);
AReport.Add('Col %3d %3d %8.3f %8.3f %8.3f', [minf2+j, ColCount[j], XBar, V, S]);
lReport.Add('Col %3d %3d %8.3f %8.3f %8.3f', [minf2+j, ColCount[j], XBar, V, S]);
end;
AReport.Add('TOTAL %3d %8.3f %8.3f %8.3f', [N, MeanDep, MSDep, sqrt(MSDep)]);
AReport.Add('');
AReport.Add('');
lReport.Add('TOTAL %3d %8.3f %8.3f %8.3f', [N, MeanDep, MSDep, sqrt(MSDep)]);
lReport.Add('');
lReport.Add('');
c := 1.0 + (1.0 / (3.0 * NoGrpsA * NoGrpsB - 1.0)) * (sumDFrecip - (1.0 / DFErr));
bartlett := (2.303 / c) * ((DFErr * ln(MSErr)) - sumfreqlogvar);
@ -1154,15 +1193,185 @@ begin
cochran := maxvar / sumvars;
hartley := maxvar / minvar;
AReport.Add(DIVIDER);
AReport.Add('TESTS FOR HOMOGENEITY OF VARIANCE');
AReport.Add(DIVIDER_SMALL);
AReport.Add('Hartley Fmax test statistic: %.2f with deg.s freedom: %d and %d.', [hartley, NoGrpsA*NoGrpsB, groupsize-1]);
AReport.Add('Cochran C statistic: %.2f with deg.s freedom: %d and %d.', [cochran, NoGrpsA*NoGrpsB, groupsize - 1]);
AReport.Add('Bartlett Chi-square statistic: %.2f with %d D.F.; prob. larger value %.3f', [bartlett, NoGrpsA*NoGrpsB - 1, chiprob]);
AReport.Add(DIVIDER);
lReport.Add(DIVIDER);
lReport.Add('TESTS FOR HOMOGENEITY OF VARIANCE');
lReport.Add(DIVIDER_SMALL);
lReport.Add('Hartley Fmax test statistic: %.2f with deg.s freedom: %d and %d.', [hartley, NoGrpsA*NoGrpsB, groupsize-1]);
lReport.Add('Cochran C statistic: %.2f with deg.s freedom: %d and %d.', [cochran, NoGrpsA*NoGrpsB, groupsize - 1]);
lReport.Add('Bartlett Chi-square statistic: %.2f with %d D.F.; prob. larger value %.3f', [bartlett, NoGrpsA*NoGrpsB - 1, chiprob]);
lReport.Add(DIVIDER);
FReportFrame.DisplayReport(lReport);
finally
lReport.Free;
end;
end;
procedure TBlksAnovaForm.PopulateChartCombobox(ThreeWay: Boolean);
var
a, b, c: String;
i, idx: Integer;
begin
a := Factor1Edit.Text;
b := Factor2Edit.Text;
c := Factor3Edit.Text;
idx := FChartCombobox.ItemIndex;
FChartCombobox.Items.Clear;
FChartCombobox.Items.Add(a);
FChartCombobox.Items.Add(b);
if ThreeWay then
FChartCombobox.Items.Add(c);
{
if InteractChk.Checked then
begin
}
if ThreeWay then
begin
for i := 0 to NF3Cells-1 do
FChartCombobox.Items.Add(Format('%s * %s vs %s with %s=%d', [a, b, a, c, Round(MinF3) + i]));
for i := 0 to NF3Cells-1 do
FChartCombobox.Items.Add(Format('%s * %s vs %s with %s=%d', [a, b, b, c, Round(MinF3) + i]));
for i := 0 to NF2Cells-1 do
FChartCombobox.Items.Add(Format('%s * %s vs %s with %s=%d', [a, c, a, b, Round(MinF2) + i]));
for i := 0 to NF2Cells-1 do
FChartCombobox.Items.Add(Format('%s * %s vs %s with %s=%d', [a, c, c, b, Round(MinF2) + i]));
for i := 0 to NF1Cells-1 do
FChartCombobox.Items.Add(Format('%s * %s vs %s with %s=%d', [b, c, b, a, Round(MinF1) + i]));
for i := 0 to NF1Cells-1 do
FChartCombobox.Items.Add(Format('%s * %s vs %s with %s=%d', [b, c, c, a, Round(MinF1) + i]));
end else
begin
FChartCombobox.Items.Add(Format('%s * %s vs %s', [a, b, a]));
FChartCombobox.Items.Add(Format('%s * %s vs %s', [a, b, b]));
end;
{
end;
}
FChartComboBox.ItemIndex := EnsureRange(idx, 0, FChartComboBox.Items.Count-1);
end;
procedure TBlksAnovaForm.SelectTwoWayPlot(Sender: TObject);
var
i, j, idx: Integer;
item: PChartDataItem;
begin
FStyles.Styles.Clear;
FSeries.Clear;
case FChartCombobox.ItemIndex of
0: begin // Plot means vs factor A
FSeries.ListSource.YCount := 1;
for i := 0 to NF1Cells-1 do
FSeries.AddXY(minF1 + i, RowSums[i] / RowCount[i], IntToStr(MinF1 + i));
FChartFrame.SetXTitle(Factor1Edit.Text + ' codes');
FChartFrame.SetTitle(Factor1Edit.Text);
end;
1: begin // Plot means vs factor B
FSeries.ListSource.YCount := 1;
for j := 0 to NF2Cells-1 do
FSeries.AddXY(minF2 + j, ColSums[j] / ColCount[j], IntToStr(MinF2 + j));
FChartFrame.SetXTitle(Factor2Edit.Text + ' codes');
FChartFrame.SetTitle(Factor2Edit.Text);
end;
2: begin // Plot interaction A*B vs A
FSeries.ListSource.YCount := NF2Cells;
for i := 0 to NF1Cells-1 do
begin
idx := FSeries.AddXY(minF1 + i, NaN, IntToStr(minF1 + i));
item := FSeries.Source.Item[idx];
for j := 0 to NF2Cells-1 do
item^.SetY(j, sums[i, j] / counts[i, j]);
end;
FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"', [Factor1Edit.Text, Factor2Edit.Text]));
FChartFrame.SetXTitle(Factor1Edit.Text + ' codes');
for j := 0 to NF2cells-1 do
with TChartStyle(FStyles.Styles.Add) do
begin
Brush.Color := DATA_COLORS[j mod Length(DATA_COLORS)];
UseBrush := true;
Text := Format('%s %s', [Factor2Edit.Text, IntToStr(MinF2 + j)]);
end;
end;
3: begin // Plot means vs interaction A*B vs B
FSeries.ListSource.YCount := NF1Cells;
for j := 0 to NF2Cells-1 do
begin
idx := FSeries.AddXY(minF2 + j, NaN, IntToStr(minF2 + j));
item := FSeries.Source.Item[idx];
for i := 0 to NF1Cells-1 do
item^.SetY(i, sums[i, j] / counts[i, j]);
end;
FChartFrame.SetTitle(Format('Factor "%s" x Factor "5s"', [Factor1Edit.Text, Factor2Edit.Text]));
FChartFrame.SetXTitle(Factor2Edit.Text + ' codes');
for i := 0 to NF1Cells-1 do
with TChartStyle(FStyles.styles.Add) do
begin
Brush.Color := DATA_COLORS[i mod Length(DATA_COLORS)];
UseBrush := True;
Text := Format('%s %s', [Factor1Edit.Text, IntToStr(MinF1 + i)]);
end;
end;
end; // case
if (FSeries is TBarSeries) then
begin
if FStyles.Styles.Count > 0 then
begin
TBarSeries(FSeries).Styles := FStyles;
FSeries.Legend.Multiplicity := lmStyle;
end else
FSeries.Legend.Multiplicity := lmSingle;
end;
FChartFrame.Chart.BottomAxis.Marks.Source := FSeries.Source;
FChartFrame.Chart.BottomAxis.Marks.Style := smsLabel;
FChartFrame.Chart.Legend.Visible := FSeries.Source.YCount > 1;
FChartFrame.UpdateBtnStates;
end;
procedure TBlksAnovaForm.TwoWayPlot;
begin
{
if not ShowPlotsChk.Checked then
begin
ChartPage.TabVisible := false;
exit;
end;
}
FChartFrame.Clear;
FChartFrame.SetYTitle('Mean');
FSeries := FChartFrame.PlotXY(ptBars, nil, nil, nil, nil, '', DATA_COLORS[0]);
with TBarSeries(FSeries) do
begin
Stacked := false;
{$IF LCL_FullVersion >= 2010000}
DepthBrightnessDelta := -30;
{$ENDIF}
end;
{
if Plot3DChk.Checked then
FSeries.Depth := 20;
}
FChartCombobox.Parent.Visible := true;
PopulateChartCombobox(false);
FChartCombobox.OnChange := @SelectTwoWayPlot;
SelectTwoWayPlot(nil);
{
ChartPage.TabVisible := true;
}
end;
(*
procedure TBlksAnovaForm.TwoWayPlot;
var
i, j : integer;
@ -1275,6 +1484,7 @@ begin
GraphFrm.Xpoints := nil;
GraphFrm.Ypoints := nil;
end;
*)
function TBlksAnovaForm.Calc3Way: Boolean;
var
@ -1865,7 +2075,7 @@ begin
for i := 0 to NoGrpsA-1 do
begin
XBar := RowSums[i] / RowCount[i];
OrdMeansA[i] := XBar;
// OrdMeansA[i] := XBar;
RowSS := 0.0;
for j := 0 to NoGrpsB-1 do
for k := 0 to NoGrpsC-1 do RowSS := RowSS + wx2[i,j,k];
@ -1879,7 +2089,7 @@ begin
for j := 0 to NoGrpsB-1 do
begin
XBar := ColSums[j] / ColCount[j];
OrdMeansB[j] := XBar;
// OrdMeansB[j] := XBar;
ColSS := 0.0;
for i := 0 to NoGrpsA-1 do
for k := 0 to NoGrpsC-1 do ColSS := ColSS + wx2[i,j,k];
@ -1893,7 +2103,7 @@ begin
for k := 0 to NoGrpsC-1 do
begin
XBar := SlcSums[k] / SlcCount[k];
OrdMeansC[k] := XBar;
// OrdMeansC[k] := XBar;
SlcSS := 0.0;
for i := 0 to NoGrpsA-1 do
for j := 0 to NoGrpsB-1 do SlcSS := SlcSS + wx2[i,j,k];
@ -2137,20 +2347,31 @@ begin
end;
procedure TBlksAnovaForm.TwoWayContrasts(AReport: TStrings);
procedure TBlksAnovaForm.TwoWayContrasts;
var
lReport: TStrings;
i, j: integer;
value: double;
variances: DblDyneVec = nil;
RowSS, ColSS: double;
totalCells: Integer;
begin
SetLength(variances, totcells);
if not (ScheffeChk.Checked or TukeyHSDChk.Checked or TukeyBChk.Checked or
TukeyKramerChk.Checked or NewmanKeulsChk.Checked or
BonferroniChk.Checked or OrthoContrastsChk.Checked) then
begin
PostHocPage.TabVisible := false;
exit;
end;
totalCells := NF1Cells + NF2Cells + NF3Cells;
SetLength(variances, totalCells);
lReport := TStringList.Create;
try
// Do row comparisons
if (NF1cells > 2) then
if ProbF1 < allAlpha then
if Fact2Combo.ItemIndex = 0 then
if (NF1cells > 2) and (ProbF1 < allAlpha) and (Fact2Combo.ItemIndex = 0) then
begin
for i := 0 to NoGrpsA-1 do
begin
@ -2160,133 +2381,178 @@ begin
variances[i] := variances[i] / (RowCount[i] - 1.0);
end;
AReport.Add('');
AReport.Add('COMPARISONS AMONG ROWS');
lReport.Add('COMPARISONS AMONG ROWS');
// get smallest group size
// Get smallest group size
value := 1e308;
for i := 0 to NF1cells-1 do if RowCount[i] < value then value := RowCount[i];
for i := 0 to NF1Cells-1 do
if RowCount[i] < value then value := RowCount[i];
if ScheffeChk.Checked then
ScheffeTest(MSErr, RowSums, RowCount, minf1, maxf1, N, posthocAlpha, AReport);
ScheffeTest(MSErr, RowSums, RowCount, minf1, maxf1, N, posthocAlpha, lReport);
if TukeyHSDChk.Checked and equal_grp then
Tukey(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, AReport);
Tukey(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, lReport);
if TukeyBChk.Checked and equal_grp then
TukeyBTest(MSErr, DFErr, RowSums, RowCount, minf1, maxf1, value, posthocAlpha, AReport);
TukeyBTest(MSErr, DFErr, RowSums, RowCount, minf1, maxf1, value, posthocAlpha, lReport);
if TukeyKramerChk.Checked and equal_grp then
Tukey_Kramer(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, AReport);
Tukey_Kramer(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, lReport);
if NewmanKeulsChk.Checked and equal_grp then
Newman_Keuls(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, AReport);
Newman_Keuls(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, lReport);
if BonferroniChk.Checked then
Bonferroni(RowSums, RowCount, variances, minf1, maxf1, posthocAlpha, AReport);
Bonferroni(RowSums, RowCount, variances, minf1, maxf1, posthocAlpha, lReport);
if OrthoContrastsChk.Checked then
Contrasts(MSErr, DFErr, RowSums, RowCount, minf1, maxf1, AllAlpha, posthocAlpha, AReport);
Contrasts(MSErr, DFErr, RowSums, RowCount, minf1, maxf1, AllAlpha, posthocAlpha, lReport);
end;
// Do column comparisons
if (NF2cells > 2) and (ProbF2 < allAlpha) and (Fact2Combo.ItemIndex = 0) then
if (NF2Cells > 2) and (ProbF2 < allAlpha) and (Fact2Combo.ItemIndex = 0) then
begin
for j := 0 to NoGrpsB-1 do
begin
ColSS := 0.0;
for i := 0 to NoGrpsA-1 do ColSS := ColSS + vars[i,j];
for i := 0 to NoGrpsA-1 do
ColSS := ColSS + vars[i,j];
variances[j] := ColSS - (ColSums[j] * ColSums[j] / ColCount[j]);
variances[j] := variances[j] / (ColCount[j] - 1.0);
end;
AReport.Add('');
AReport.Add('COMPARISONS AMONG COLUMNS');
value := 1e20;
if lReport.Count <> 0 then
lReport.Add('');
lReport.Add('COMPARISONS AMONG COLUMNS');
value := 1e308;
for i := 0 to NF2cells-1 do
if ColCount[i] < value then value := ColCount[i];
if ScheffeChk.Checked then
ScheffeTest(MSErr, ColSums, ColCount, minf2, maxf2, N, posthocAlpha, AReport);
ScheffeTest(MSErr, ColSums, ColCount, minf2, maxf2, N, posthocAlpha, lReport);
if TukeyHSDChk.Checked and equal_grp then
Tukey(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, AReport);
Tukey(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, lReport);
if TukeyBChk.Checked and equal_grp then
TukeyBTest(MSErr, DFErr, ColSums, ColCount, minf2, maxf2, value, posthocAlpha, AReport);
TukeyBTest(MSErr, DFErr, ColSums, ColCount, minf2, maxf2, value, posthocAlpha, lReport);
if TukeyKramerChk.Checked and equal_grp then
Tukey_Kramer(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, AReport);
Tukey_Kramer(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, lReport);
if NewmanKeulsChk.Checked and equal_grp then
Newman_Keuls(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, AReport);
Newman_Keuls(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, lReport);
if BonferroniChk.Checked then
Bonferroni(ColSums, ColCount, variances, minf2, maxf2, posthocAlpha, AReport);
Bonferroni(ColSums, ColCount, variances, minf2, maxf2, posthocAlpha, lReport);
if OrthoContrastsChk.Checked then
Contrasts(MSErr, DFErr, ColSums, ColCount, minf2, maxf2, AllAlpha, postHocAlpha, AReport);
Contrasts(MSErr, DFErr, ColSums, ColCount, minf2, maxf2, AllAlpha, postHocAlpha, lReport);
end;
// do simple effects for columns within each row
if (ProbF3 < allAlpha) and (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) then
begin
AReport.Add('');
AReport.Add('COMPARISONS AMONG COLUMNS WITHIN EACH ROW');
for i := 0 to NF1cells-1 do
if lReport.Count <> 0 then
lReport.Add('');
lReport.Add('COMPARISONS AMONG COLUMNS WITHIN EACH ROW');
for i := 0 to NF1Cells-1 do
begin
AReport.Add('');
AReport.Add('ROW %d COMPARISONS',[i+1]);
lReport.Add('');
lReport.Add('ROW %d COMPARISONS',[i+1]);
// move cell sums and counts to cellsums and cellcnts
for j := 0 to NF2cells-1 do
for j := 0 to NF2Cells-1 do
begin
cellsums[j] := sums[i,j];
cellcnts[j] := counts[i,j];
cellvars[j] := vars[i,j];
cellSums[j] := Sums[i,j];
cellCnts[j] := Counts[i,j];
cellVars[j] := Vars[i,j];
end;
value := 1e308;
for j := 0 to NF2cells-1 do
if cellcnts[j] < value then value := cellcnts[j];
if cellCnts[j] < value then value := cellCnts[j];
if ScheffeChk.Checked then
ScheffeTest(MSErr, cellsums, cellcnts, minf2, maxf2, N, posthocAlpha, AReport);
ScheffeTest(MSErr, cellSums, cellCnts, MinF2, MaxF2, N, posthocAlpha, lReport);
if TukeyHSDChk.Checked and equal_grp then
Tukey(MSErr, DFErr, value, cellsums, cellcnts, minf2, maxf2, posthocAlpha, AReport);
Tukey(MSErr, DFErr, value, cellSums, cellCnts, MinF2, MaxF2, posthocAlpha, lReport);
if TukeyBChk.Checked and equal_grp then
TukeyBTest(MSErr, DFErr, cellsums, cellcnts, minf2, maxf2, value, posthocAlpha, AReport);
TukeyBTest(MSErr, DFErr, cellSums, cellCnts, MinF2, MaxF2, value, posthocAlpha, lReport);
if TukeyKramerChk.Checked and equal_grp then
Tukey_Kramer(MSErr, DFErr, value, cellsums, cellcnts, minf2, maxf2, posthocAlpha, AReport);
Tukey_Kramer(MSErr, DFErr, value, cellSums, cellCnts, MinF2, MaxF2, posthocAlpha, lReport);
if NewmanKeulsChk.Checked and equal_grp then
Newman_Keuls(MSErr, DFErr, value, cellsums, cellcnts, minf2, maxf2, posthocAlpha, AReport);
Newman_Keuls(MSErr, DFErr, value, cellSums, cellCnts, MinF2, MaxF2, posthocAlpha, lReport);
if BonferroniChk.Checked then
Bonferroni(cellsums, cellcnts, cellvars, minf2, maxf2, posthocAlpha, AReport);
Bonferroni(cellSums, cellCnts, cellVars, MinF2, MaxF2, posthocAlpha, lReport);
if OrthoContrastsChk.Checked then
Contrasts(MSErr, DFErr, cellsums, cellcnts, minf2, maxf2, allAlpha, PostHocAlpha, AReport);
Contrasts(MSErr, DFErr, cellSums, cellCnts, MinF2, MaxF2, allAlpha, PostHocAlpha, lReport);
end;
end;
// do simple effects for rows within each column
if (ProbF3 < allAlpha) and (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) then
begin
AReport.Add('');
AReport.Add('COMPARISONS AMONG ROWS WITHIN EACH COLUMN');
for j := 0 to NF2cells-1 do
if lReport.Count > 0 then
lReport.Add('');
lReport.Add('COMPARISONS AMONG ROWS WITHIN EACH COLUMN');
for j := 0 to NF2Cells-1 do
begin
AReport.Add('');
AReport.Add('COLUMN %d COMPARISONS', [j+1]);
lReport.Add('');
lReport.Add('COLUMN %d COMPARISONS', [j+1]);
// move cell sums and counts to cellsums and cellcnts
for i := 0 to NF1cells-1 do
for i := 0 to NF1Cells-1 do
begin
cellsums[i] := sums[i,j];
cellcnts[i] := counts[i,j];
cellvars[i] := vars[i,j];
cellSums[i] := Sums[i,j];
cellCnts[i] := Counts[i,j];
cellVars[i] := Vars[i,j];
end;
value := 1e308;
for i := 0 to NF1cells-1 do
if cellcnts[j] < value then value := cellcnts[j];
for i := 0 to NF1Cells-1 do
if cellCnts[j] < value then value := cellCnts[j];
if ScheffeChk.Checked then
ScheffeTest(MSErr, cellsums, cellcnts, minf1, maxf1, N, posthocAlpha, AReport);
ScheffeTest(MSErr, cellSums, cellCnts, MinF1, MaxF1, N, posthocAlpha, lReport);
if TukeyHSDChk.Checked and equal_grp then
Tukey(MSErr, DFErr, value, cellsums, cellcnts, minf1, maxf1, posthocAlpha, AReport);
Tukey(MSErr, DFErr, value, cellSums, cellCnts, MinF1, MaxF1, posthocAlpha, lReport);
if TukeyBChk.Checked and equal_grp then
TukeyBTest(MSErr, DFErr, cellsums, cellcnts, minf1, maxf1, value, posthocAlpha, AReport);
TukeyBTest(MSErr, DFErr, cellSums, cellCnts, MinF1, MaxF1, value, posthocAlpha, lReport);
if TukeyKramerChk.Checked and equal_grp then
Tukey_Kramer(MSErr, DFErr, value, cellsums, cellcnts, minf1, maxf1, posthocAlpha, AReport);
Tukey_Kramer(MSErr, DFErr, value, cellSums, cellCnts, MinF1, MaxF1, posthocAlpha, lReport);
if NewmanKeulsChk.Checked and equal_grp then
Newman_Keuls(MSErr, DFErr, value, cellsums, cellcnts, minf1, maxf1, posthocAlpha, AReport);
Newman_Keuls(MSErr, DFErr, value, cellSums, cellCnts, MinF1, MaxF1, posthocAlpha, lReport);
if BonferroniChk.Checked then
Bonferroni(cellsums, cellcnts, cellvars, minf1, maxf1, posthocAlpha, AReport);
Bonferroni(cellSums, cellCnts, cellVars, MinF1, MaxF1, posthocAlpha, lReport);
if OrthoContrastsChk.Checked then
Contrasts(MSErr, DFErr, cellsums, cellcnts, minf1, maxf1, allAlpha, postHocAlpha, AReport);
Contrasts(MSErr, DFErr, cellSums, cellCnts, MinF1, MaxF1, allAlpha, postHocAlpha, lReport);
end;
end;
variances := nil;
FPosthocReportFrame.DisplayReport(lReport);
finally
lReport.Free;
end;
PostHocPage.TabVisible := true;
end;
procedure TBlksAnovaForm.ThreeWayContrasts(AReport: TStrings);
var
i, j, k: integer;

View File

@ -438,9 +438,9 @@ begin
SetLength(ColSums, NF2Cells); // 2-way column sums
SetLength(RowCount, NF1Cells); // 2-way row counts
SetLength(ColCount, NF2Cells); // 2-way column counts
SetLength(counts, Nf1cells, Nf2cells); // matrix for 2-way containing cell sizes
SetLength(sums, Nf1cells, Nf2cells); // matrix for 2-way containing cell sums
SetLength(vars, Nf1cells, Nf2cells); // matrix for 2-way containing sums of squares
SetLength(counts, NF1Cells, NF2Cells); // matrix for 2-way containing cell sizes
SetLength(sums, NF1Cells, NF2Cells); // matrix for 2-way containing cell sums
SetLength(vars, NF1Cells, NF2Cells); // matrix for 2-way containing sums of squares
end;
@ -519,9 +519,9 @@ begin
else
Constant := 0.0;
SSDep := SSDep - Constant;
SSF1 := SSF1 - Constant;
SSF2 := SSF2 - Constant;
SSDep := SSDep - Constant;
SSErr := SSDep - (SSF1 + SSF2);
SSNonAdd := (SSNonAdd * SSNonAdd) / ((SSF1 * SSF2) / (NoGrpsA * NoGrpsB) );
MSNonAdd := SSNonAdd;
@ -538,14 +538,17 @@ begin
DFF2 := NoGrpsB - 1;
DFErr := DFF1 * DFF2;
DFBalance := DFErr - 1;
MSF1 := SSF1 / DFF1;
MSF2 := SSF2 / DFF2;
MSErr := SSErr / DFErr;
MSDep := SSDep / DFTot;
MSBalance := SSBalance / DFBalance;
OmegaF1 := (SSF1 - DFF1 * MSErr) / (SSDep + MSErr);
OmegaF2 := (SSF2 - DFF2 * MSErr) / (SSDep + MSErr);
Omega := OmegaF1 + OmegaF2;
MeanDep := MeanDep / N;
// F tests for fixed effects