From bc5987793a3bd8a6087e98b4cf3e0dc9cc3460c2 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 9 Nov 2020 21:52:26 +0000 Subject: [PATCH] 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 --- .../analysis/comparisons/blkanovaunit.pas | 950 +++++++++++------- .../analysis/comparisons/onecaseanovaunit.pas | 11 +- 2 files changed, 615 insertions(+), 346 deletions(-) diff --git a/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.pas b/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.pas index b46441dc9..f4afe5852 100644 --- a/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.pas +++ b/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.pas @@ -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; - OneWayTable; - OneWayPostHoc; - OneWayPlot; - - exit; + if Calc1Way(DepValues, F1Values) then + begin + OneWayTable; + OneWayPostHoc; + OneWayPlot; + 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,16 +356,16 @@ 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); - TwoWayPlot; +// 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 @@ -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; + i, j: integer; + grpA, grpB: integer; + 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,140 +1070,308 @@ 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(''); + 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 + lReport.Add('Note: Denominator of F ratio is MSErr'); + if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 1) then + lReport.Add('Note: Denominator of F ratio is MSAxB'); + if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 1) then + begin + 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 + lReport.Add('Note: Denominator of F ratio for B is MSAxB'); + lReport.Add('and denominator for A and AxB is MSErr'); + end; + 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; + MinVar := 1e20; + sumvars := 0.0; + sumfreqlogvar := 0.0; + sumDFrecip := 0.0; - 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(''); - if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) then - AReport.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'); - 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'); - 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'); - end; - AReport.Add(''); - AReport.Add('Descriptive Statistics'); - AReport.Add(''); - AReport.Add('GROUP Row Col. N MEAN VARIANCE STD.DEV.'); - groupsize := counts[0, 0]; - equal_grp := true; - MaxVar := 0.0; - MinVar := 1e20; - sumvars := 0.0; - sumfreqlogvar := 0.0; - sumDFrecip := 0.0; + // Display cell means, variances, standard deviations + V := 0.0; + XBar := 0.0; + S := 0.0; + for i := 0 to NoGrpsA-1 do + begin + for j := 0 to NoGrpsB-1 do + begin + if counts[i,j] > 1 then + begin + XBar := sums[i,j] / counts[i,j]; + V := vars[i,j] - ( (sums[i,j] * sums[i,j]) / counts[i,j]); + V := V / (counts[i,j] - 1.0); + S := sqrt(V); + sumvars := sumvars + V; + if V > MaxVar then MaxVar := V; + if V < MinVar then MinVar := V; + sumDFrecip := sumDFrecip + (1.0 / (counts[i,j] - 1.0)); + sumfreqlogvar := sumfreqlogvar + ((counts[i,j] - 1.0) * ln(V)); + if counts[i,j] <> groupsize then equal_grp := false; + end; + lReport.Add('Cell %3d %3d %3d %8.3f %8.3f %8.3f', [minf1+i, minf2+j, counts[i,j], XBar, V, S]); + end; + end; - // Display cell means, variances, standard deviations - V := 0.0; - XBar := 0.0; - S := 0.0; - for i := 0 to NoGrpsA-1 do - begin + //Display Row means, variances, standard deviations + for i := 0 to NoGrpsA-1 do + begin + XBar := RowSums[i] / RowCount[i]; +// 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); + 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 - if counts[i,j] > 1 then - begin - XBar := sums[i,j] / counts[i,j]; - V := vars[i,j] - ( (sums[i,j] * sums[i,j]) / counts[i,j]); - V := V / (counts[i,j] - 1.0); - S := sqrt(V); - sumvars := sumvars + V; - if V > MaxVar then MaxVar := V; - if V < MinVar then MinVar := V; - sumDFrecip := sumDFrecip + (1.0 / (counts[i,j] - 1.0)); - 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]); + XBar := ColSums[j] / ColCount[j]; +// 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); + lReport.Add('Col %3d %3d %8.3f %8.3f %8.3f', [minf2+j, ColCount[j], XBar, V, S]); end; + + 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); + chiprob := 1.0 - chisquaredprob(bartlett,round(NoGrpsA * NoGrpsB - 1)); + cochran := maxvar / sumvars; + hartley := maxvar / minvar; + + 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; - - //Display Row means, variances, standard deviations - for i := 0 to NoGrpsA-1 do - begin - XBar := RowSums[i] / RowCount[i]; - 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]); - 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; - 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]); - end; - - AReport.Add('TOTAL %3d %8.3f %8.3f %8.3f', [N, MeanDep, MSDep, sqrt(MSDep)]); - AReport.Add(''); - AReport.Add(''); - - c := 1.0 + (1.0 / (3.0 * NoGrpsA * NoGrpsB - 1.0)) * (sumDFrecip - (1.0 / DFErr)); - bartlett := (2.303 / c) * ((DFErr * ln(MSErr)) - sumfreqlogvar); - chiprob := 1.0 - chisquaredprob(bartlett,round(NoGrpsA * NoGrpsB - 1)); - 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); 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,156 +2347,212 @@ begin end; -procedure TBlksAnovaForm.TwoWayContrasts(AReport: TStrings); +procedure TBlksAnovaForm.TwoWayContrasts; var - i, j : integer; - value : double; - variances : DblDyneVec = nil; - RowSS, ColSS : double; + 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; - // Do row comparisons - if (NF1cells > 2) then - if ProbF1 < allAlpha then - if Fact2Combo.ItemIndex = 0 then + totalCells := NF1Cells + NF2Cells + NF3Cells; + SetLength(variances, totalCells); + + lReport := TStringList.Create; + try + // Do row comparisons + if (NF1cells > 2) and (ProbF1 < allAlpha) and (Fact2Combo.ItemIndex = 0) then + begin + for i := 0 to NoGrpsA-1 do begin + RowSS := 0.0; + for j := 0 to NoGrpsB-1 do RowSS := RowSS + vars[i,j]; + variances[i] := RowSS - (RowSums[i] * RowSums[i] / RowCount[i]); + variances[i] := variances[i] / (RowCount[i] - 1.0); + end; + + lReport.Add('COMPARISONS AMONG ROWS'); + + // Get smallest group size + value := 1e308; + 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, lReport); + + if TukeyHSDChk.Checked and equal_grp then + 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, lReport); + + if TukeyKramerChk.Checked and equal_grp then + 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, lReport); + + if BonferroniChk.Checked then + Bonferroni(RowSums, RowCount, variances, minf1, maxf1, posthocAlpha, lReport); + + if OrthoContrastsChk.Checked then + 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 + begin + for j := 0 to NoGrpsB-1 do + begin + ColSS := 0.0; 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; + + 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, lReport); + + if TukeyHSDChk.Checked and equal_grp then + 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, lReport); + + if TukeyKramerChk.Checked and equal_grp then + 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, lReport); + + if BonferroniChk.Checked then + Bonferroni(ColSums, ColCount, variances, minf2, maxf2, posthocAlpha, lReport); + + if OrthoContrastsChk.Checked then + 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 + if lReport.Count <> 0 then + lReport.Add(''); + lReport.Add('COMPARISONS AMONG COLUMNS WITHIN EACH ROW'); + for i := 0 to NF1Cells-1 do + begin + 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 begin - RowSS := 0.0; - for j := 0 to NoGrpsB-1 do RowSS := RowSS + vars[i,j]; - variances[i] := RowSS - (RowSums[i] * RowSums[i] / RowCount[i]); - variances[i] := variances[i] / (RowCount[i] - 1.0); + cellSums[j] := Sums[i,j]; + cellCnts[j] := Counts[i,j]; + cellVars[j] := Vars[i,j]; end; - AReport.Add(''); - AReport.Add('COMPARISONS AMONG ROWS'); - - // get smallest group size value := 1e308; - 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); - if TukeyHSDChk.Checked and equal_grp then - Tukey(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, AReport); - if TukeyBChk.Checked and equal_grp then - TukeyBTest(MSErr, DFErr, RowSums, RowCount, minf1, maxf1, value, posthocAlpha, AReport); - if TukeyKramerChk.Checked and equal_grp then - Tukey_Kramer(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, AReport); - if NewmanKeulsChk.Checked and equal_grp then - Newman_Keuls(MSErr, DFErr, value, RowSums, RowCount, minf1, maxf1, posthocAlpha, AReport); - if BonferroniChk.Checked then - Bonferroni(RowSums, RowCount, variances, minf1, maxf1, posthocAlpha, AReport); - if OrthoContrastsChk.Checked then - Contrasts(MSErr, DFErr, RowSums, RowCount, minf1, maxf1, AllAlpha, posthocAlpha, AReport); - end; - - // Do column comparisons - 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]; - 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; - 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); - if TukeyHSDChk.Checked and equal_grp then - Tukey(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, AReport); - if TukeyBChk.Checked and equal_grp then - TukeyBTest(MSErr, DFErr, ColSums, ColCount, minf2, maxf2, value, posthocAlpha, AReport); - if TukeyKramerChk.Checked and equal_grp then - Tukey_Kramer(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, AReport); - if NewmanKeulsChk.Checked and equal_grp then - Newman_Keuls(MSErr, DFErr, value, ColSums, ColCount, minf2, maxf2, posthocAlpha, AReport); - if BonferroniChk.Checked then - Bonferroni(ColSums, ColCount, variances, minf2, maxf2, posthocAlpha, AReport); - if OrthoContrastsChk.Checked then - Contrasts(MSErr, DFErr, ColSums, ColCount, minf2, maxf2, AllAlpha, postHocAlpha, AReport); - 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 - begin - AReport.Add(''); - AReport.Add('ROW %d COMPARISONS',[i+1]); - // move cell sums and counts to cellsums and cellcnts - for j := 0 to NF2cells-1 do - begin - 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 ScheffeChk.Checked then - ScheffeTest(MSErr, cellsums, cellcnts, minf2, maxf2, N, posthocAlpha, AReport); - if TukeyHSDChk.Checked and equal_grp then - Tukey(MSErr, DFErr, value, cellsums, cellcnts, minf2, maxf2, posthocAlpha, AReport); - if TukeyBChk.Checked and equal_grp then - TukeyBTest(MSErr, DFErr, cellsums, cellcnts, minf2, maxf2, value, posthocAlpha, AReport); - if TukeyKramerChk.Checked and equal_grp then - Tukey_Kramer(MSErr, DFErr, value, cellsums, cellcnts, minf2, maxf2, posthocAlpha, AReport); - if NewmanKeulsChk.Checked and equal_grp then - Newman_Keuls(MSErr, DFErr, value, cellsums, cellcnts, minf2, maxf2, posthocAlpha, AReport); - if BonferroniChk.Checked then - Bonferroni(cellsums, cellcnts, cellvars, minf2, maxf2, posthocAlpha, AReport); - if OrthoContrastsChk.Checked then - Contrasts(MSErr, DFErr, cellsums, cellcnts, minf2, maxf2, allAlpha, PostHocAlpha, AReport); - 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 - begin - AReport.Add(''); - AReport.Add('COLUMN %d COMPARISONS', [j+1]); - // move cell sums and counts to cellsums and cellcnts - for i := 0 to NF1cells-1 do - begin - 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]; - if ScheffeChk.Checked then - ScheffeTest(MSErr, cellsums, cellcnts, minf1, maxf1, N, posthocAlpha, AReport); - if TukeyHSDChk.Checked and equal_grp then - Tukey(MSErr, DFErr, value, cellsums, cellcnts, minf1, maxf1, posthocAlpha, AReport); - if TukeyBChk.Checked and equal_grp then - TukeyBTest(MSErr, DFErr, cellsums, cellcnts, minf1, maxf1, value, posthocAlpha, AReport); - if TukeyKramerChk.Checked and equal_grp then - Tukey_Kramer(MSErr, DFErr, value, cellsums, cellcnts, minf1, maxf1, posthocAlpha, AReport); - if NewmanKeulsChk.Checked and equal_grp then - Newman_Keuls(MSErr, DFErr, value, cellsums, cellcnts, minf1, maxf1, posthocAlpha, AReport); - if BonferroniChk.Checked then - Bonferroni(cellsums, cellcnts, cellvars, minf1, maxf1, posthocAlpha, AReport); - if OrthoContrastsChk.Checked then - Contrasts(MSErr, DFErr, cellsums, cellcnts, minf1, maxf1, allAlpha, postHocAlpha, AReport); - end; + if cellCnts[j] < value then value := cellCnts[j]; + + if ScheffeChk.Checked then + 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, lReport); + + if TukeyBChk.Checked and equal_grp then + 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, lReport); + + if NewmanKeulsChk.Checked and equal_grp then + Newman_Keuls(MSErr, DFErr, value, cellSums, cellCnts, MinF2, MaxF2, posthocAlpha, lReport); + + if BonferroniChk.Checked then + Bonferroni(cellSums, cellCnts, cellVars, MinF2, MaxF2, posthocAlpha, lReport); + + if OrthoContrastsChk.Checked then + Contrasts(MSErr, DFErr, cellSums, cellCnts, MinF2, MaxF2, allAlpha, PostHocAlpha, lReport); end; - variances := nil; + end; + + // do simple effects for rows within each column + if (ProbF3 < allAlpha) and (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) then + begin + if lReport.Count > 0 then + lReport.Add(''); + lReport.Add('COMPARISONS AMONG ROWS WITHIN EACH COLUMN'); + for j := 0 to NF2Cells-1 do + begin + 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 + begin + 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]; + + if ScheffeChk.Checked then + 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, lReport); + + if TukeyBChk.Checked and equal_grp then + 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, lReport); + + if NewmanKeulsChk.Checked and equal_grp then + Newman_Keuls(MSErr, DFErr, value, cellSums, cellCnts, MinF1, MaxF1, posthocAlpha, lReport); + + if BonferroniChk.Checked then + Bonferroni(cellSums, cellCnts, cellVars, MinF1, MaxF1, posthocAlpha, lReport); + + if OrthoContrastsChk.Checked then + Contrasts(MSErr, DFErr, cellSums, cellCnts, MinF1, MaxF1, allAlpha, postHocAlpha, lReport); + end; + end; + + FPosthocReportFrame.DisplayReport(lReport); + finally + lReport.Free; + end; + + PostHocPage.TabVisible := true; end; + procedure TBlksAnovaForm.ThreeWayContrasts(AReport: TStrings); var i, j, k: integer; diff --git a/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.pas b/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.pas index a0b30a3b3..51e677dfd 100644 --- a/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.pas +++ b/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.pas @@ -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