From 5dba503a4ba2d47210c9ffd81cf86edb5ca38f80 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Tue, 10 Nov 2020 16:46:13 +0000 Subject: [PATCH] LazStats: More refactoring of BlkAnovaUnit; clean-up. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7855 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../analysis/comparisons/blkanovaunit.lfm | 129 +- .../analysis/comparisons/blkanovaunit.pas | 3316 ++++++++--------- 2 files changed, 1562 insertions(+), 1883 deletions(-) diff --git a/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.lfm b/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.lfm index 83a5fcb55..75e3e6f94 100644 --- a/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.lfm +++ b/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.lfm @@ -1,37 +1,37 @@ inherited BlksAnovaForm: TBlksAnovaForm - Left = 971 - Height = 553 - Top = 314 + Left = 236 + Height = 577 + Top = 121 Width = 914 HelpType = htKeyword HelpKeyword = 'html/OneTwoorThreeWayAnalysesofVarian.htm' Caption = 'One, Two or Three Way Analysis of Variance' - ClientHeight = 553 + ClientHeight = 577 ClientWidth = 914 inherited ParamsPanel: TPanel - Height = 537 + Height = 561 Width = 400 - ClientHeight = 537 + ClientHeight = 561 ClientWidth = 400 inherited CloseBtn: TButton Left = 345 - Top = 512 + Top = 536 end inherited ComputeBtn: TButton Left = 261 - Top = 512 + Top = 536 end inherited ResetBtn: TButton Left = 199 - Top = 512 + Top = 536 end inherited HelpBtn: TButton Tag = 107 Left = 140 - Top = 512 + Top = 536 end inherited ButtonBevel: TBevel - Top = 496 + Top = 520 Width = 400 end object Label1: TLabel[5] @@ -51,7 +51,7 @@ inherited BlksAnovaForm: TBlksAnovaForm AnchorSideRight.Control = DepIn AnchorSideBottom.Control = PosthocGroup Left = 0 - Height = 302 + Height = 310 Top = 17 Width = 181 Anchors = [akTop, akLeft, akRight, akBottom] @@ -198,10 +198,10 @@ inherited BlksAnovaForm: TBlksAnovaForm Left = 221 Height = 16 Top = 89 - Width = 87 + Width = 121 AutoSize = True BorderSpacing.Bottom = 2 - Caption = 'Factor 1 Variable' + Caption = 'Factor 1 Variable (Row)' TabOrder = 13 end object Factor1Edit: TEdit[16] @@ -284,11 +284,11 @@ inherited BlksAnovaForm: TBlksAnovaForm Left = 221 Height = 16 Top = 173 - Width = 87 + Width = 141 AutoSize = True BorderSpacing.Left = 8 BorderSpacing.Bottom = 2 - Caption = 'Factor 2 Variable' + Caption = 'Factor 2 Variable (Column)' TabOrder = 17 end object Factor2Edit: TEdit[21] @@ -369,11 +369,11 @@ inherited BlksAnovaForm: TBlksAnovaForm Left = 221 Height = 16 Top = 257 - Width = 72 + Width = 107 AutoSize = True BorderSpacing.Left = 8 BorderSpacing.Bottom = 2 - Caption = 'Factor 3 Clark' + Caption = 'Factor 3 Clark (Slice)' TabOrder = 21 end object Factor3Edit: TEdit[26] @@ -431,15 +431,17 @@ inherited BlksAnovaForm: TBlksAnovaForm TabOrder = 23 Text = 'Fixed' end - object GroupBox3: TGroupBox[29] + object CorrectionsGroup: TGroupBox[29] AnchorSideLeft.Control = Bevel1 AnchorSideLeft.Side = asrBottom - AnchorSideTop.Control = PosthocGroup + AnchorSideBottom.Control = AlphaGroup Left = 207 Height = 66 - Top = 331 + Top = 339 Width = 196 + Anchors = [akLeft, akBottom] AutoSize = True + BorderSpacing.Bottom = 8 Caption = 'Corrections for unequal variances' ChildSizing.LeftRightSpacing = 12 ChildSizing.TopBottomSpacing = 2 @@ -468,21 +470,23 @@ inherited BlksAnovaForm: TBlksAnovaForm end object PosthocGroup: TGroupBox[30] AnchorSideLeft.Control = ParamsPanel + AnchorSideTop.Control = CorrectionsGroup AnchorSideRight.Control = Bevel1 AnchorSideBottom.Control = ButtonBevel Left = 0 - Height = 165 - Top = 331 + Height = 181 + Top = 339 Width = 186 - Anchors = [akLeft, akRight, akBottom] + Anchors = [akTop, akLeft, akRight, akBottom] AutoSize = True BorderSpacing.Right = 8 Caption = 'Post-Hoc Comparisons:' ChildSizing.LeftRightSpacing = 12 ChildSizing.TopBottomSpacing = 6 + ChildSizing.VerticalSpacing = 2 ChildSizing.Layout = cclLeftToRightThenTopToBottom ChildSizing.ControlsPerLine = 1 - ClientHeight = 145 + ClientHeight = 161 ClientWidth = 182 TabOrder = 25 object ScheffeChk: TCheckBox @@ -496,7 +500,7 @@ inherited BlksAnovaForm: TBlksAnovaForm object TukeyHSDChk: TCheckBox Left = 12 Height = 19 - Top = 25 + Top = 27 Width = 138 Caption = 'Tukey HSD (= n''s)' TabOrder = 1 @@ -504,7 +508,7 @@ inherited BlksAnovaForm: TBlksAnovaForm object TukeyBChk: TCheckBox Left = 12 Height = 19 - Top = 44 + Top = 48 Width = 138 Caption = 'Tukey B (= n''s)' TabOrder = 2 @@ -512,7 +516,7 @@ inherited BlksAnovaForm: TBlksAnovaForm object TukeyKramerChk: TCheckBox Left = 12 Height = 19 - Top = 63 + Top = 69 Width = 138 Caption = 'Tukey-Kramer' TabOrder = 3 @@ -520,7 +524,7 @@ inherited BlksAnovaForm: TBlksAnovaForm object NewmanKeulsChk: TCheckBox Left = 12 Height = 19 - Top = 82 + Top = 90 Width = 138 Caption = 'Newman-Keuls (= n''s)' TabOrder = 4 @@ -528,7 +532,7 @@ inherited BlksAnovaForm: TBlksAnovaForm object BonferroniChk: TCheckBox Left = 12 Height = 19 - Top = 101 + Top = 111 Width = 138 Caption = 'Bonferroni' TabOrder = 5 @@ -536,20 +540,22 @@ inherited BlksAnovaForm: TBlksAnovaForm object OrthoContrastsChk: TCheckBox Left = 12 Height = 19 - Top = 120 + Top = 132 Width = 138 Caption = 'Orthogonal Contrasts' TabOrder = 6 end end - object GroupBox1: TGroupBox[31] - AnchorSideLeft.Control = GroupBox3 - AnchorSideRight.Control = GroupBox3 + object AlphaGroup: TGroupBox[31] + AnchorSideLeft.Control = CorrectionsGroup + AnchorSideTop.Control = CorrectionsGroup + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = CorrectionsGroup AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = ButtonBevel + AnchorSideBottom.Control = ShowPlotsChk Left = 207 Height = 80 - Top = 416 + Top = 413 Width = 196 Anchors = [akLeft, akRight, akBottom] AutoSize = True @@ -574,8 +580,8 @@ inherited BlksAnovaForm: TBlksAnovaForm end object OverallAlphaEdit: TEdit AnchorSideLeft.Control = PostAlphaEdit - AnchorSideTop.Control = GroupBox1 - AnchorSideRight.Control = GroupBox1 + AnchorSideTop.Control = AlphaGroup + AnchorSideRight.Control = AlphaGroup AnchorSideRight.Side = asrBottom Left = 113 Height = 23 @@ -589,7 +595,7 @@ inherited BlksAnovaForm: TBlksAnovaForm Text = 'OverallAlphaEdit' end object Label4: TLabel - AnchorSideLeft.Control = GroupBox1 + AnchorSideLeft.Control = AlphaGroup AnchorSideTop.Control = PostAlphaEdit AnchorSideTop.Side = asrCenter Left = 24 @@ -627,30 +633,59 @@ inherited BlksAnovaForm: TBlksAnovaForm AnchorSideTop.Control = PosthocGroup Left = 194 Height = 42 - Top = 331 + Top = 339 Width = 13 Shape = bsSpacer end + object ShowPlotsChk: TCheckBox[33] + AnchorSideLeft.Control = AlphaGroup + AnchorSideTop.Control = AlphaGroup + AnchorSideTop.Side = asrBottom + AnchorSideBottom.Control = ButtonBevel + Left = 207 + Height = 19 + Top = 501 + Width = 79 + Anchors = [akLeft, akBottom] + BorderSpacing.Top = 8 + Caption = 'Plot Means' + Checked = True + OnChange = ShowPlotsChkChange + State = cbChecked + TabOrder = 27 + end + object Plot3dChk: TCheckBox[34] + AnchorSideLeft.Control = ShowPlotsChk + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = ShowPlotsChk + Left = 302 + Height = 19 + Top = 501 + Width = 34 + BorderSpacing.Left = 16 + Caption = '3D' + OnChange = Plot3dChkChange + TabOrder = 28 + end end inherited ParamsSplitter: TSplitter Left = 412 - Height = 553 + Height = 577 end inherited PageControl: TPageControl Left = 421 - Height = 537 + Height = 561 Width = 485 ActivePage = PosthocPage - TabIndex = 2 inherited ReportPage: TTabSheet Caption = 'ANOVA Results' end - inherited ChartPage: TTabSheet - Caption = 'Plots' - end - object PosthocPage: TTabSheet[2] + object PosthocPage: TTabSheet[1] Caption = 'Post-Hoc' TabVisible = False end + inherited ChartPage: TTabSheet[2] + Caption = 'Plots' + end end end diff --git a/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.pas b/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.pas index f417b7719..7bf5209ea 100644 --- a/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.pas +++ b/applications/lazstats/source/forms/analysis/comparisons/blkanovaunit.pas @@ -20,11 +20,13 @@ type TBlksAnovaForm = class(TBasicStatsReportAndChartForm) Bevel1: TBevel; BrownForsythe: TCheckBox; + ShowPlotsChk: TCheckBox; + Plot3dChk: TCheckBox; Fact1Combo: TComboBox; Fact2Combo: TComboBox; Fact3Combo: TComboBox; - GroupBox1: TGroupBox; - GroupBox3: TGroupBox; + AlphaGroup: TGroupBox; + CorrectionsGroup: TGroupBox; Label6: TLabel; Label7: TLabel; Label8: TLabel; @@ -62,6 +64,8 @@ type StaticText4: TStaticText; procedure DepInClick(Sender: TObject); procedure DepOutClick(Sender: TObject); + procedure Plot3dChkChange(Sender: TObject); + procedure ShowPlotsChkChange(Sender: TObject); procedure VarChange(Sender: TObject); procedure Fact1OutClick(Sender: TObject); procedure Fact2InClick(Sender: TObject); @@ -83,10 +87,9 @@ type OmegaF1F3, OmegaF2F3, OmegaF1F2F3: double; FF1, FF2, FF1F2, ProbF1, ProbF2, ProbF3, ProbF1F2, ProbF1F3: double; FF3, FF2F3, FF1F3, FF1F2F3, ProbF2F3, ProbF1F2F3: double; - //DepVarCol, F1Col, F2Col, F3Col, + MeanDep: Double; Nf1cells, Nf2cells, Nf3cells: integer; - MeanDep, MeanF1, MeanF2, MeanF3: double; - minf1, maxf1, minf2, maxf2, minf3, maxf3, nofactors: integer; + MinF1, MaxF1, MinF2, MaxF2, MinF3, MaxF3, NoFactors: integer; equal_grp: boolean; // check for equal groups for post-hoc tests cellcnts: IntDyneVec; // array of cell counts cellvars: DblDyneVec; // arrray of cell sums of squares then variances @@ -103,11 +106,7 @@ type WSum, WX2: DblDyneCube; NCnt: IntDyneCube; NoGrpsA, NoGrpsB, NoGrpsC: integer; -// 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 -// wx2 : array[1..20,1..20,1..20] of double; // sums of squares for 3 way cells +// AllAlpha, PostHocAlpha: double; // alphas for tests OKterms: array[1..14] of Boolean; procedure Init; @@ -185,8 +184,8 @@ begin if GraphFrm = nil then Application.CreateForm(TGraphFrm, GraphFrm); - OverallAlphaEdit.Text := FloatToStr(DEFAULT_ALPHA_LEVEL); - PostAlphaEdit.Text := FloatToStr(DEFAULT_ALPHA_LEVEL); + PostAlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); + OverallAlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); FPosthocReportFrame := TReportFrame.Create(PostHocPage); FPosthocReportFrame.Parent := PostHocPage; @@ -229,288 +228,138 @@ procedure TBlksAnovaForm.AdjustConstraints; begin inherited; ParamsPanel.Constraints.MinHeight := Fact3Combo.Top + Fact3Combo.Height + - VarList.BorderSpacing.Bottom + PosthocGroup.Height + + VarList.BorderSpacing.Bottom + + CorrectionsGroup.Height + CorrectionsGroup.BorderSpacing.Bottom + + AlphaGroup.Height + + ShowPlotsChk.BorderSpacing.Top + ShowPlotsChk.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; ParamsPanel.Constraints.MinWidth := Max( - GroupBox3.Width * 2 + Bevel1.Width, + CorrectionsGroup.Width * 2 + Bevel1.Width, 4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left ); end; -procedure TBlksAnovaForm.Reset; +procedure TBlksAnovaForm.BrownForsytheOneWay(AReport: TStrings); var - i: integer; + i, intValue: integer; + c1: array[1..50] of double; + cellmeans: array[1..50] of double; + sumc1: double; + fdegfree: double; + Fnumerator, Fdenominator, NewF: double; + X, Xsq: Double; begin - inherited; - - VarList.Clear; - for i := 1 to NoVariables do - VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); - - DepVarEdit.Text := ''; - Factor1Edit.Text := ''; - Factor2Edit.Text := ''; - Factor3Edit.Text := ''; - Fact1Combo.ItemIndex := 0; - Fact2Combo.ItemIndex := 0; - Fact3Combo.ItemIndex := 0; -// PlotMeans.Checked := false; - ScheffeChk.Checked := false; - TukeyHSDChk.Checked := false; - TukeyBChk.Checked := false; - TukeyKramerChk.Checked := false; - NewmanKeulsChk.Checked := false; - BonferroniChk.Checked := false; - PostAlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); - OverallAlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); - UpdateBtnStates; -end; - - -procedure TBlksAnovaForm.DepInClick(Sender: TObject); -var - index: integer; -begin - index := VarList.ItemIndex; - if (index > -1) and (DepVarEdit.Text = '') then + for i := 1 to 50 do begin - DepVarEdit.Text := VarList.Items[index]; - VarList.Items.Delete(index); - UpdateBtnStates; - end; -end; - -procedure TBlksAnovaForm.Compute; -var - DepValues, F1Values, F2Values, F3Values: DblDyneVec; -begin - // Get column numbers of dependent variable and factors - Init; - allAlpha := StrToFloat(OverallAlphaEdit.Text); - PostHocAlpha := StrToFloat(PostAlphaEdit.Text); - - // Get min and max of each factor code - // The function fails when codes are not integers or not consecutive. - if not GetLevels(DepValues, F1Values, F2Values, F3Values) then - exit; - - // Do analysis - case nofactors of - 1 : // Single factor anova - begin - Init1Way; - if Calc1Way(DepValues, F1Values) then - begin - OneWayTable; - OneWayPostHoc; - OneWayPlot; - end; - end; - - 2 : // Rwo-way anova - begin - Init2Way; - if Calc2Way(DepValues, F1Values, F2Values) then - begin - TwoWayTable; - TwoWayContrasts; - TwoWayPlot; - end; - end; - - 3 : // three way anova - begin - Init3Way; - if Calc3Way(DepValues, F1Values, F2Values, F3Values) then - begin - ThreeWayTable; - ThreeWayContrasts; - ThreeWayPlot; - end; - end; + c1[i] := 0.0; + cellmeans[i] := 0.0; end; -end; - -procedure TBlksAnovaForm.DepOutClick(Sender: TObject); -begin - if DepVarEdit.Text <> '' then + for i := 1 to NoCases do begin - VarList.Items.Add(DepVarEdit.Text); - DepVarEdit.Text := ''; + if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; + intvalue := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i]))) - MinF1; + cellcnts[intvalue] := 0; + cellsums[intvalue] := 0.0; + cellvars[intvalue] := 0.0; end; - UpdateBtnStates; -end; + MeanDep := 0.0; + SSDep := 0.0; + SSF1 := 0.0; + MSErr := 0.0; + N := 0; -procedure TBlksAnovaForm.VarChange(Sender: TObject); -begin - UpdateBtnStates; -end; - - -procedure TBlksAnovaForm.Fact1OutClick(Sender: TObject); -begin - if Factor1Edit.Text <> '' then + for i := 1 to NoCases do begin - VarList.Items.Add(Factor1Edit.Text); - Factor1Edit.Text := ''; - UpdateBtnStates; + if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; + intvalue := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i]))) - MinF1; + X := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[0], i])); + Xsq := X*X; + cellcnts[intvalue] := cellcnts[intvalue] + 1; + cellsums[intvalue] := cellsums[intvalue] + X; + cellvars[intvalue] := cellvars[intvalue] + Xsq; + MeanDep := MeanDep + X; + SSDep := SSDep + Xsq; + N := N + 1; end; -end; - -procedure TBlksAnovaForm.Fact2InClick(Sender: TObject); -var - index: integer; -begin - index := VarList.ItemIndex; - if (index > -1) and (Factor2Edit.Text = '') then + DFF1 := 0; + for i := 0 to Nf1cells-1 do begin - Factor2Edit.Text := VarList.Items[index]; - VarList.Items.Delete(index); - UpdateBtnStates; + if cellcnts[i] > 0 then + begin + cellvars[i] := (cellvars[i] - sqr(cellsums[i]) /cellcnts[i]) / (cellcnts[i] - 1); + SSF1 := SSF1 + (sqr(cellsums[i]) / cellcnts[i]); + DFF1 := DFF1 + 1; + end; end; -end; + SSF1 := SSF1 - (sqr(MeanDep) / N); + SSDep := SSDep - (sqr(MeanDep) / N); + SSErr := SSDep - SSF1; + DFTot := N - 1; + DFF1 := DFF1 - 1; + DFErr := DFTot - DFF1; + MSF1 := SSF1 / DFF1; + MSErr := SSErr / DFErr; + MSDep := SSDep / DFTot; + Omega := (SSF1 - DFF1 * MSErr) / (SSDep + MSErr); + F := MSF1 / MSErr; + ProbF1 := probf(F,DFF1,DFErr); + MeanDep := MeanDep / N; -procedure TBlksAnovaForm.Fact2OutClick(Sender: TObject); -begin - if Factor2Edit.Text <> '' then + AReport.Add(''); + AReport.Add(''); + AReport.Add(DIVIDER); + AReport.Add('BROWN-FORSYTHE ONE WAY ANALYSIS OF VARIANCE RESULTS'); + AReport.Add(''); + AReport.Add('Dependent variable is: %s, Independent variable is: %s', [DepVarEdit.Text, Factor1Edit.Text]); + AReport.Add(''); + AReport.Add('Traditional One-Way ANOVA Results'); + AReport.Add(DIVIDER_SMALL); + AReport.Add('SOURCE D.F. SS MS F PROB.>F OMEGA SQR.'); + AReport.Add('-----------------------------------------------------------------------'); + AReport.Add('BETWEEN %4.0f%10.2f%10.2f%10.2f%10.2f%10.2f', [DFF1, SSF1, MSF1, F, ProbF1, Omega]); + AReport.Add('WITHIN %4.0f%10.2f%10.2f', [DFErr, SSErr, MSErr]); + AReport.Add('TOTAL %4.0f%10.2f', [DFTot, SSDep]); + AReport.Add(DIVIDER); + + sumc1 := 0.0; + MSErr := 0.0; + for i := 0 to Nf1cells-1 do begin - VarList.Items.Add(Factor2Edit.Text); - Factor2Edit.Text := ''; - UpdateBtnStates; + c1[i+1] := (1.0 - (cellcnts[i] / N)) * cellvars[i]; + sumc1 := sumc1 + c1[i+1]; end; -end; + for i := 1 to Nf1cells do + c1[i] := c1[i] / sumc1; + fdegfree := 0.0; + for i := 1 to Nf1cells do + fdegfree := fdegfree + ((c1[i] * c1[i]) / (cellcnts[i-1]-1.0)); + fdegfree := round(1.0 / fdegfree); -procedure TBlksAnovaForm.Fact3InClick(Sender: TObject); -var - index: integer; -begin - index := VarList.ItemIndex; - if (index > -1) and (Factor3Edit.Text = '') then + Fnumerator := 0.0; + Fdenominator := 0.0; + for i := 1 to Nf1cells do begin - Factor3Edit.Text := VarList.Items[index]; - VarList.Items.Delete(index); - UpdateBtnStates; + cellmeans[i] := cellsums[i-1] / cellcnts[i-1]; + FNumerator := FNumerator + (cellcnts[i-1] * (sqr(cellmeans[i] - MeanDep))); + FDenominator := FDenominator + ((1.0 - (cellcnts[i-1] / N)) * cellvars[i-1]); end; -end; + NewF := FNumerator / FDenominator; + ProbF1 := probf(NewF,DFF1, fdegfree); + AReport.Add(''); + AReport.Add(DIVIDER); + AReport.Add('Brown-Forsythe F statistic: %8.3f', [NewF]); + AReport.Add('Brown-Forsythe denominator degrees of freedom: %8.0f', [fdegfree]); + AReport.Add('Brown-Forsythe F probability: %8.3f', [probf1]); + AReport.Add(DIVIDER); -procedure TBlksAnovaForm.Fact3OutClick(Sender: TObject); -begin - if Factor3Edit.Text <> '' then - begin - VarList.Items.Add(Factor3Edit.Text); - Factor3Edit.Text := ''; - UpdateBtnStates; - end; -end; - -procedure TBlksAnovaForm.Fact1InClick(Sender: TObject); -var - index: integer; -begin - index := VarList.ItemIndex; - if (index > -1) and (Factor1Edit.Text = '') then - begin - Factor1Edit.Text := VarList.Items[index]; - VarList.Items.Delete(index); - UpdateBtnStates; - end; -end; - - -procedure TBlksAnovaForm.Init; -begin - if Factor2Edit.Text = '' then - NoFactors := 1 - else if Factor3Edit.Text = '' then - NoFactors := 2 - else - NoFactors := 3; - - // Get column numbers of dependent variable and factors - SetLength(ColNoSelected, NoFactors + 1); // because DepVar is at index 0 - ColNoSelected[0] := GetVariableIndex(OS3MainFrm.DataGrid, DepVarEdit.Text); - ColNoSelected[1] := GetVariableIndex(OS3MainFrm.DataGrid, Factor1Edit.Text); - if Factor2Edit.Text <> '' then - ColNoSelected[2] := GetVariableIndex(OS3MainFrm.DataGrid, Factor2Edit.Text) - else - ColNoSelected[2] := -1; - if Factor3Edit.Text <> '' then - ColNoSelected[3] := GetVariableIndex(OS3MainFrm.DataGrid, Factor3Edit.Text) - else - ColNoSelected[3] := -1; -end; - - -function TBlksAnovaForm.GetLevels( - out DepValues, F1Values, F2Values, F3Values: DblDyneVec): Boolean; -var - mx, mn: Double; -begin - Result := false; - - DepValues := nil; - F1Values := nil; - F2Values := nil; - F3Values := nil; - - // Extract dependent variable values - DepValues := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[0], ColNoSelected); - - // Extract factor 1 values - F1Values := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[1], ColNoSelected); - VecMaxMin(F1Values, mx, mn); - MaxF1 := round(mx); - MinF1 := round(mn); - NF1Cells := MaxF1 - MinF1 + 1; - if not CheckFactorCodes(Factor1Edit.Text, F1Values) then - exit; - - // Extract factor 2 values when available - if NoFactors >= 2 then - begin - F2Values := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[2], ColNoSelected); - VecMaxMin(F2Values, mx, mn); - MaxF2 := round(mx); - MinF2 := round(mn); - NF2Cells := MaxF2 - MinF2 + 1; - if not CheckFactorCodes(Factor2Edit.Text, F2Values) then - exit; - end else - NF2Cells := 0; - - // Extract factor 3 values when available - if NoFactors = 3 then - begin - F3Values := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[3], ColNoSelected); - VecMaxMin(F3Values, mx, mn); - MaxF3 := round(mx); - MinF3 := round(mn); - NF3Cells := MaxF3 - MinF3 + 1; - if not CheckFactorCodes(Factor3Edit.Text, F3Values) then - exit; - end else - NF3cells := 0; - - Result := true; -end; - - -procedure TBlksAnovaForm.Init1Way; -begin - cellCnts := nil; - cellSums := nil; - cellVars := nil; - - SetLength(cellCnts, NF1Cells); - SetLength(cellSums, NF1Cells); - SetLength(cellVars, NF1Cells); + WelchtTests(AReport); end; @@ -567,249 +416,6 @@ begin Result := true; end; -procedure TBlksAnovaForm.OneWayTable; -var - lReport: TStrings; - i, grpsize: integer; - minvar, maxvar, sumvar, sumfreqlogvar, sumDFrecip: double; - c, bartlett, cochran, hartley, chiprob: double; - maxSize: Integer; -begin - lReport := TStringList.Create; - try - lReport.Add('ONE WAY ANALYSIS OF VARIANCE RESULTS'); - lReport.Add(''); - lReport.Add('Dependent variable is: %s', [DepVarEdit.Text]); - lReport.Add('Independent variable is: %s', [Factor1Edit.Text]); - lReport.Add(''); - - lReport.Add('-----------------------------------------------------------------------------'); - lReport.Add('SOURCE D.F. SS MS F PROB.> F Omega Sqr.'); - lReport.Add('------------- ---- ---------- ---------- ---------- -------- ----------'); - lReport.Add('Between %4.0f %10.3f %10.3f %10.3f %8.3f %8.3f', [DFF1, SSF1, MSF1, FF1, ProbF1, OmegaF1]); - lReport.Add('Within %4.0f %10.3f %10.3f', [DFErr, SSErr, MSErr]); - lReport.Add('Total %4.0f %10.3f', [DFTot, SSDep]); - lReport.Add('-----------------------------------------------------------------------------'); - - lReport.Add(''); - lReport.Add(''); - lReport.Add('MEANS AND VARIABILITY OF THE DEPENDENT VARIABLE'); - lReport.Add('FOR LEVELS OF THE INDEPENDENT VARIABLE'); - lReport.Add('---------------------------------------------------'); - lReport.Add('GROUP MEAN VARIANCE STD.DEV. N'); - lReport.Add('----- --------- ---------- ---------- ---------'); - // xxxx xxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxx - - equal_grp := true; - minVar := 1e308; - maxVar := -minVar; - sumVar := 0.0; - sumDFRecip := 0.0; - sumFreqLogVar := 0.0; - - grpSize := round(cellCnts[0]); - MinSize := grpSize; // initialized minimum group size - maxSize := grpSize; // initialize maximum group size - for i := 0 to NF1Cells-1 do - begin - grpSize := round(cellCnts[i]); - if grpSize < MinSize then - begin - MinSize := grpSize; - equal_grp := false; - end; - if grpSize > maxSize then - maxSize := grpSize; - - if cellCnts[i] > 1 then - begin - cellVars[i] := (cellVars[i] - sqr(cellSums[i]) / cellCnts[i]) / (cellCnts[i] - 1); - if cellvars[i] > maxVar then maxVar := cellVars[i]; - if cellvars[i] < minVar then minVar := cellVars[i]; - sumVar :=sumVar + cellVars[i]; - sumDFRecip := sumDFRecip + 1.0 / (cellCnts[i] - 1); - sumFreqLogVar := sumFreqLogVar + (cellCnts[i] - 1) * ln(cellVars[i]); // wp: was log10, but all the other s ahve ln() here - end; - - if cellCnts[i] > 0 then - lReport.Add('%5d %9.2f %10.2f %10.2f %7d', [ - i+1, cellSums[i] / cellCnts[i], cellVars[i], sqrt(cellVars[i]), cellCnts[i] - ]); - end; - - lReport.Add('---------------------------------------------------'); - lReport.Add('TOTAL %9.2f %10.2f %10.2f %7d', [MeanDep, MSDep, sqrt(MSDep), N]); - lReport.Add('---------------------------------------------------'); - lReport.Add(''); - - c := 1.0 + (1.0 / (3 * DFF1)) * (sumDFRecip - (1.0 / DFErr)); - bartlett := (2.303 / c) * (DFErr * ln(MSErr) - sumFreqLogVar); // wp: was log10(),but all the others use ln() here - chiProb := 1.0 - ChiSquaredProb(bartlett, round(DFF1)); - cochran := maxVar / sumVar; - hartley := maxVar / minVar; - - lReport.Add(DIVIDER_AUTO); - lReport.Add(''); - lReport.Add('TESTS FOR HOMOGENEITY OF VARIANCE'); - lReport.Add(''); - lReport.Add('Hartley Fmax test statistic: %8.3f (%d and %d degrees of freedom)', [hartley, NF1cells, maxSize-1]); - lReport.Add('Cochran C statistic: %8.3f (%d and %d degrees of freedom)', [cochran, Nf1cells, maxSize-1]); - lReport.Add('Bartlett Chi-square: %8.3f (%.0f degrees of freedom)', [bartlett, DFF1]); - lReport.Add(' probability > Chi-Square: %8.3f', [chiprob]); - - FReportFrame.DisplayReport(lReport); - finally - lReport.Free; - end; -end; - - -procedure TBlksAnovaForm.OneWayPostHoc; -var - lReport: TStrings; -begin - 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; - - lReport := TStringList.Create; - try - if ScheffeChk.Checked then - ScheffeTest(MSErr, cellSums, cellCnts, minF1, maxF1, N, posthocAlpha, lReport); - - if TukeyHSDChk.Checked and equal_grp then - Tukey(MSErr, DFErr, MinSize, cellSums, cellCnts, minF1, maxF1, posthocAlpha, lReport); - - if TukeyBChk.Checked and equal_grp then - TukeyBTest(MSErr, DFErr, cellSums, cellCnts, minF1, maxF1, MinSize, posthocAlpha, lReport); - - if TukeyKramerChk.Checked then - Tukey_Kramer(MSErr, DFErr, MinSize, cellSums, cellCnts, minF1, maxF1, posthocAlpha, lReport); - - if NewmanKeulsChk.Checked and equal_grp then - Newman_Keuls(MSErr, DFErr, MinSize, 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); - - if BrownForsythe.Checked then - BrownForsytheOneWay(lReport); - - if Welch.Checked then - WelchOneWay(lReport); - - FPostHocReportFrame.DisplayReport(lReport); - - PosthocPage.TabVisible := true; - - finally - lReport.Free; - end; -end; - -procedure TBlksAnovaForm.OneWayPlot; -var - i: Integer; -begin - FChartFrame.Clear; - FChartFrame.SetYTitle('Mean'); - FSeries := FChartFrame.PlotXY(ptBars, nil, nil, nil, nil, '', DATA_COLORS[0]); - - TBarSeries(FSeries).Styles := nil; - FSeries.ListSource.YCount := 1; - for i := 0 to NF1Cells-1 do - FSeries.AddXY(minF1 + i, cellSums[i] / cellCnts[i], IntToStr(MinF1 + i)); - FChartFrame.SetXTitle(Factor1Edit.Text + ' codes (Factor A level)'); - - FChartCombobox.Parent.Hide; - FChartFrame.Chart.Legend.Visible := false; - FChartFrame.Chart.BottomAxis.Marks.Source := FSeries.Source; - FChartFrame.Chart.BottomAxis.Marks.Style := smsXValue; -end; - -(* -procedure TBlksAnovaForm.OneWayPlot; -var - i : integer; - maxmean : double; - plottype : integer; -begin - plotType := 2; - { - if PlotMeans.Checked then plottype := 2; - if Plot2DLines.Checked then plottype := 5; - if Plot3DLines.Checked then plottype := 6; - } - GraphFrm.SetLabels[1] := 'FACTOR A'; - SetLength(GraphFrm.YPoints,1,NF1cells); - SetLength(GraphFrm.Xpoints,1,NF1cells); - - maxmean := 0.0; - for i := 0 to NF1cells-1 do - begin - cellsums[i] := cellsums[i] / cellcnts[i]; - GraphFrm.Ypoints[0, i] := cellsums[i]; - if cellsums[i] > maxmean then maxmean := cellsums[i]; - GraphFrm.Xpoints[0,i] := minF1 + i; - end; - GraphFrm.nosets := 1; - GraphFrm.nbars := NF1cells; - GraphFrm.Heading := Factor1Edit.Text; - GraphFrm.XTitle := 'FACTOR A LEVEL'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; // 3d Vertical Bar Chart - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - GraphFrm.ShowModal; - - GraphFrm.Xpoints := nil; - GraphFrm.Ypoints := nil; -end; -*) - - -procedure TBlksAnovaForm.Init2Way; -var - totalcells: Integer; -begin - RowSums := nil; - ColSums := nil; - RowCount := nil; - ColCount := nil; - Counts := nil; - Sums := nil; - Vars := nil; - cellCnts := nil; - cellVars := nil; - cellSums := 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 - - totalCells := NF1Cells + NF2Cells + NF3Cells; - SetLength(cellCnts, totalCells); // array of cell counts - SetLength(cellSums, totalCells); // array of cell sums then means - SetLength(cellVars, totalCells); // array of cell sums of squares then variances -end; - function TBlksAnovaForm.Calc2Way(const DepValues, F1Values, F2Values: DblDyneVec): Boolean; var @@ -962,459 +568,6 @@ begin Result := true; end; -procedure TBlksAnovaForm.TwoWayTable; -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 - lReport := TStringList.Create; - try - lReport.Add('TWO-WAY ANALYSIS OF VARIANCE'); - lReport.Add(''); - lReport.Add('Variable analyzed: %s', [DepVarEdit.Text]); - 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(''); - - lReport.Add( '--------------------------------------------------------------------------------'); - lReport.Add( 'SOURCE D.F. SS MS F PROB.> F Omega Squared'); - lReport.Add( '------------- ---- ---------- ---------- ---------- -------- -------------'); - lReport.Add( 'Among Rows %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1, SSF1, MSF1, FF1, ProbF1, OmegaF1]); - lReport.Add( 'Among Columns %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF2, SSF2, MSF2, FF2, ProbF2, OmegaF2]); - lReport.Add( 'Interaction %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1F2, SSF1F2, MSF1F2, FF1F2, ProbF1F2, OmegaF1F2]); - lReport.Add( 'Within Groups %4.0f %10.3f %10.3f', [DFErr, SSErr, MSErr]); - lReport.Add( 'Total %4.0f %10.3f %10.3f', [DFTot, SSDep, MSDep]); - lReport.Add( '--------------------------------------------------------------------------------'); - 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(DIVIDER_AUTO); - lReport.Add(''); - lReport.Add('DESCRIPTIVE STATISTICS'); -// lReport.Add(''); - lReport.Add('------------------------------------------------------'); - lReport.Add('GROUP Row Col. N MEAN VARIANCE STD.DEV.'); - lReport.Add('----- --- ---- ----- --------- -------- --------'); - 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)); - sumfreqlogvar := sumfreqlogvar + ((counts[i,j] - 1) * ln(V)); - if counts[i,j] <> groupsize then equal_grp := false; - end; - lReport.Add('Cell %3d %3d %4d %9.3f %8.3f %8.3f', [MinF1+i, MinF2+j, counts[i,j], XBar, V, S]); - end; - end; - - //Display Row means, variances, standard deviations - for i := 0 to NoGrpsA-1 do - begin - XBar := RowSums[i] / RowCount[i]; - 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); - S := sqrt(V); - lReport.Add('Row %3d %4d %9.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]; - 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); - if V > 0.0 then S := sqrt(V); - lReport.Add('Col %3d %4d %9.3f %8.3f %8.3f', [minf2+j, ColCount[j], XBar, V, S]); - end; - - lReport.Add('------------------------------------------------------'); - lReport.Add('TOTAL %4d %9.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_AUTO); - lReport.Add(''); - lReport.Add('TESTS FOR HOMOGENEITY OF VARIANCE'); - lReport.Add(''); //DIVIDER_SMALL_AUTO); - lReport.Add('Hartley Fmax test statistic: %8.3f with %d and %d degrees of freedom.', [hartley, NoGrpsA*NoGrpsB, groupsize-1]); - lReport.Add('Cochran C statistic: %8.3f with %d and %d degrees of freedom.', [cochran, NoGrpsA*NoGrpsB, groupsize - 1]); - lReport.Add('Bartlett Chi-square statistic: %8.3f with %d degrees of freedom; prob. > value: %.3f', [bartlett, NoGrpsA*NoGrpsB - 1, chiprob]); -// lReport.Add(DIVIDER_AUTO); - - 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; - maxmean, XBar : double; - plottype : integer; -begin - plottype := 2; - { - if PlotMeans.Checked then plottype := 2; - if Plot2DLines.Checked then plottype := 5; - if Plot3DLines.Checked then plottype := 6; - } - - // do Factor A first - GraphFrm.SetLabels[1] := 'FACTOR A'; - SetLength(GraphFrm.Xpoints, 1, NF1cells); - SetLength(GraphFrm.Ypoints, 1, NF1cells); - - maxmean := 0.0; - for i := 1 to NF1cells do - begin - RowSums[i-1] := RowSums[i-1] / RowCount[i-1]; - GraphFrm.Ypoints[0,i-1] := RowSums[i-1]; - if RowSums[i-1] > maxmean then maxmean := RowSums[i-1]; - GraphFrm.Xpoints[0,i-1] := minF1 + i - 1; - end; - - GraphFrm.nosets := 1; - GraphFrm.nbars := NF1cells; - GraphFrm.Heading := Factor1Edit.Text; - GraphFrm.XTitle := Factor1Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - if GraphFrm.ShowModal <> mroK then - exit; - - // do Factor B next - GraphFrm.SetLabels[1] := 'FACTOR B'; - SetLength(GraphFrm.Xpoints, 1, NF2cells); - SetLength(GraphFrm.Ypoints, 1, NF2cells); - - maxmean := 0.0; - for i := 1 to NF2cells do - begin - ColSums[i-1] := ColSums[i-1] / ColCount[i-1]; - GraphFrm.Ypoints[0,i-1] := ColSums[i-1]; - if ColSums[i-1] > maxmean then maxmean := ColSums[i-1]; - GraphFrm.Xpoints[0,i-1] := minF1 + i - 1; - end; - - GraphFrm.nosets := 1; - GraphFrm.nbars := NF2cells; - GraphFrm.Heading := Factor2Edit.Text; - GraphFrm.XTitle := Factor2Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - if GraphFrm.ShowModal <> mrOK then - exit; - - // do Factor A x B Interaction next - SetLength(GraphFrm.Ypoints, NF1cells, NF2cells); - SetLength(GraphFrm.Xpoints, 1, NF2cells); - maxmean := 0.0; - for i := 1 to NF1cells do - begin - GraphFrm.SetLabels[i] := Factor1Edit.Text + ' ' + IntToStr(i); - for j := 1 to NF2cells do - begin - XBar := sums[i-1,j-1] / counts[i-1,j-1]; - if XBar > maxmean then maxmean := XBar; - GraphFrm.Ypoints[i-1,j-1] := XBar; - end; - end; - - for j := 1 to NF2cells do - GraphFrm.Xpoints[0,j-1] := minF2 + j - 1; - - GraphFrm.nosets := NF1cells; - GraphFrm.nbars := NF2cells; - GraphFrm.Heading := 'Factor A x Factor B'; - GraphFrm.XTitle := Factor2Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - GraphFrm.ShowModal; - - GraphFrm.Xpoints := nil; - GraphFrm.Ypoints := nil; -end; - *) - - -procedure TBlksAnovaForm.Init3Way; -var - totalCells: Integer; -begin - RowSums := nil; - ColSums := nil; - SlcSums := nil; - RowCount := nil; - ColCount := nil; - SlcCount := nil; - WSum := nil; - WX2 := nil; - NCnt := nil; - cellCnts := nil; - cellSums := nil; - cellVars := nil; - - SetLength(RowSums, NF1Cells); // 2 way row sums - SetLength(RowCount, NF1Cells); // 2 way row count - SetLength(ColSums, NF2Cells); // 2 way col sums - SetLength(ColCount, NF2Cells); // 2 way col count - SetLength(SlcSums, NF3Cells); // 3 way slice sums - SetLength(SlcCount, NF3Cells); // 3 way slice counts - SetLength(WSum, NF1Cells, NF2Cells, NF3Cells); - SetLength(WX2, NF1Cells, NF2Cells, NF3Cells); - SetLength(NCnt, NF1Cells, NF2Cells, NF3Cells); - - totalCells := NF1Cells + NF2Cells + NF3Cells; - SetLength(cellCnts, totalCells); // array of cell counts - SetLength(cellSums, totalCells); // array of cell sums then means - SetLength(cellVars, totalCells); // array of cell sums of squares then variances - -end; - function TBlksAnovaForm.Calc3Way(const DepValues, F1Values, F2Values, F3Values: DblDyneVec): Boolean; var @@ -1555,7 +708,7 @@ begin (SSF1F2 < 0.0) or (SSF1F3 < 0.0) or (SSF2F3 < 0.0) or (SSF1F2F3 < 0.0) then begin - ErrorMsg('ERROR! A negative SS found. Unbalanced Design? Ending analysis.'); + ErrorMsg('A negative SS found. Unbalanced Design? Ending analysis.'); Result := false; exit; end; @@ -1784,242 +937,152 @@ begin end; -procedure TBlksAnovaForm.ThreeWayTable; +procedure TBlksAnovaForm.Compute; var - lReport: TStrings; - groupsize: integer; - MinVar, MaxVar, sumvars, sumDFrecip: double; - i, j, k: integer; - XBar, V, S, RowSS, ColSS, SlcSS: double; - sumfreqlogvar, c, bartlett, cochran, hartley, chiprob: double; - problem: boolean = false; + DepValues, F1Values, F2Values, F3Values: DblDyneVec; begin - lReport := TStringList.Create; - try - lReport.Add('THREE-WAY ANALYSIS OF VARIANCE'); - lReport.Add(''); - lReport.Add('Variable analyzed: %s', [DepVarEdit.Text]); - 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('Factor C (slices) variable: %s (%s levels)', [ - Factor3Edit.Text, FIXED_RANDOM[Fact3Combo.ItemIndex] - ]); - lReport.Add(''); - lReport.Add( '--------------------------------------------------------------------------------'); - lReport.Add( 'SOURCE D.F. SS MS F PROB.> F Omega Squared'); - lReport.Add( '------------- ---- ---------- ---------- ---------- -------- -------------'); - if OKTerms[1]and OKTerms[8] then - lReport.Add('Among Rows %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1, SSF1, MSF1, FF1, ProbF1, OmegaF1]) - else - lReport.Add('Among Rows %4.0f %10.3f %10.3f --- error ---', [DFF1, SSF1, MSF1 ]); + // Get column numbers of dependent variable and factors + Init; - if OKTerms[2] and OKTerms[9] then - lReport.Add('Among Columns %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF2, SSF2, MSF2, FF2, ProbF2, OmegaF2]) - else - lReport.Add('Among Columns %4.0f %10.3f %10.3f --- error ---', [DFF2, SSF2, MSF2]); + // Get min and max of each factor code + // The function fails when codes are not integers or not consecutive. + if not GetLevels(DepValues, F1Values, F2Values, F3Values) then + exit; - if OKTerms[3] and OKTerms[10] then - lReport.Add('Among Slices %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF3, SSF3, MSF3, FF3, ProbF3, OmegaF3]) - else - lReport.Add('Among Slices %4.0f %10.3f %10.3f --- error ---', [DFF3, SSF3, MSF3]); - - lReport.Add( 'A x B Inter. %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1F2, SSF1F2, MSF1F2, FF1F2, ProbF1F2, OmegaF1F2]); - lReport.Add( 'A x C Inter. %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1F3, SSF1F3, MSF1F3, FF1F3, ProbF1F3, OmegaF1F3]); - lReport.Add( 'B x C Inter. %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF2F3, SSF2F3, MSF2F3, FF2F3, ProbF2F3, OmegaF2F3]); - lReport.Add( 'AxBxC Inter. %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1F2F3, SSF1F2F3, MSF1F2F3, FF1F2F3, ProbF1F2F3, OmegaF1F2F3]); - lReport.Add( 'Within Groups %4.0f %10.3f %10.3f', [DFErr, SSErr, MSErr]); - lReport.Add( 'Total %4.0f %10.3f %10.3f', [DFTot, SSDep, MSDep]); - lReport.Add( '--------------------------------------------------------------------------------'); - lReport.Add( ''); - lReport.Add( 'Omega squared for combined effects: %.3f', [Omega]); - lReport.Add( ''); - if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) and (Fact3Combo.ItemIndex = 0) then - lReport.Add('Note: MSErr denominator for all F ratios.') - else - if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 1) and (Fact3Combo.ItemIndex = 1) then - begin - lReport.Add('Note: Error term for A is MSAxB + MSAxC - MSAxBxC'); - lReport.Add('Error term for B is MSAxB + MSBxC - MSAxBxC'); - lReport.Add('Error term for C is MSAxC + MSBxC - MSAxBxC'); - lReport.Add('Error term for AxB, AxC and BxC is MSAxBxC'); - lReport.Add('Error term for AxBxC is MSErr.'); - end else - if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 0) and (Fact3Combo.ItemIndex = 0) then - begin - lReport.Add('Note: Error term for A is MSErr'); - lReport.Add('Note: Error term for B is MSAxB'); - lReport.Add('Note: Error term for C is MSAxC'); - lReport.Add('Note: Error term for AxB is MSErr'); - lReport.Add('Note: Error term for AxC is MSErr'); - lReport.Add('Note: Error term for BxC is MSAxBxC'); - lReport.Add('Note: Error term for AxBxC is MSErr'); - end else - if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 1) and (Fact3Combo.ItemIndex = 0) then - begin - lReport.Add('Note: Error term for A is MSAxB'); - lReport.Add('Note: Error term for B is MSErr'); - lReport.Add('Note: Error term for C is MSBxC'); - lReport.Add('Note: Error term for AxB is MSErr'); - lReport.Add('Note: Error term for AxC is MSAxBxC'); - lReport.Add('Note: Error term for BxC is MSErr'); - lReport.Add('Note: Error term for AxBxC is MSErr'); - end else - if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) and (Fact3Combo.ItemIndex = 1) then - begin - lReport.Add('Note: Error term for A is MSAxC'); - lReport.Add('Note: Error term for B is MSBxC'); - lReport.Add('Note: Error term for C is MSErr'); - lReport.Add('Note: Error term for AxB is MSAxBxC'); - lReport.Add('Note: Error term for AxC is MSErr'); - lReport.Add('Note: Error term for BxC is MSErr'); - lReport.Add('Note: Error term for AxBxC is MSErr'); - end else - if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 1) and (Fact3Combo.ItemIndex = 1) then - begin - lReport.Add('Note: Error term for A is MSAxC + MSAxB - MSAxBxC'); - lReport.Add('Note: Error term for B is MSBxC'); - lReport.Add('Note: Error term for C is MSBxC'); - lReport.Add('Note: Error term for AxB is MSAxBxC'); - lReport.Add('Note: Error term for AxC is MSAxBxC'); - lReport.Add('Note: Error term for BxC is MSErr'); - lReport.Add('Note: Error term for AxBxC is MSErr'); - end else - if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 0) and (Fact3Combo.ItemIndex = 1) then - begin - lReport.Add('Note: Error term for A is MSAxC'); - lReport.Add('Note: Error term for B is MSBxC + MSAxB - MSAxBxC'); - lReport.Add('Note: Error term for C is MSAxC'); - lReport.Add('Note: Error term for AxB is MSAxBxC'); - lReport.Add('Note: Error term for AxC is MSErr'); - lReport.Add('Note: Error term for BxC is MSAxBxC'); - lReport.Add('Note: Error term for AxBxC is MSErr'); - end else - if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 1) and (Fact3Combo.ItemIndex = 0) then - begin - lReport.Add('Note: Error term for A is MSAxB'); - lReport.Add('Note: Error term for B is MSAxB'); - lReport.Add('Note: Error term for C is MSBxC + MSAxC - MSAxBxC'); - lReport.Add('Note: Error term for AxB is MSErr'); - lReport.Add('Note: Error term for AxC is MSAxBxC'); - lReport.Add('Note: Error term for BxC is MSAxBxC'); - lReport.Add('Note: Error term for AxBxC is MSErr'); - end; - - for i := 1 to 10 do - if not OKTerms[i] then - problem := true; - - if problem then - begin - lReport.Add(''); - lReport.Add('An error occurred due to either an estimate of MS being negative'); - lReport.Add('or the degrees of freedom being zero. This may occur in a design'); - lReport.Add('with random factors using the expected values for an exact F-test.'); - lReport.Add('Quasi-F statistics may be employed where this problem exists. See'); - lReport.Add('Winer, B.J., "Statistical Principles in Experimental Design, 1962'); - lReport.Add('Section 5.15, pages 199-202 and Glass, G.V. and Stanley, J.C.,'); - lReport.Add('1970, Section 18.10, pages 481-482.'); - end; - - lReport.Add(''); - lReport.Add(DIVIDER_AUTO); - lReport.Add(''); - lReport.Add('DESCRIPTIVE STATISTICS'); - lReport.Add(''); - lReport.Add('-------------------------------------------------------------'); - lReport.Add('GROUP ROW COL SLICE N MEAN VARIANCE STD.DEV.'); - lReport.Add('----- --- --- ----- ----- -------- ---------- --------'); - - // Display cell means, variances, standard deviations - groupsize := NCnt[1, 1, 1]; - equal_grp := true; - MaxVar := -1E308; - MinVar := 1E308; - sumVars := 0.0; - sumFreqLogVar := 0.0; - sumDFRecip := 0.0; - for i := 0 to NoGrpsA-1 do - begin - for j := 0 to NoGrpsB-1 do + // Do analysis + case nofactors of + 1 : // Single factor anova begin - for k := 0 to NoGrpsC-1 do + Init1Way; + if Calc1Way(DepValues, F1Values) then begin - XBar := WSum[i, j, k] / NCnt[i, j, k]; - V := (WX2[i, j, k] - sqr(WSum[i, j, k]) / NCnt[i, j, k]) / (NCnt[i, j, k] - 1); - S := sqrt(V); - sumVars := sumVars + V; - if V > MaxVar then MaxVar := V; - if V < MinVar then MinVar := V; - sumDFRecip := sumDFRecip + (1.0 / (NCnt[i, j, k] - 1)); - sumfreqLogVar := sumFreqLogVar + (NCnt[i, j, k] - 1) * ln(V); - if NCnt[i, j, k] <> groupsize then equal_grp := false; - lReport.Add('Cell %3d %3d %5d %5d %8.3f %10.3f %8.3f', [MinF1+i, MinF2+j, MinF3+k, NCnt[i, j, k], XBar, V, S]); + OneWayTable; + OneWayPostHoc; + OneWayPlot; end; - end; - end; + end; - //Display Row means, variances, standard deviations - for i := 0 to NoGrpsA-1 do - begin - XBar := RowSums[i] / RowCount[i]; - RowSS := 0.0; - for j := 0 to NoGrpsB-1 do - for k := 0 to NoGrpsC-1 do RowSS := RowSS + WX2[i, j, k]; - V := (RowSS - sqr(RowSums[i]) / RowCount[i]) / (RowCount[i] - 1); - S := sqrt(V); - lReport.Add('Row %3d %5d %8.3f %10.3f %8.3f', [MinF1+i, RowCount[i], xBar, V, s]); - end; + 2 : // Rwo-way anova + begin + Init2Way; + if Calc2Way(DepValues, F1Values, F2Values) then + begin + TwoWayTable; + TwoWayContrasts; + TwoWayPlot; + end; + end; - //Display means, variances and standard deviations for columns - for j := 0 to NoGrpsB-1 do - begin - XBar := ColSums[j] / ColCount[j]; - ColSS := 0.0; - for i := 0 to NoGrpsA-1 do - for k := 0 to NoGrpsC-1 do ColSS := ColSS + WX2[i, j, k]; - V := (ColSS - sqr(ColSums[j]) / ColCount[j]) / (ColCount[j] - 1); - S := sqrt(V); - lReport.Add('Col %3d %5d %8.3f %10.3f %8.3f', [MinF2+j, ColCount[j], xBar, V, S]); - end; + 3 : // three way anova + begin + Init3Way; + if Calc3Way(DepValues, F1Values, F2Values, F3Values) then + begin + ThreeWayTable; + ThreeWayContrasts; + ThreeWayPlot; + end; + end; + end; - //Display means, variances and standard deviations for slices - for k := 0 to NoGrpsC-1 do - begin - XBar := SlcSums[k] / SlcCount[k]; - SlcSS := 0.0; - for i := 0 to NoGrpsA-1 do - for j := 0 to NoGrpsB-1 do SlcSS := SlcSS + WX2[i, j, k]; - V := (SlcSS - sqr(SlcSums[k]) / SlcCount[k]) / (SlcCount[k] - 1); - S := sqrt(V); - lReport.Add('Slice %5d %5d %8.3f %10.3f %8.3f', [MinF3+k, SlcCount[k], xBar, V,S]); - end; +end; - lReport.Add('-------------------------------------------------------------'); - lReport.Add('TOTAL %5d %8.3f %10.3f %8.3f', [N, MeanDep, MSDep, sqrt(MSDep)]); - lReport.Add('-------------------------------------------------------------'); - lReport.Add(''); - c := 1.0 + (1.0 / (3.0 * NoGrpsA * NoGrpsB * NoGrpsC - 1)) * (sumDFrecip - (1.0 / DFErr)); - bartlett := (2.303 / c) * ((DFErr * ln(MSErr)) - SumFreqLogVar); - chiProb := ChiSquaredProb(bartlett, round(NoGrpsA * NoGrpsB * NoGrpsC - 1)); - cochran := MaxVar / SumVars; - hartley := MaxVar / MinVar; +procedure TBlksAnovaForm.DepInClick(Sender: TObject); +var + index: integer; +begin + index := VarList.ItemIndex; + if (index > -1) and (DepVarEdit.Text = '') then + begin + DepVarEdit.Text := VarList.Items[index]; + VarList.Items.Delete(index); + UpdateBtnStates; + end; +end; - lReport.Add(DIVIDER); - lReport.Add(''); - lReport.Add('TESTS FOR HOMOGENEITY OF VARIANCE'); - lReport.Add(''); - lReport.Add('Hartley Fmax test statistic: %8.3f with %d and %d degrees of freedom.', [hartley, NoGrpsA*NoGrpsB, groupsize-1]); - lReport.Add('Cochran C statistic: %8.3f with %d and %d degrees of freedom.', [cochran, NoGrpsA*NoGrpsB, groupsize-1]); - lReport.Add('Bartlett Chi-square statistic: %8.3f with %d degrees of freedom; probability > value: %.3f', [bartlett, NoGrpsA*NoGrpsB-1, 1-chiProb]); - FReportFrame.DisplayReport(lReport); - finally - lReport.Free; +procedure TBlksAnovaForm.DepOutClick(Sender: TObject); +begin + if DepVarEdit.Text <> '' then + begin + VarList.Items.Add(DepVarEdit.Text); + DepVarEdit.Text := ''; + end; + UpdateBtnStates; +end; + +procedure TBlksAnovaForm.Fact1InClick(Sender: TObject); +var + index: integer; +begin + index := VarList.ItemIndex; + if (index > -1) and (Factor1Edit.Text = '') then + begin + Factor1Edit.Text := VarList.Items[index]; + VarList.Items.Delete(index); + UpdateBtnStates; + end; +end; + + +procedure TBlksAnovaForm.Fact1OutClick(Sender: TObject); +begin + if Factor1Edit.Text <> '' then + begin + VarList.Items.Add(Factor1Edit.Text); + Factor1Edit.Text := ''; + UpdateBtnStates; + end; +end; + + +procedure TBlksAnovaForm.Fact2InClick(Sender: TObject); +var + index: integer; +begin + index := VarList.ItemIndex; + if (index > -1) and (Factor2Edit.Text = '') then + begin + Factor2Edit.Text := VarList.Items[index]; + VarList.Items.Delete(index); + UpdateBtnStates; + end; +end; + + +procedure TBlksAnovaForm.Fact2OutClick(Sender: TObject); +begin + if Factor2Edit.Text <> '' then + begin + VarList.Items.Add(Factor2Edit.Text); + Factor2Edit.Text := ''; + UpdateBtnStates; + end; +end; + + +procedure TBlksAnovaForm.Fact3InClick(Sender: TObject); +var + index: integer; +begin + index := VarList.ItemIndex; + if (index > -1) and (Factor3Edit.Text = '') then + begin + Factor3Edit.Text := VarList.Items[index]; + VarList.Items.Delete(index); + UpdateBtnStates; + end; +end; + + +procedure TBlksAnovaForm.Fact3OutClick(Sender: TObject); +begin + if Factor3Edit.Text <> '' then + begin + VarList.Items.Add(Factor3Edit.Text); + Factor3Edit.Text := ''; + UpdateBtnStates; end; end; @@ -2090,6 +1153,413 @@ begin end; +function TBlksAnovaForm.GetLevels( + out DepValues, F1Values, F2Values, F3Values: DblDyneVec): Boolean; +var + mx, mn: Double; +begin + Result := false; + + DepValues := nil; + F1Values := nil; + F2Values := nil; + F3Values := nil; + + // Extract dependent variable values + DepValues := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[0], ColNoSelected); + + // Extract factor 1 values + F1Values := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[1], ColNoSelected); + VecMaxMin(F1Values, mx, mn); + MaxF1 := round(mx); + MinF1 := round(mn); + NF1Cells := MaxF1 - MinF1 + 1; + if not CheckFactorCodes(Factor1Edit.Text, F1Values) then + exit; + + // Extract factor 2 values when available + if NoFactors >= 2 then + begin + F2Values := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[2], ColNoSelected); + VecMaxMin(F2Values, mx, mn); + MaxF2 := round(mx); + MinF2 := round(mn); + NF2Cells := MaxF2 - MinF2 + 1; + if not CheckFactorCodes(Factor2Edit.Text, F2Values) then + exit; + end else + NF2Cells := 0; + + // Extract factor 3 values when available + if NoFactors = 3 then + begin + F3Values := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[3], ColNoSelected); + VecMaxMin(F3Values, mx, mn); + MaxF3 := round(mx); + MinF3 := round(mn); + NF3Cells := MaxF3 - MinF3 + 1; + if not CheckFactorCodes(Factor3Edit.Text, F3Values) then + exit; + end else + NF3cells := 0; + + Result := true; +end; + + +procedure TBlksAnovaForm.Init; +begin + if Factor2Edit.Text = '' then + NoFactors := 1 + else if Factor3Edit.Text = '' then + NoFactors := 2 + else + NoFactors := 3; + + // Get column numbers of dependent variable and factors + SetLength(ColNoSelected, NoFactors + 1); // because DepVar is at index 0 + ColNoSelected[0] := GetVariableIndex(OS3MainFrm.DataGrid, DepVarEdit.Text); + ColNoSelected[1] := GetVariableIndex(OS3MainFrm.DataGrid, Factor1Edit.Text); + if Factor2Edit.Text <> '' then + ColNoSelected[2] := GetVariableIndex(OS3MainFrm.DataGrid, Factor2Edit.Text) + else + ColNoSelected[2] := -1; + if Factor3Edit.Text <> '' then + ColNoSelected[3] := GetVariableIndex(OS3MainFrm.DataGrid, Factor3Edit.Text) + else + ColNoSelected[3] := -1; + + CorrectionsGroup.Enabled := (NoFactors = 1); +end; + + +procedure TBlksAnovaForm.Init1Way; +begin + cellCnts := nil; + cellSums := nil; + cellVars := nil; + + SetLength(cellCnts, NF1Cells); + SetLength(cellSums, NF1Cells); + SetLength(cellVars, NF1Cells); +end; + + +procedure TBlksAnovaForm.Init2Way; +begin + Init1Way; + + 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; + + +procedure TBlksAnovaForm.Init3Way; +begin + Init2Way; + + SlcSums := nil; + SlcCount := nil; + WSum := nil; + WX2 := nil; + NCnt := nil; + + SetLength(SlcSums, NF3Cells); // 3 way slice sums + SetLength(SlcCount, NF3Cells); // 3 way slice counts + SetLength(WSum, NF1Cells, NF2Cells, NF3Cells); + SetLength(WX2, NF1Cells, NF2Cells, NF3Cells); + SetLength(NCnt, NF1Cells, NF2Cells, NF3Cells); +end; + + +procedure TBlksAnovaForm.OneWayTable; +var + lReport: TStrings; + i, grpsize: integer; + minvar, maxvar, sumvar, sumfreqlogvar, sumDFrecip: double; + c, bartlett, cochran, hartley, chiprob: double; + maxSize: Integer; +begin + lReport := TStringList.Create; + try + lReport.Add('ONE WAY ANALYSIS OF VARIANCE RESULTS'); + lReport.Add(''); + lReport.Add('Dependent variable is: %s', [DepVarEdit.Text]); + lReport.Add('Independent variable is: %s', [Factor1Edit.Text]); + lReport.Add(''); + + lReport.Add('-----------------------------------------------------------------------------'); + lReport.Add('SOURCE D.F. SS MS F PROB.> F Omega Sqr.'); + lReport.Add('------------- ---- ---------- ---------- ---------- -------- ----------'); + lReport.Add('Between %4.0f %10.3f %10.3f %10.3f %8.3f %8.3f', [DFF1, SSF1, MSF1, FF1, ProbF1, OmegaF1]); + lReport.Add('Within %4.0f %10.3f %10.3f', [DFErr, SSErr, MSErr]); + lReport.Add('Total %4.0f %10.3f', [DFTot, SSDep]); + lReport.Add('-----------------------------------------------------------------------------'); + + lReport.Add(''); + lReport.Add(''); + lReport.Add('MEANS AND VARIABILITY OF THE DEPENDENT VARIABLE'); + lReport.Add('FOR LEVELS OF THE INDEPENDENT VARIABLE'); + lReport.Add('---------------------------------------------------'); + lReport.Add('GROUP MEAN VARIANCE STD.DEV. N'); + lReport.Add('----- --------- ---------- ---------- ---------'); + // xxxx xxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxx + + equal_grp := true; + minVar := 1e308; + maxVar := -minVar; + sumVar := 0.0; + sumDFRecip := 0.0; + sumFreqLogVar := 0.0; + + grpSize := round(cellCnts[0]); + MinSize := grpSize; // initialized minimum group size + maxSize := grpSize; // initialize maximum group size + for i := 0 to NF1Cells-1 do + begin + grpSize := round(cellCnts[i]); + if grpSize < MinSize then + begin + MinSize := grpSize; + equal_grp := false; + end; + if grpSize > maxSize then + maxSize := grpSize; + + if cellCnts[i] > 1 then + begin + cellVars[i] := (cellVars[i] - sqr(cellSums[i]) / cellCnts[i]) / (cellCnts[i] - 1); + if cellvars[i] > maxVar then maxVar := cellVars[i]; + if cellvars[i] < minVar then minVar := cellVars[i]; + sumVar :=sumVar + cellVars[i]; + sumDFRecip := sumDFRecip + 1.0 / (cellCnts[i] - 1); + sumFreqLogVar := sumFreqLogVar + (cellCnts[i] - 1) * ln(cellVars[i]); // wp: was log10, but all the other s ahve ln() here + end; + + if cellCnts[i] > 0 then + lReport.Add('%5d %9.2f %10.2f %10.2f %7d', [ + i+1, cellSums[i] / cellCnts[i], cellVars[i], sqrt(cellVars[i]), cellCnts[i] + ]); + end; + + lReport.Add('---------------------------------------------------'); + lReport.Add('TOTAL %9.2f %10.2f %10.2f %7d', [MeanDep, MSDep, sqrt(MSDep), N]); + lReport.Add('---------------------------------------------------'); + lReport.Add(''); + + c := 1.0 + (1.0 / (3 * DFF1)) * (sumDFRecip - (1.0 / DFErr)); + bartlett := (2.303 / c) * (DFErr * ln(MSErr) - sumFreqLogVar); // wp: was log10(),but all the others use ln() here + chiProb := 1.0 - ChiSquaredProb(bartlett, round(DFF1)); + cochran := maxVar / sumVar; + hartley := maxVar / minVar; + + lReport.Add(DIVIDER_AUTO); + lReport.Add(''); + lReport.Add('TESTS FOR HOMOGENEITY OF VARIANCE'); + lReport.Add(''); + lReport.Add('Hartley Fmax test statistic: %8.3f (%d and %d degrees of freedom)', [hartley, NF1cells, maxSize-1]); + lReport.Add('Cochran C statistic: %8.3f (%d and %d degrees of freedom)', [cochran, Nf1cells, maxSize-1]); + lReport.Add('Bartlett Chi-square: %8.3f (%.0f degrees of freedom)', [bartlett, DFF1]); + lReport.Add(' probability > Chi-Square: %8.3f', [chiprob]); + + FReportFrame.DisplayReport(lReport); + finally + lReport.Free; + end; +end; + + +procedure TBlksAnovaForm.OneWayPostHoc; +var + lReport: TStrings; + allAlpha, posthocAlpha: Double; +begin + 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; + + allAlpha := StrToFloat(OverallAlphaEdit.Text); + posthocAlpha := StrToFloat(PostAlphaEdit.Text); + + lReport := TStringList.Create; + try + if ScheffeChk.Checked then + ScheffeTest(MSErr, cellSums, cellCnts, minF1, maxF1, N, posthocAlpha, lReport); + + if TukeyHSDChk.Checked and equal_grp then + Tukey(MSErr, DFErr, MinSize, cellSums, cellCnts, minF1, maxF1, posthocAlpha, lReport); + + if TukeyBChk.Checked and equal_grp then + TukeyBTest(MSErr, DFErr, cellSums, cellCnts, minF1, maxF1, MinSize, posthocAlpha, lReport); + + if TukeyKramerChk.Checked then + Tukey_Kramer(MSErr, DFErr, MinSize, cellSums, cellCnts, minF1, maxF1, posthocAlpha, lReport); + + if NewmanKeulsChk.Checked and equal_grp then + Newman_Keuls(MSErr, DFErr, MinSize, 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); + + if BrownForsythe.Checked then + BrownForsytheOneWay(lReport); + + if Welch.Checked then + WelchOneWay(lReport); + + FPostHocReportFrame.DisplayReport(lReport); + + PosthocPage.TabVisible := true; + + finally + lReport.Free; + end; +end; + +procedure TBlksAnovaForm.OneWayPlot; +var + i: Integer; +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 + Styles := nil; + Stacked := false; + {$IF LCL_FullVersion >= 2010000} + DepthBrightnessDelta := -30; + {$ENDIF} + end; + if Plot3DChk.Checked then + FSeries.Depth := 20; + + FSeries.ListSource.YCount := 1; + for i := 0 to NF1Cells-1 do + FSeries.AddXY(minF1 + i, cellSums[i] / cellCnts[i], IntToStr(MinF1 + i)); + FChartFrame.SetXTitle(Factor1Edit.Text + ' codes (Factor A level)'); + + FChartCombobox.Parent.Hide; + FChartFrame.Chart.Legend.Visible := false; + FChartFrame.Chart.BottomAxis.Marks.Source := FSeries.Source; + FChartFrame.Chart.BottomAxis.Marks.Style := smsXValue; +end; + + +procedure TBlksAnovaForm.Plot3dChkChange(Sender: TObject); +begin + if FSeries is TBarSeries then + begin + if Plot3dChk.Checked then + TBarSeries(FSeries).Depth := 20 + else + TBarSeries(FSeries).Depth := 0; + 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.Reset; +var + i: integer; +begin + inherited; + + VarList.Clear; + for i := 1 to NoVariables do + VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); + + DepVarEdit.Clear; + Factor1Edit.Clear; + Factor2Edit.Clear; + Factor3Edit.Clear; + Fact1Combo.ItemIndex := 0; + Fact2Combo.ItemIndex := 0; + Fact3Combo.ItemIndex := 0; + ScheffeChk.Checked := false; + TukeyHSDChk.Checked := false; + TukeyBChk.Checked := false; + TukeyKramerChk.Checked := false; + NewmanKeulsChk.Checked := false; + BonferroniChk.Checked := false; + + UpdateBtnStates; +end; + + { OnChange event handler for the ChartCombobox. Displays the chart associated with the current ItemIndex. } procedure TBlksAnovaForm.SelectThreeWayPlot(Sender: TObject); @@ -2275,454 +1745,92 @@ begin FChartFrame.UpdateBtnStates; end; -procedure TBlksAnovaForm.ThreeWayPlot; -begin - { - if not ShowPlotsChk.Checked then - begin - ChartPage.TabVisible := false; - exit; - end; - } - FChartFrame.Clear; // this destroys the series - FChartFrame.SetYTitle('Means'); - FSeries := FChartFrame.PlotXY(ptBars, nil, nil, nil, nil, '', DATA_Colors[0]); - with TBarSeries(FSeries) do - begin - Stacked := false; - {$IF LCL_FullVersion >= 2010000} - DepthBrightnessDelta := -30; - {$IFEND} - end; - { - if Plot3DChk.Checked then - FSeries.Depth := 20; - } - PopulateChartCombobox(true); - FChartCombobox.OnChange := @SelectThreeWayPlot; - SelectThreeWayPlot(nil); - ChartPage.TabVisible := true; +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.ThreeWayPlot; -var - i, j, k : integer; - maxmean, XBar : double; - plottype : integer; + +procedure TBlksAnovaForm.ShowPlotsChkChange(Sender: TObject); begin - plottype := 2; - { - if PlotMeans.Checked then plottype := 2; - if Plot2DLines.Checked then plottype := 5; - if Plot3DLines.Checked then plottype := 6; - } - // do Factor A first - GraphFrm.SetLabels[1] := 'FACTOR A'; - SetLength(GraphFrm.Xpoints,1,NF1cells); - SetLength(GraphFrm.Ypoints,1,NF1cells); - maxmean := 0.0; - for i := 0 to NF1cells-1 do - begin - RowSums[i] := RowSums[i] / RowCount[i]; - GraphFrm.Ypoints[0,i] := RowSums[i]; - if RowSums[i] > maxmean then maxmean := RowSums[i]; - GraphFrm.Xpoints[0,i] := minF1 + i; - end; - - GraphFrm.nosets := 1; - GraphFrm.nbars := NF1cells; - GraphFrm.Heading := Factor1Edit.Text; - GraphFrm.XTitle := Factor1Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - if GraphFrm.ShowModal <> mrOK then - exit; - - // do Factor B next - GraphFrm.SetLabels[1] := 'FACTOR B'; - maxmean := 0.0; - SetLength(GraphFrm.Xpoints, 1, NF2cells); - SetLength(GraphFrm.Ypoints, 1, NF2cells); - for i := 0 to NF2cells-1 do - begin - ColSums[i] := ColSums[i] / ColCount[i]; - GraphFrm.Ypoints[0,i] := ColSums[i]; - if ColSums[i] > maxmean then maxmean := ColSums[i]; - GraphFrm.Xpoints[0,i] := minF2 + i; - end; - GraphFrm.nosets := 1; - GraphFrm.nbars := NF2cells; - GraphFrm.Heading := Factor2Edit.Text; - GraphFrm.XTitle := Factor2Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - if GraphFrm.ShowModal <> mrOK then - exit; - - // do Factor C next - GraphFrm.SetLabels[1] := 'FACTOR C'; - maxmean := 0.0; - SetLength(GraphFrm.Xpoints, 1, NF3cells); - SetLength(GraphFrm.Ypoints, 1, NF3cells); - for i := 0 to NF3cells-1 do - begin - SlcSums[i] := SlcSums[i] / SlcCount[i]; - GraphFrm.Ypoints[0,i] := SlcSums[i]; - if SlcSums[i] > maxmean then maxmean := SlcSums[i]; - GraphFrm.Xpoints[0,i] := minF3 + i; - end; - GraphFrm.nosets := 1; - GraphFrm.nbars := NF3cells; - GraphFrm.Heading := Factor3Edit.Text; - GraphFrm.XTitle := Factor2Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - if GraphFrm.ShowModal <> mrOK then - exit; - - // do Factor A x B Interaction within each slice next - SetLength(GraphFrm.Ypoints, NF1cells, NF2cells); - SetLength(GraphFrm.Xpoints, 1, NF2cells); - for k := 0 to NF3cells-1 do - begin - maxmean := 0.0; - for i := 0 to NF1cells-1 do - begin - GraphFrm.SetLabels[i+1] := Factor1Edit.Text + ' ' + IntToStr(i+1); - for j := 0 to NF2cells-1 do - begin - XBar := wsum[i,j,k] / ncnt[i,j,k]; - if XBar > maxmean then maxmean := XBar; - GraphFrm.Ypoints[i,j] := XBar; - end; - end; - for j := 0 to NF2cells-1 do - GraphFrm.Xpoints[0,j] := minF2 + j ; - - GraphFrm.nosets := NF1cells; - GraphFrm.nbars := NF2cells; - GraphFrm.Heading := 'Factor A x Factor B Within Slice' + IntToStr(k); - GraphFrm.XTitle := Factor2Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - if GraphFrm.ShowModal <> mrOK then - exit; - end; - - // do Factor A x C Interaction within each Column next - SetLength(GraphFrm.Ypoints, NF1cells, NF3cells); - SetLength(GraphFrm.Xpoints, 1, NF3cells); - for j := 0 to NF2cells-1 do - begin - maxmean := 0.0; - for i := 0 to NF1cells-1 do - begin - GraphFrm.SetLabels[i+1] := Factor1Edit.Text + ' ' + IntToStr(i+1); - for k := 0 to NF3cells-1 do - begin - XBar := wsum[i,j,k] / ncnt[i,j,k]; - if XBar > maxmean then maxmean := XBar; - GraphFrm.Ypoints[i,k] := XBar; - end; - end; - for k := 0 to NF3cells-1 do - GraphFrm.Xpoints[0,k] := minF3 + k; - - GraphFrm.nosets := NF1cells; - GraphFrm.nbars := NF3cells; - GraphFrm.Heading := 'Factor A x Factor C Within Column ' + IntToStr(j+1); - GraphFrm.XTitle := Factor3Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - if GraphFrm.ShowModal <> mrOK then - exit; - end; - - // do Factor B x C Interaction within each row next - SetLength(GraphFrm.Ypoints, NF2cells, NF3cells); - SetLength(GraphFrm.Xpoints, 1, NF3cells); - for i := 0 to NF1cells-1 do - begin - maxmean := 0.0; - for j := 0 to NF2cells-1 do - begin - GraphFrm.SetLabels[j+1] := Factor2Edit.Text + ' ' + IntToStr(j+1); - for k := 0 to NF3cells-1 do - begin - XBar := wsum[i,j,k] / ncnt[i,j,k]; - if XBar > maxmean then maxmean := XBar; - GraphFrm.Ypoints[j,k] := XBar; - end; - end; - for j := 0 to NF2cells-1 do - GraphFrm.Xpoints[0,j] := minF2 + j; - - GraphFrm.nosets := NF2cells; - GraphFrm.nbars := NF3cells; - GraphFrm.Heading := 'Factor B x Factor C Within Row ' + IntToStr(i+1); - GraphFrm.XTitle := Factor3Edit.Text + ' Codes'; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := 0.0; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := GRAPH_BACK_COLOR; - GraphFrm.WallColor := GRAPH_WALL_COLOR; - GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; - GraphFrm.ShowBackWall := true; - if GraphFrm.ShowModal <> mrOK then - exit; - end; // next row - - GraphFrm.Xpoints := nil; - GraphFrm.Ypoints := nil; -end; -*) - -procedure TBlksAnovaForm.TwoWayContrasts; -var - lReport: TStrings; - i, j: integer; - value: double; - variances: DblDyneVec = nil; - RowSS, ColSS: double; - totalCells: Integer; - -begin - 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) 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 - sqr(RowSums[i]) / RowCount[i]) / (RowCount[i] - 1); - 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 - sqr(ColSums[j]) / ColCount[j]) / (ColCount[j] - 1); - 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 - 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, 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; - 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; + ChartPage.TabVisible := ShowPlotsChk.Checked; + Plot3DChk.Enabled := ShowPlotsChk.Checked; end; @@ -2734,6 +1842,7 @@ var variances: DblDyneVec = nil; RowSS, ColSS, SlcSS: double; totalCells: Integer; + allAlpha, posthocAlpha: Double; begin if not (ScheffeChk.Checked or TukeyHSDChk.Checked or TukeyBChk.Checked or TukeyKramerChk.Checked or NewmanKeulsChk.Checked or @@ -2743,6 +1852,9 @@ begin exit; end; + allAlpha := StrToFloat(OverallAlphaEdit.Text); + PostHocAlpha := StrToFloat(PostAlphaEdit.Text); + totalCells := NF1Cells + NF2Cells + NF3Cells; SetLength(variances, totalCells); @@ -3081,126 +2193,765 @@ begin end; -procedure TBlksAnovaForm.BrownForsytheOneWay(AReport: TStrings); -var - i, intValue: integer; - c1: array[1..50] of double; - cellmeans: array[1..50] of double; - sumc1: double; - fdegfree: double; - Fnumerator, Fdenominator, NewF: double; - X, Xsq: Double; +procedure TBlksAnovaForm.ThreeWayPlot; begin - for i := 1 to 50 do + if not ShowPlotsChk.Checked then begin - c1[i] := 0.0; - cellmeans[i] := 0.0; + ChartPage.TabVisible := false; + exit; end; - for i := 1 to NoCases do + FChartFrame.Clear; // this destroys the series + FChartFrame.SetYTitle('Mean'); + FSeries := FChartFrame.PlotXY(ptBars, nil, nil, nil, nil, '', DATA_Colors[0]); + with TBarSeries(FSeries) do begin - if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; - intvalue := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i]))) - MinF1; - cellcnts[intvalue] := 0; - cellsums[intvalue] := 0.0; - cellvars[intvalue] := 0.0; + Stacked := false; + {$IF LCL_FullVersion >= 2010000} + DepthBrightnessDelta := -30; + {$IFEND} end; + if Plot3DChk.Checked then + FSeries.Depth := 20; - MeanDep := 0.0; - SSDep := 0.0; - SSF1 := 0.0; - MSErr := 0.0; - N := 0; + FChartCombobox.Parent.Left := 0; + FChartCombobox.Parent.Visible := true; + PopulateChartCombobox(true); + FChartCombobox.OnChange := @SelectThreeWayPlot; + SelectThreeWayPlot(nil); - for i := 1 to NoCases do - begin - if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; - intvalue := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i]))) - MinF1; - X := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[0], i])); - Xsq := X*X; - cellcnts[intvalue] := cellcnts[intvalue] + 1; - cellsums[intvalue] := cellsums[intvalue] + X; - cellvars[intvalue] := cellvars[intvalue] + Xsq; - MeanDep := MeanDep + X; - SSDep := SSDep + Xsq; - N := N + 1; - end; + ChartPage.TabVisible := true; +end; - DFF1 := 0; - for i := 0 to Nf1cells-1 do - begin - if cellcnts[i] > 0 then + +procedure TBlksAnovaForm.ThreeWayTable; +var + lReport: TStrings; + groupsize: integer; + MinVar, MaxVar, sumvars, sumDFrecip: double; + i, j, k: integer; + XBar, V, S, RowSS, ColSS, SlcSS: double; + sumfreqlogvar, c, bartlett, cochran, hartley, chiprob: double; + problem: boolean = false; +begin + lReport := TStringList.Create; + try + lReport.Add('THREE-WAY ANALYSIS OF VARIANCE'); + lReport.Add(''); + lReport.Add('Variable analyzed: %s', [DepVarEdit.Text]); + 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('Factor C (slices) variable: %s (%s levels)', [ + Factor3Edit.Text, FIXED_RANDOM[Fact3Combo.ItemIndex] + ]); + lReport.Add(''); + lReport.Add( '--------------------------------------------------------------------------------'); + lReport.Add( 'SOURCE D.F. SS MS F PROB.> F Omega Squared'); + lReport.Add( '------------- ---- ---------- ---------- ---------- -------- -------------'); + if OKTerms[1]and OKTerms[8] then + lReport.Add('Among Rows %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1, SSF1, MSF1, FF1, ProbF1, OmegaF1]) + else + lReport.Add('Among Rows %4.0f %10.3f %10.3f --- error ---', [DFF1, SSF1, MSF1 ]); + + if OKTerms[2] and OKTerms[9] then + lReport.Add('Among Columns %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF2, SSF2, MSF2, FF2, ProbF2, OmegaF2]) + else + lReport.Add('Among Columns %4.0f %10.3f %10.3f --- error ---', [DFF2, SSF2, MSF2]); + + if OKTerms[3] and OKTerms[10] then + lReport.Add('Among Slices %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF3, SSF3, MSF3, FF3, ProbF3, OmegaF3]) + else + lReport.Add('Among Slices %4.0f %10.3f %10.3f --- error ---', [DFF3, SSF3, MSF3]); + + lReport.Add( 'A x B Inter. %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1F2, SSF1F2, MSF1F2, FF1F2, ProbF1F2, OmegaF1F2]); + lReport.Add( 'A x C Inter. %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1F3, SSF1F3, MSF1F3, FF1F3, ProbF1F3, OmegaF1F3]); + lReport.Add( 'B x C Inter. %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF2F3, SSF2F3, MSF2F3, FF2F3, ProbF2F3, OmegaF2F3]); + lReport.Add( 'AxBxC Inter. %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1F2F3, SSF1F2F3, MSF1F2F3, FF1F2F3, ProbF1F2F3, OmegaF1F2F3]); + lReport.Add( 'Within Groups %4.0f %10.3f %10.3f', [DFErr, SSErr, MSErr]); + lReport.Add( 'Total %4.0f %10.3f %10.3f', [DFTot, SSDep, MSDep]); + lReport.Add( '--------------------------------------------------------------------------------'); + lReport.Add( ''); + lReport.Add( 'Omega squared for combined effects: %.3f', [Omega]); + lReport.Add( ''); + if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) and (Fact3Combo.ItemIndex = 0) then + lReport.Add('Note: MSErr denominator for all F ratios.') + else + if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 1) and (Fact3Combo.ItemIndex = 1) then begin - cellvars[i] := (cellvars[i] - sqr(cellsums[i]) /cellcnts[i]) / (cellcnts[i] - 1); - SSF1 := SSF1 + (sqr(cellsums[i]) / cellcnts[i]); - DFF1 := DFF1 + 1; + lReport.Add('Note: Error term for A is MSAxB + MSAxC - MSAxBxC'); + lReport.Add('Error term for B is MSAxB + MSBxC - MSAxBxC'); + lReport.Add('Error term for C is MSAxC + MSBxC - MSAxBxC'); + lReport.Add('Error term for AxB, AxC and BxC is MSAxBxC'); + lReport.Add('Error term for AxBxC is MSErr.'); + end else + if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 0) and (Fact3Combo.ItemIndex = 0) then + begin + lReport.Add('Note: Error term for A is MSErr'); + lReport.Add('Note: Error term for B is MSAxB'); + lReport.Add('Note: Error term for C is MSAxC'); + lReport.Add('Note: Error term for AxB is MSErr'); + lReport.Add('Note: Error term for AxC is MSErr'); + lReport.Add('Note: Error term for BxC is MSAxBxC'); + lReport.Add('Note: Error term for AxBxC is MSErr'); + end else + if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 1) and (Fact3Combo.ItemIndex = 0) then + begin + lReport.Add('Note: Error term for A is MSAxB'); + lReport.Add('Note: Error term for B is MSErr'); + lReport.Add('Note: Error term for C is MSBxC'); + lReport.Add('Note: Error term for AxB is MSErr'); + lReport.Add('Note: Error term for AxC is MSAxBxC'); + lReport.Add('Note: Error term for BxC is MSErr'); + lReport.Add('Note: Error term for AxBxC is MSErr'); + end else + if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 0) and (Fact3Combo.ItemIndex = 1) then + begin + lReport.Add('Note: Error term for A is MSAxC'); + lReport.Add('Note: Error term for B is MSBxC'); + lReport.Add('Note: Error term for C is MSErr'); + lReport.Add('Note: Error term for AxB is MSAxBxC'); + lReport.Add('Note: Error term for AxC is MSErr'); + lReport.Add('Note: Error term for BxC is MSErr'); + lReport.Add('Note: Error term for AxBxC is MSErr'); + end else + if (Fact1Combo.ItemIndex = 0) and (Fact2Combo.ItemIndex = 1) and (Fact3Combo.ItemIndex = 1) then + begin + lReport.Add('Note: Error term for A is MSAxC + MSAxB - MSAxBxC'); + lReport.Add('Note: Error term for B is MSBxC'); + lReport.Add('Note: Error term for C is MSBxC'); + lReport.Add('Note: Error term for AxB is MSAxBxC'); + lReport.Add('Note: Error term for AxC is MSAxBxC'); + lReport.Add('Note: Error term for BxC is MSErr'); + lReport.Add('Note: Error term for AxBxC is MSErr'); + end else + if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 0) and (Fact3Combo.ItemIndex = 1) then + begin + lReport.Add('Note: Error term for A is MSAxC'); + lReport.Add('Note: Error term for B is MSBxC + MSAxB - MSAxBxC'); + lReport.Add('Note: Error term for C is MSAxC'); + lReport.Add('Note: Error term for AxB is MSAxBxC'); + lReport.Add('Note: Error term for AxC is MSErr'); + lReport.Add('Note: Error term for BxC is MSAxBxC'); + lReport.Add('Note: Error term for AxBxC is MSErr'); + end else + if (Fact1Combo.ItemIndex = 1) and (Fact2Combo.ItemIndex = 1) and (Fact3Combo.ItemIndex = 0) then + begin + lReport.Add('Note: Error term for A is MSAxB'); + lReport.Add('Note: Error term for B is MSAxB'); + lReport.Add('Note: Error term for C is MSBxC + MSAxC - MSAxBxC'); + lReport.Add('Note: Error term for AxB is MSErr'); + lReport.Add('Note: Error term for AxC is MSAxBxC'); + lReport.Add('Note: Error term for BxC is MSAxBxC'); + lReport.Add('Note: Error term for AxBxC is MSErr'); end; + + for i := 1 to 10 do + if not OKTerms[i] then + problem := true; + + if problem then + begin + lReport.Add(''); + lReport.Add('An error occurred due to either an estimate of MS being negative'); + lReport.Add('or the degrees of freedom being zero. This may occur in a design'); + lReport.Add('with random factors using the expected values for an exact F-test.'); + lReport.Add('Quasi-F statistics may be employed where this problem exists. See'); + lReport.Add('Winer, B.J., "Statistical Principles in Experimental Design, 1962'); + lReport.Add('Section 5.15, pages 199-202 and Glass, G.V. and Stanley, J.C.,'); + lReport.Add('1970, Section 18.10, pages 481-482.'); + end; + + lReport.Add(''); + lReport.Add(DIVIDER_AUTO); + lReport.Add(''); + lReport.Add('DESCRIPTIVE STATISTICS'); + lReport.Add(''); + lReport.Add('-------------------------------------------------------------'); + lReport.Add('GROUP ROW COL SLICE N MEAN VARIANCE STD.DEV.'); + lReport.Add('----- --- --- ----- ----- -------- ---------- --------'); + + // Display cell means, variances, standard deviations + groupsize := NCnt[1, 1, 1]; + equal_grp := true; + MaxVar := -1E308; + MinVar := 1E308; + sumVars := 0.0; + sumFreqLogVar := 0.0; + sumDFRecip := 0.0; + for i := 0 to NoGrpsA-1 do + begin + for j := 0 to NoGrpsB-1 do + begin + for k := 0 to NoGrpsC-1 do + begin + XBar := WSum[i, j, k] / NCnt[i, j, k]; + V := (WX2[i, j, k] - sqr(WSum[i, j, k]) / NCnt[i, j, k]) / (NCnt[i, j, k] - 1); + S := sqrt(V); + sumVars := sumVars + V; + if V > MaxVar then MaxVar := V; + if V < MinVar then MinVar := V; + sumDFRecip := sumDFRecip + (1.0 / (NCnt[i, j, k] - 1)); + sumfreqLogVar := sumFreqLogVar + (NCnt[i, j, k] - 1) * ln(V); + if NCnt[i, j, k] <> groupsize then equal_grp := false; + lReport.Add('Cell %3d %3d %5d %5d %8.3f %10.3f %8.3f', [MinF1+i, MinF2+j, MinF3+k, NCnt[i, j, k], XBar, V, S]); + end; + end; + end; + + //Display Row means, variances, standard deviations + for i := 0 to NoGrpsA-1 do + begin + XBar := RowSums[i] / RowCount[i]; + RowSS := 0.0; + for j := 0 to NoGrpsB-1 do + for k := 0 to NoGrpsC-1 do RowSS := RowSS + WX2[i, j, k]; + V := (RowSS - sqr(RowSums[i]) / RowCount[i]) / (RowCount[i] - 1); + S := sqrt(V); + lReport.Add('Row %3d %5d %8.3f %10.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]; + ColSS := 0.0; + for i := 0 to NoGrpsA-1 do + for k := 0 to NoGrpsC-1 do ColSS := ColSS + WX2[i, j, k]; + V := (ColSS - sqr(ColSums[j]) / ColCount[j]) / (ColCount[j] - 1); + S := sqrt(V); + lReport.Add('Col %3d %5d %8.3f %10.3f %8.3f', [MinF2+j, ColCount[j], xBar, V, S]); + end; + + //Display means, variances and standard deviations for slices + for k := 0 to NoGrpsC-1 do + begin + XBar := SlcSums[k] / SlcCount[k]; + SlcSS := 0.0; + for i := 0 to NoGrpsA-1 do + for j := 0 to NoGrpsB-1 do SlcSS := SlcSS + WX2[i, j, k]; + V := (SlcSS - sqr(SlcSums[k]) / SlcCount[k]) / (SlcCount[k] - 1); + S := sqrt(V); + lReport.Add('Slice %5d %5d %8.3f %10.3f %8.3f', [MinF3+k, SlcCount[k], xBar, V,S]); + end; + + lReport.Add('-------------------------------------------------------------'); + lReport.Add('TOTAL %5d %8.3f %10.3f %8.3f', [N, MeanDep, MSDep, sqrt(MSDep)]); + lReport.Add('-------------------------------------------------------------'); + lReport.Add(''); + + c := 1.0 + (1.0 / (3.0 * NoGrpsA * NoGrpsB * NoGrpsC - 1)) * (sumDFrecip - (1.0 / DFErr)); + bartlett := (2.303 / c) * ((DFErr * ln(MSErr)) - SumFreqLogVar); + chiProb := ChiSquaredProb(bartlett, round(NoGrpsA * NoGrpsB * NoGrpsC - 1)); + cochran := MaxVar / SumVars; + hartley := MaxVar / MinVar; + + lReport.Add(DIVIDER); + lReport.Add(''); + lReport.Add('TESTS FOR HOMOGENEITY OF VARIANCE'); + lReport.Add(''); + lReport.Add('Hartley Fmax test statistic: %8.3f with %d and %d degrees of freedom.', [hartley, NoGrpsA*NoGrpsB, groupsize-1]); + lReport.Add('Cochran C statistic: %8.3f with %d and %d degrees of freedom.', [cochran, NoGrpsA*NoGrpsB, groupsize-1]); + lReport.Add('Bartlett Chi-square statistic: %8.3f with %d degrees of freedom; probability > value: %.3f', [bartlett, NoGrpsA*NoGrpsB-1, 1-chiProb]); + + FReportFrame.DisplayReport(lReport); + finally + lReport.Free; end; +end; - SSF1 := SSF1 - (sqr(MeanDep) / N); - SSDep := SSDep - (sqr(MeanDep) / N); - SSErr := SSDep - SSF1; - DFTot := N - 1; - DFF1 := DFF1 - 1; - DFErr := DFTot - DFF1; - MSF1 := SSF1 / DFF1; - MSErr := SSErr / DFErr; - MSDep := SSDep / DFTot; - Omega := (SSF1 - DFF1 * MSErr) / (SSDep + MSErr); - F := MSF1 / MSErr; - ProbF1 := probf(F,DFF1,DFErr); - MeanDep := MeanDep / N; - AReport.Add(''); - AReport.Add(''); - AReport.Add(DIVIDER); - AReport.Add('BROWN-FORSYTHE ONE WAY ANALYSIS OF VARIANCE RESULTS'); - AReport.Add(''); - AReport.Add('Dependent variable is: %s, Independent variable is: %s', [DepVarEdit.Text, Factor1Edit.Text]); - AReport.Add(''); - AReport.Add('Traditional One-Way ANOVA Results'); - AReport.Add(DIVIDER_SMALL); - AReport.Add('SOURCE D.F. SS MS F PROB.>F OMEGA SQR.'); - AReport.Add('-----------------------------------------------------------------------'); - AReport.Add('BETWEEN %4.0f%10.2f%10.2f%10.2f%10.2f%10.2f', [DFF1, SSF1, MSF1, F, ProbF1, Omega]); - AReport.Add('WITHIN %4.0f%10.2f%10.2f', [DFErr, SSErr, MSErr]); - AReport.Add('TOTAL %4.0f%10.2f', [DFTot, SSDep]); - AReport.Add(DIVIDER); - - sumc1 := 0.0; - MSErr := 0.0; - for i := 0 to Nf1cells-1 do +procedure TBlksAnovaForm.TwoWayContrasts; +var + lReport: TStrings; + i, j: integer; + value: double; + variances: DblDyneVec = nil; + RowSS, ColSS: double; + totalCells: Integer; + allAlpha, posthocAlpha: Double; +begin + if not (ScheffeChk.Checked or TukeyHSDChk.Checked or TukeyBChk.Checked or + TukeyKramerChk.Checked or NewmanKeulsChk.Checked or + BonferroniChk.Checked or OrthoContrastsChk.Checked) then begin - c1[i+1] := (1.0 - (cellcnts[i] / N)) * cellvars[i]; - sumc1 := sumc1 + c1[i+1]; + PostHocPage.TabVisible := false; + exit; end; - for i := 1 to Nf1cells do - c1[i] := c1[i] / sumc1; - fdegfree := 0.0; - for i := 1 to Nf1cells do - fdegfree := fdegfree + ((c1[i] * c1[i]) / (cellcnts[i-1]-1.0)); - fdegfree := round(1.0 / fdegfree); + allAlpha := StrToFloat(OverallAlphaEdit.Text); + posthocAlpha := StrToFloat(PostAlphaEdit.Text); - Fnumerator := 0.0; - Fdenominator := 0.0; - for i := 1 to Nf1cells do + 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 - sqr(RowSums[i]) / RowCount[i]) / (RowCount[i] - 1); + 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 - sqr(ColSums[j]) / ColCount[j]) / (ColCount[j] - 1); + 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 + 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, 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; + 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.TwoWayPlot; +begin + if not ShowPlotsChk.Checked then begin - cellmeans[i] := cellsums[i-1] / cellcnts[i-1]; - FNumerator := FNumerator + (cellcnts[i-1] * (sqr(cellmeans[i] - MeanDep))); - FDenominator := FDenominator + ((1.0 - (cellcnts[i-1] / N)) * cellvars[i-1]); + ChartPage.TabVisible := false; + exit; end; - NewF := FNumerator / FDenominator; - ProbF1 := probf(NewF,DFF1, fdegfree); - AReport.Add(''); - AReport.Add(DIVIDER); - AReport.Add('Brown-Forsythe F statistic: %8.3f', [NewF]); - AReport.Add('Brown-Forsythe denominator degrees of freedom: %8.0f', [fdegfree]); - AReport.Add('Brown-Forsythe F probability: %8.3f', [probf1]); - AReport.Add(DIVIDER); + 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; - WelchtTests(AReport); + FChartCombobox.Parent.Left := 0; + FChartCombobox.Parent.Visible := true; + PopulateChartCombobox(false); + FChartCombobox.OnChange := @SelectTwoWayPlot; + SelectTwoWayPlot(nil); + + ChartPage.TabVisible := true; +end; + + +procedure TBlksAnovaForm.TwoWayTable; +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 + lReport := TStringList.Create; + try + lReport.Add('TWO-WAY ANALYSIS OF VARIANCE'); + lReport.Add(''); + lReport.Add('Variable analyzed: %s', [DepVarEdit.Text]); + 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(''); + + lReport.Add( '--------------------------------------------------------------------------------'); + lReport.Add( 'SOURCE D.F. SS MS F PROB.> F Omega Squared'); + lReport.Add( '------------- ---- ---------- ---------- ---------- -------- -------------'); + lReport.Add( 'Among Rows %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1, SSF1, MSF1, FF1, ProbF1, OmegaF1]); + lReport.Add( 'Among Columns %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF2, SSF2, MSF2, FF2, ProbF2, OmegaF2]); + lReport.Add( 'Interaction %4.0f %10.3f %10.3f %10.3f %8.3f %10.3f', [DFF1F2, SSF1F2, MSF1F2, FF1F2, ProbF1F2, OmegaF1F2]); + lReport.Add( 'Within Groups %4.0f %10.3f %10.3f', [DFErr, SSErr, MSErr]); + lReport.Add( 'Total %4.0f %10.3f %10.3f', [DFTot, SSDep, MSDep]); + lReport.Add( '--------------------------------------------------------------------------------'); + 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(DIVIDER_AUTO); + lReport.Add(''); + lReport.Add('DESCRIPTIVE STATISTICS'); +// lReport.Add(''); + lReport.Add('------------------------------------------------------'); + lReport.Add('GROUP Row Col. N MEAN VARIANCE STD.DEV.'); + lReport.Add('----- --- ---- ----- --------- -------- --------'); + 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)); + sumfreqlogvar := sumfreqlogvar + ((counts[i,j] - 1) * ln(V)); + if counts[i,j] <> groupsize then equal_grp := false; + end; + lReport.Add('Cell %3d %3d %4d %9.3f %8.3f %8.3f', [MinF1+i, MinF2+j, counts[i,j], XBar, V, S]); + end; + end; + + //Display Row means, variances, standard deviations + for i := 0 to NoGrpsA-1 do + begin + XBar := RowSums[i] / RowCount[i]; + 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); + S := sqrt(V); + lReport.Add('Row %3d %4d %9.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]; + 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); + if V > 0.0 then S := sqrt(V); + lReport.Add('Col %3d %4d %9.3f %8.3f %8.3f', [minf2+j, ColCount[j], XBar, V, S]); + end; + + lReport.Add('------------------------------------------------------'); + lReport.Add('TOTAL %4d %9.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_AUTO); + lReport.Add(''); + lReport.Add('TESTS FOR HOMOGENEITY OF VARIANCE'); + lReport.Add(''); //DIVIDER_SMALL_AUTO); + lReport.Add('Hartley Fmax test statistic: %8.3f with %d and %d degrees of freedom.', [hartley, NoGrpsA*NoGrpsB, groupsize-1]); + lReport.Add('Cochran C statistic: %8.3f with %d and %d degrees of freedom.', [cochran, NoGrpsA*NoGrpsB, groupsize - 1]); + lReport.Add('Bartlett Chi-square statistic: %8.3f with %d degrees of freedom; prob. > value: %.3f', [bartlett, NoGrpsA*NoGrpsB - 1, chiprob]); +// lReport.Add(DIVIDER_AUTO); + + FReportFrame.DisplayReport(lReport); + finally + lReport.Free; + end; +end; + + +procedure TBlksAnovaForm.UpdateBtnStates; +begin + inherited; + + if FPosthocReportFrame <> nil then + FPostHocReportFrame.UpdateBtnStates; + + DepIn.Enabled := (VarList.ItemIndex > -1) and (DepVarEdit.Text = ''); + DepOut.Enabled := DepVarEdit.Text <> ''; + + Fact1In.Enabled := (VarList.ItemIndex > -1) and (Factor1Edit.Text = ''); + Fact1Out.Enabled := Factor1Edit.Text <> ''; + + Fact2In.Enabled := (VarList.ItemIndex > -1) and (Factor2Edit.Text = ''); + Fact2Out.Enabled := Factor2Edit.Text <> ''; + + Fact3In.Enabled := (VarList.ItemIndex > -1) and (Factor3Edit.Text = ''); + Fact3Out.Enabled := Factor3Edit.Text <> ''; +end; + + +function TBlksAnovaForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; +var + a: Double; +begin + Result := false; + + if DepVarEdit.Text = '' then + begin + AMsg := 'Dependent variable not specified.'; + AControl := VarList; + exit; + end; + + if Factor1Edit.Text = '' then + begin + AMsg := 'Factor 1 variable not specified.'; + AControl := VarList; + exit; + end; + + if (Factor2Edit.Text = '') and (Factor3Edit.Text <> '') then + begin + AMsg := 'A two-way ANOVA needs factors 1 and 2 (not 3), a three-way analysis needs all factors.'; + AControl := VarList; + exit; + end; + + if OverallAlphaEdit.Text = '' then + begin + AMsg := 'Overall alpha level not specified.'; + AControl := OverallAlphaEdit; + exit; + end; + if not TryStrToFloat(OverallAlphaEdit.Text, a) or (a <= 0) or (a >= 1) then + begin + AMsg := 'Overall alpha level is not a valid number between 0 and 1.'; + AControl := OverallAlphaEdit; + exit; + end; + + if PostAlphaEdit.Text = '' then + begin + AMsg := 'Post-hoc alpha level not specified.'; + AControl := PostAlphaEdit; + exit; + end; + if not TryStrToFloat(PostAlphaEdit.Text, a) or (a <= 0) or (a >= 1) then + begin + AMsg := 'Post-hoc alpha level is not a valid number between 0 and 1.'; + AControl := PostAlphaEdit; + exit; + end; + + Result := true; +end; + + +procedure TBlksAnovaForm.VarChange(Sender: TObject); +begin + UpdateBtnStates; +end; + + +procedure TBlksAnovaForm.VarListDblClick(Sender: TObject); +var + index: Integer; + s: String; +begin + index := VarList.ItemIndex; + if index > -1 then + begin + s := VarList.Items[index]; + if DepVarEdit.Text = '' then + DepVarEdit.Text := s + else if Factor1Edit.Text = '' then + Factor1Edit.Text := s + else if Factor2Edit.Text = '' then + Factor2Edit.Text := s + else if Factor3Edit.Text = '' then + Factor3Edit.Text := s; + VarList.Items.Delete(index); + UpdateBtnStates; + end; +end; + + +procedure TBlksAnovaForm.VarListSelectionChange(Sender: TObject; User: boolean); +begin + UpdateBtnStates; end; @@ -3363,13 +3114,13 @@ end; procedure TBlksAnovaForm.WelchtTests(AReport: TStrings); var i, j, NoCompares: integer; - t: double; // Welch t value - gnu: double; // degrees of freedom - var1, var2: double; // variance estimates for two variables + t: double; // Welch t value + gnu: double; // degrees of freedom + var1, var2: double; // variance estimates for two variables mean1, mean2: double; // means for two variables - probability: double; // t probability + probability: double; // t probability numerator, denominator, term1, term2: double; // work values - v: integer; // rounded degrees of freedom + v: integer; // rounded degrees of freedom begin NoCompares := Nf1cells; @@ -3413,112 +3164,5 @@ begin end; -procedure TBlksAnovaForm.UpdateBtnStates; -begin - inherited; - - if FPosthocReportFrame <> nil then - FPostHocReportFrame.UpdateBtnStates; - - DepIn.Enabled := (VarList.ItemIndex > -1) and (DepVarEdit.Text = ''); - DepOut.Enabled := DepVarEdit.Text <> ''; - - Fact1In.Enabled := (VarList.ItemIndex > -1) and (Factor1Edit.Text = ''); - Fact1Out.Enabled := Factor1Edit.Text <> ''; - - Fact2In.Enabled := (VarList.ItemIndex > -1) and (Factor2Edit.Text = ''); - Fact2Out.Enabled := Factor2Edit.Text <> ''; - - Fact3In.Enabled := (VarList.ItemIndex > -1) and (Factor3Edit.Text = ''); - Fact3Out.Enabled := Factor3Edit.Text <> ''; -end; - - -function TBlksAnovaForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; -var - a: Double; -begin - Result := false; - - if DepVarEdit.Text = '' then - begin - AMsg := 'Dependent variable not specified.'; - AControl := VarList; - exit; - end; - - if Factor1Edit.Text = '' then - begin - AMsg := 'Factor 1 variable not specified.'; - AControl := VarList; - exit; - end; - - if (Factor2Edit.Text = '') and (Factor3Edit.Text <> '') then - begin - AMsg := 'A two-way ANOVA needs factors 1 and 2 (not 3), a three-way analysis needs all factors.'; - AControl := VarList; - exit; - end; - - if OverallAlphaEdit.Text = '' then - begin - AMsg := 'Overall alpha level not specified.'; - AControl := OverallAlphaEdit; - exit; - end; - if not TryStrToFloat(OverallAlphaEdit.Text, a) or (a <= 0) or (a >= 1) then - begin - AMsg := 'Overall alpha level is not a valid number between 0 and 1.'; - AControl := OverallAlphaEdit; - exit; - end; - - if PostAlphaEdit.Text = '' then - begin - AMsg := 'Post-hoc alpha level not specified.'; - AControl := PostAlphaEdit; - exit; - end; - if not TryStrToFloat(PostAlphaEdit.Text, a) or (a <= 0) or (a >= 1) then - begin - AMsg := 'Post-hoc alpha level is not a valid number between 0 and 1.'; - AControl := PostAlphaEdit; - exit; - end; - - Result := true; -end; - - -procedure TBlksAnovaForm.VarListDblClick(Sender: TObject); -var - index: Integer; - s: String; -begin - index := VarList.ItemIndex; - if index > -1 then - begin - s := VarList.Items[index]; - if DepVarEdit.Text = '' then - DepVarEdit.Text := s - else if Factor1Edit.Text = '' then - Factor1Edit.Text := s - else if Factor2Edit.Text = '' then - Factor2Edit.Text := s - else if Factor3Edit.Text = '' then - Factor3Edit.Text := s; - VarList.Items.Delete(index); - UpdateBtnStates; - end; -end; - - -procedure TBlksAnovaForm.VarListSelectionChange(Sender: TObject; User: boolean); -begin - UpdateBtnStates; -end; - - end.