{ Test file: ABCNested.laz (imported from OpenStat sample data zip file) Dependent: Dep Factor A : A Factor B : B Factor C : C } unit ABCNestedUnit; {$mode objfpc}{$H+} {$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined} interface uses Classes, SysUtils, FileUtil, TAStyles, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons, ExtCtrls, ComCtrls, LCLVersion, TACustomSeries, MainUnit, Globals, ReportFrameUnit, BasicStatsReportAndChartFormUnit; type { TABCNestedForm } TABCNestedForm = class(TBasicStatsReportAndChartForm) ChartStyles: TChartStyles; ShowPlotsChk: TCheckBox; Plot3DChk: TCheckBox; FactorCEdit: TEdit; FactorAEdit: TEdit; AInBtn: TBitBtn; AOutBtn: TBitBtn; FactorBEdit: TEdit; BInBtn: TBitBtn; BOutBtn: TBitBtn; DepEdit: TEdit; DepInBtn: TBitBtn; CInBtn: TBitBtn; DepOutBtn: TBitBtn; COutBtn: TBitBtn; PlotOptionsGroup: TGroupBox; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Label5: TLabel; Memo1: TLabel; Panel1: TPanel; MeansPage: TTabSheet; VarList: TListBox; procedure AInBtnClick(Sender: TObject); procedure AOutBtnClick(Sender: TObject); procedure BInBtnClick(Sender: TObject); procedure BOutBtnClick(Sender: TObject); procedure CInBtnClick(Sender: TObject); procedure COutBtnClick(Sender: TObject); procedure DepInBtnClick(Sender: TObject); procedure DepOutBtnClick(Sender: TObject); procedure Plot3DChkChange(Sender: TObject); procedure ShowPlotsChkChange(Sender: TObject); procedure VarListDblClick(Sender: TObject); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); private CellCount: IntDyneCube; ASS, BSS, CSS, ASumSqr, BSumSqr, CSumSqr, ASDs, BSDs, CSDs : DblDyneVec; ACSS,ACSumSqr, ACSDs, ABSS, ABSumSqr, ABSDs : DblDyneMat; ACount, BCount, CCount : IntDyneVec; ACCount, ABCount : IntDyneMat; CellSDs, SS, SumSqr, CellMeans : DblDyneCube; MinA, MaxA, NoALevels: Integer; MinB, MaxB, NoBLevels: Integer; MinC, MaxC, NoCLevels: integer; SSTot, SumSqrTot, TotMean, MSTot, SSA, MSA, SSB, MSB, SSW, MSW : double; SSC, MSC, SSAC, MSAC, SSBwAC, SSAB, MSBwAC : double; TotN, dfA, dfBwA, dfwcell, dftotal, dfC, dfAC, dfBwAC : integer; AMeans, BMeans, CMeans: DblDyneVec; ABMeans, ACMeans: DblDyneMat; function GetVariables(out AValues, BValues, CValues, DepValues: DblDyneVec): Boolean; procedure GetMemory; procedure GetSums(const AValues, BValues, CValues, DepValues: DblDyneVec); procedure ShowMeans; procedure GetResults; procedure ShowResults; procedure ReleaseMemory; procedure TwoWayPlot; private FMeansReportFrame: TReportFrame; FChartCombobox: TCombobox; FSeries: TChartSeries; procedure PopulateChartCombobox; procedure SelectPlot(Sender: TObject); protected procedure AdjustConstraints; override; procedure Compute; override; procedure UpdateBtnStates; override; function Validate(out AMsg: String; out AControl: TWinControl): Boolean; override; public constructor Create(AOwner: TComponent); override; procedure Reset; override; end; var ABCNestedForm: TABCNestedForm; implementation {$R *.lfm} uses Math, TAChartUtils, TACustomSource, TALegend, TASeries, Utils, MathUnit, MatrixUnit, GridProcs, ChartFrameUnit; { TABCNestedForm } constructor TABCNestedForm.Create(AOwner: TComponent); begin inherited; FMeansReportFrame := TReportFrame.Create(MeansPage); FMeansReportFrame.Parent := MeansPage; FMeansReportFrame.Align := alClient; InitToolbar(FMeansReportFrame.ReportToolbar, tpTop); MeansPage.PageIndex := 1; FChartFrame.Chart.Margins.Bottom := 0; FChartFrame.Chart.BottomAxis.AxisPen.Visible := true; FChartFrame.Chart.BottomAxis.ZPosition := 1; FChartFrame.Chart.BottomAxis.Grid.Visible := false; AddComboboxToToolbar(FChartFrame.ChartToolbar, 'Plots:', FChartCombobox); FChartCombobox.OnSelect := @SelectPlot; PageControl.ActivePageIndex := 0; end; procedure TABCNestedForm.AdjustConstraints; begin inherited; ParamsPanel.Constraints.MinWidth := Max( 4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left, MaxValue([Label2.Width, Label3.Width, Label4.Width, Label5.Width])*2 + AInBtn.Width + VarList.BorderSpacing.Right*2 ); ParamsPanel.Constraints.MinHeight := COutBtn.Top + COutBtn.Height + ButtonBevel.Height + CloseBtn.Height + CloseBtn.BorderSpacing.Top; end; procedure TABCNestedForm.AInBtnClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (FactorAEdit.Text = '') then begin FactorAEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); end; UpdateBtnStates; end; procedure TABCNestedForm.AOutBtnClick(Sender: TObject); begin if FactorAEdit.Text <> '' then begin VarList.Items.Add(FactorAEdit.Text); FactorAEdit.Text := ''; end; UpdateBtnStates; end; procedure TABCNestedForm.BInBtnClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (FactorBEdit.Text = '') then begin FactorBEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); end; UpdateBtnStates; end; procedure TABCNestedForm.BOutBtnClick(Sender: TObject); begin if FactorBEdit.Text <> '' then begin VarList.Items.Add(FactorBEdit.Text); FactorBEdit.Text := ''; end; UpdateBtnStates; end; procedure TABCNestedForm.CInBtnClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (FactorCEdit.Text = '') then begin FactorCEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); end; UpdateBtnStates; end; procedure TABCNestedForm.COutBtnClick(Sender: TObject); begin if FactorCEdit.Text <> '' then begin VarList.Items.Add(FactorCEdit.Text); FactorCEdit.Text := ''; end; UpdateBtnStates; end; procedure TABCNestedForm.Compute; var dataA: DblDyneVec = nil; dataB: DblDyneVec = nil; dataC: DblDyneVec = nil; dataDep: DblDyneVec = nil; begin if GetVariables(dataA, dataB, dataC, dataDep) then begin GetMemory; GetSums(dataA, dataB, dataC, dataDep); ShowMeans; GetResults; ShowResults; TwoWayPlot; ReleaseMemory; end; end; procedure TABCNestedForm.DepInBtnClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (DepEdit.Text = '') then begin DepEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); end; UpdateBtnStates; end; procedure TABCNestedForm.DepOutBtnClick(Sender: TObject); begin if DepEdit.Text <> '' then begin VarList.Items.Add(DepEdit.Text); DepEdit.Text := ''; end; UpdateBtnStates; end; procedure TABCNestedForm.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 TABCNestedForm.ShowPlotsChkChange(Sender: TObject); begin ChartPage.TabVisible := ShowPlotsChk.Checked; Plot3DChk.Enabled := ShowPlotsChk.Checked; end; procedure TABCNestedForm.PopulateChartCombobox; var a, b, c: String; idx: Integer; begin idx := FChartCombobox.ItemIndex; a := FactorAEdit.Text; b := FactorBEdit.Text; c := FactorCEdit.Text; FChartCombobox.Items.Clear; FChartCombobox.Items.Add(a); FChartCombobox.Items.Add(b); FChartCombobox.Items.Add(c); FChartCombobox.Items.Add(Format('%s x %s', [a, b])); FChartCombobox.Items.Add(Format('%s x %s', [a, c])); FChartCombobox.ItemIndex := EnsureRange(idx, 0, FChartCombobox.Items.Count-1); end; function TABCNestedForm.GetVariables( out AValues, BValues, CValues, DepValues: DblDyneVec): Boolean; var ColNoSelected: IntDyneVec = nil; mn, mx: Double; msg: String; begin Result := false; SetLength(ColNoSelected, 4); ColNoSelected[0] := GetVariableIndex(OS3MainFrm.DataGrid, FactorAEdit.Text); // A ColNoSelected[1] := GetVariableIndex(OS3MainFrm.DataGrid, FactorBEdit.Text); // B ColNoSelected[2] := GetVariableIndex(OS3MainFrm.DataGrid, FactorCEdit.Text); // C ColNoSelected[3] := GetVariableindex(OS3MainFrm.DataGrid, DepEdit.Text); // Dependent AValues := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[0], ColNoSelected); if not CheckFactorCodes(FactorAEdit.Text, AValues, msg) then begin ErrorMsg(msg); exit; end; BValues := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[1], ColNoSelected); if not CheckFactorCodes(FactorBEdit.Text, BValues, msg) then begin ErrorMsg(msg); exit; end; CValues := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[2], ColNoSelected); if not CheckFactorCodes(FactorCEdit.Text, CValues, msg) then begin ErrorMsg(msg); exit; end; DepValues := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[3], ColNoSelected); if (Length(DepValues) <> Length(AValues)) or (Length(DepValues) <> Length(BValues)) or (Length(DepValues) <> Length(CValues)) then begin ErrorMsg('All variables must contain equal amounts of cases.'); exit; end; VecMaxMin(AValues, mx, mn); MaxA := round(mx); MinA := round(mn); VecMaxMin(BValues, mx, mn); MaxB := round(mx); MinB := round(mn); VecMaxMin(CValues, mx, mn); MaxC := round(mx); MinC := round(mn); NoALevels := MaxA - MinA + 1; NoBLevels := MaxB - MinB + 1; NoCLevels := MaxC - MinC + 1; Result := true; end; procedure TABCNestedForm.GetMemory; begin SS := CubeCreate(NoBLevels, NoALevels, NoCLevels); SumSqr := CubeCreate(NoBLevels, NoALevels, NoCLevels); CellCount := IntCubeCreate(NoBLevels, NoALevels, NoCLevels); CellMeans := CubeCreate(NoBLevels, NoALevels, NoCLevels); CellSDs := CubeCreate(NoBLevels, NoALevels, NoCLevels); ASS := VecCreate(NoALevels); BSS := VecCreate(NoBLevels); CSS := VecCreate(NoCLevels); ASumSqr := VecCreate(NoALevels); BSumSqr := VecCreate(NoBLevels); CSumSqr := VecCreate(NoCLevels); AMeans := VecCreate(NoALevels); BMeans := VecCreate(NoBLevels); CMeans := VecCreate(NoCLevels); ACount := IntVecCreate(NoALevels); BCount := IntVecCreate(NoBLevels); CCount := IntVecCreate(NoCLevels); ASDs := VecCreate(NoALevels); BSDs := VecCreate(NoBLevels); CSDs := VecCreate(NoCLevels); ACSS := MatCreate(NoALevels, NoCLevels); ACSumSqr := MatCreate(NoALevels, NoCLevels); ACCount := IntMatCreate(NoALevels, NoCLevels); ACMeans := MatCreate(NoALevels, NoCLevels); ACSDs := MatCreate(NoALevels, NoCLevels); ABSS := MatCreate(NoALevels, NoBLevels); ABSumSqr := MatCreate(NoALevels, NoBLevels); ABMeans := MatCreate(NoALevels, NoBLevels); ABCount := IntMatCreate(NoALevels, NoBLevels); ABSDs := MatCreate(NoALevels,NoBLevels); end; procedure TABCNestedForm.GetSums(const AValues, BValues, CValues, DepValues: DblDyneVec); VAR Aindex, Bindex, Cindex, i, j, k: integer; YValue, YValueSqr: double; begin // Accumulate sums and sums of squared values SSTot := 0.0; SumSqrTot := 0.0; TotN := 0; for i := 0 to High(DepValues) do begin AIndex := round(AValues[i]) - MinA; BIndex := round(BValues[i]) - MinB; CIndex := round(CValues[i]) - MinC; YValue := DepValues[i]; YValueSqr := YValue * YValue; SS[Bindex,Aindex,Cindex] := SS[Bindex,Aindex,Cindex] + YValueSqr; SumSqr[Bindex,Aindex,Cindex] := SumSqr[Bindex,Aindex,Cindex] + YValue; // wp: why no square? CellCount[Bindex,Aindex,Cindex] := CellCount[Bindex,Aindex,Cindex] + 1; ACount[Aindex] := ACount[Aindex] + 1; BCount[Bindex] := BCount[Bindex] + 1; CCount[Cindex] := CCount[Cindex] + 1; ASS[Aindex] := ASS[Aindex] + YValueSqr; BSS[Bindex] := BSS[Bindex] + YValueSqr; CSS[Cindex] := CSS[Cindex] + YValueSqr; ASumSqr[Aindex] := ASumSqr[Aindex] + YValue; BSumSqr[Bindex] := BSumSqr[Bindex] + YValue; CSumSqr[Cindex] := CSumSqr[Cindex] + YValue; ACSS[Aindex,Cindex] := ACSS[Aindex,Cindex] + YValueSqr; ACSumSqr[Aindex,Cindex] := ACSumSqr[Aindex,Cindex] + YValue; ACCount[Aindex,Cindex] := ACCount[Aindex,Cindex] + 1; ABSS[Aindex,Bindex] := ABSS[Aindex,Bindex] + YValueSqr; ABSumSqr[Aindex,Bindex] := ABSumSqr[Aindex,Bindex] + YValue; ABCount[Aindex,Bindex] := ABCount[Aindex,Bindex] + 1; SSTot := SSTot + YValueSqr; SumSqrTot := SumSqrTot + YValue; TotN := TotN + 1; end; // Get cell means and marginal means plus square of sums for i := 0 to NoBLevels-1 do begin for j := 0 to NoALevels-1 do begin for k := 0 to NoCLevels-1 do begin if CellCount[i,j,k] > 0 then begin CellMeans[i,j,k] := SumSqr[i,j,k] / CellCount[i,j,k]; SumSqr[i,j,k] := SumSqr[i,j,k] * SumSqr[i,j,k]; CellSDs[i,j,k] := SS[i,j,k] - (SumSqr[i,j,k] / CellCount[i,j,k]); CellSDs[i,j,k] := CellSDs[i,j,k] / (CellCount[i,j,k] - 1); CellSDs[i,j,k] := sqrt(CellSDs[i,j,k]); end; end; end; end; for i := 0 to NoBLevels-1 do begin if BCount[i] > 0 then begin BMeans[i] := BSumSqr[i] / BCount[i]; BSumSqr[i] := BSumSqr[i] * BSumSqr[i]; BSDs[i] := BSS[i] - (BSumSqr[i] / BCount[i]); BSDs[i] := BSDs[i] / (BCount[i] - 1); BSDs[i] := sqrt(BSDs[i]); end; end; for i := 0 to NoALevels-1 do begin AMeans[i] := ASumSqr[i] / ACount[i]; ASumSqr[i] := ASumSqr[i] * ASumSqr[i]; ASDs[i] := ASS[i] - (ASumSqr[i] / ACount[i]); ASDs[i] := ASDs[i] / (ACount[i] - 1); ASDs[i] := Sqrt(ASDs[i]); end; for i := 0 to NoCLevels-1 do begin CMeans[i] := CSumSqr[i] / CCount[i]; CSumSqr[i] := CSumSqr[i] * CSumSqr[i]; CSDs[i] := CSS[i] - (CSumSqr[i] / CCount[i]); CSDs[i] := CSDs[i] / (CCount[i] - 1); CSDs[i] := sqrt(CSDs[i]); end; for i := 0 to NoALevels-1 do begin for k := 0 to NoCLevels-1 do begin ACMeans[i,k] := ACMeans[i,k] / ACCount[i,k]; ACSumSqr[i,k] := ACSumSqr[i,k] * ACSumSqr[i,k]; ACSDs[i,k] := ACSS[i,k] - (ACSumSqr[i,k] / ACCount[i,k]); ACSDs[i,k] := ACSDs[i,k] / (ACCount[i,k] - 1); ACSDs[i,k] := sqrt(ACSDs[i,k]); end; end; for i := 0 to NoALevels-1 do begin for j := 0 to NoBLevels-1 do begin if ABCount[i,j] > 0 then begin ABMeans[i,j] := ABSumSqr[i,j] / ABCount[i,j]; ABSumSqr[i,j] := ABSumSqr[i,j] * ABSumSqr[i,j]; ABSDs[i,j] := ABSS[i,j] - (ABSumSqr[i,j] / ABCount[i,j]); ABSDs[i,j] := ABSDs[i,j] / (ABCount[i,j] - 1); ABSDs[i,j] := sqrt(ABSDs[i,j]); end; end; end; TotMean := SumSqrTot / TotN; SumSqrTot := SumSqrTot * SumSqrTot; end; procedure TABCNestedForm.ShowMeans; var lReport: TStrings; i, j, k : integer; begin lReport := TStringList.Create; try lReport.Add('Nested ANOVA by Bill Miller'); lReport.Add(''); lReport.Add('File analyzed: %s', [OS3MainFrm.FileNameEdit.Text]); lReport.Add('Factor A: %s', [FactorAEdit.Text]); lReport.Add('Factor B: %s', [FactorBEdit.Text]); lReport.Add('Factor C: %s', [FactorCEdit.Text]); lReport.Add(''); lReport.Add('CELL MEANS'); lReport.Add('-----------------------------------------------------'); lReport.Add('A LEVEL B LEVEL C LEVEL MEAN STD.DEV. '); lReport.Add('------- ------- ------- ---------- ----------'); for i := 0 to NoALevels-1 do for j := 0 to NoBLevels-1 do for k := 0 to NoCLevels-1 do if CellCount[j,i,k] > 0 then lReport.Add('%5d %5d %5d %10.3f %10.3f', [i+MinA, j+MinB, k+MinC, CellMeans[j,i,k], CellSDs[j,i,k]]); lReport.Add('-----------------------------------------------------'); lReport.Add(''); lReport.Add('A MARGIN MEANS'); lReport.Add('---------------------------------'); lReport.Add('A LEVEL MEAN STD.DEV. '); lReport.Add('------- ---------- ----------'); for i := 0 to NoALevels-1 do lReport.Add('%5d %10.3f %10.3f', [i+MinA, AMeans[i], ASDs[i]]); lReport.Add('---------------------------------'); lReport.Add(''); lReport.Add('B MARGIN MEANS'); lReport.Add('---------------------------------'); lReport.Add('B LEVEL MEAN STD.DEV. '); lReport.Add('------- ---------- ----------'); for i := 0 to NoBLevels-1 do if BCount[i] > 0 then lReport.Add('%5d %10.3f %10.3f', [i+MinB, BMeans[i], BSDs[i]]); lReport.Add('---------------------------------'); lReport.Add(''); lReport.Add('C MARGIN MEANS'); lReport.Add('---------------------------------'); lReport.Add('C LEVEL MEAN STD.DEV. '); lReport.Add('------- ---------- ----------'); for i := 0 to NoCLevels-1 do if CCount[i] > 0 then lReport.Add('%5d %10.3f %10.3f', [i+MinC, CMeans[i], CSDs[i]]); lReport.Add('---------------------------------'); lReport.Add(''); lReport.Add('AB MARGIN MEANS'); lReport.Add('-------------------------------------------'); lReport.Add('A LEVEL B LEVEL MEAN STD.DEV. '); lReport.Add('------- ------- ---------- ----------'); for i := 0 to NoALevels-1 do for j := 0 to NoBLevels-1 do if ABCount[i,j] > 0 then lReport.Add('%5d %5d %10.3f %10.3f', [i+MinA, j+MinB, ABMeans[i,j], ABSDs[i,j]]); lReport.Add('-------------------------------------------'); lReport.Add(''); lReport.Add('AC MARGIN MEANS'); lReport.Add('-------------------------------------------'); lReport.Add('A LEVEL C LEVEL MEAN STD.DEV. '); lReport.Add('------- ------- ---------- ----------'); for i := 0 to NoALevels-1 do for j := 0 to NoCLevels-1 do if ACCount[i,j] > 0 then lReport.Add('%5d %5d %10.3f %10.3f',[i+MinA, j+MinC, ACMeans[i,j], ACSDs[i,j]]); lReport.Add('-------------------------------------------'); lReport.Add(''); lReport.Add('GRAND MEAN: %10.3f', [TotMean]); lReport.Add(''); FMeansReportFrame.DisplayReport(lReport); finally lReport.Free; end; end; procedure TABCNestedForm.GetResults; VAR temp, temp2, temp3, temp4, constant : double; NoBLevelsInA, BLevCount, i, j, k, celln : integer; begin celln := 0; for i := 0 to NoALevels-1 do begin for j := 0 to NoBLevels-1 do begin for k := 0 to NoCLevels-1 do begin if CellCount[j,i,k] > celln then celln := CellCount[j,i,k]; end; end; end; // Assume all cells have same n size // Get number of levels in A BLevCount := 0; for i := 0 to NoALevels-1 do begin NoBLevelsInA := 0; for j := 0 to NoBLevels-1 do begin if CellCount[j,i,0] > 0 then NoBLevelsInA := NoBLevelsInA + 1; end; if NoBLevelsInA > BLevCount then BLevCount := NoBLevelsInA; end; dfA := NoALevels - 1; dfBwA := NoALevels * (BLevCount - 1); dfC := NoCLevels - 1; dfAC := (NoALevels-1) * (NoCLevels-1); dfBwAC := NoALevels * (BLevCount-1) * (NoCLevels -1); dfwcell := NoALevels * BLevCount * NoCLevels * (celln - 1); dftotal := TotN - 1; constant := SumSqrTot / TotN; SSTot := SSTot - constant; MSTot := SSTot / dftotal; // Get A Effects SSA := 0.0; for i := 0 to NoALevels-1 do SSA := SSA + (ASumSqr[i] / ACount[i]); temp := SSA; SSA := SSA - constant; MSA := SSA / dfA; // Get C Effects SSC := 0.0; for i := 0 to NoCLevels-1 do SSC := SSC + (CSumSqr[i] / CCount[i]); temp2 := SSC; SSC := SSC - constant; MSC := SSC / dfC; // Get B within A SSB := 0.0; for i := 0 to NoALevels - 1 do begin for j := 0 to NoBLevels-1 do begin if ABCount[i,j] > 0 then SSB := SSB + (ABSumSqr[i,j] / ABCount[i,j]); end; end; temp3 := SSB; SSB := SSB - temp; MSB := SSB / dfBwA; // Get AC interaction SSAC := 0.0; for i := 0 to NoALevels-1 do begin for j := 0 to NoCLevels-1 do SSAC := SSAC + ACSumSqr[i,j] / ACCount[i,j] end; temp4 := SSAC; SSAC := SSAC - temp - temp2 + constant; MSAC := SSAC / dfAC; // get B within A x C interaction SSBwAC := 0.0; for i := 0 to NoALevels-1 do begin for j := 0 to NoBLevels-1 do begin for k := 0 to NoCLevels-1 do begin if CellCount[j,i,k] > 0 then SSBwAC := SSBwAC + (SumSqr[j,i,k] / CellCount[j,i,k]); end; end; end; SSBwAC := SSBwAC - temp3 - temp4 + temp; MSBwAC := SSBwAC / dfBwAC; SSW := SSTot - SSA - SSB - SSAB - SSAC - SSBwAC; MSW := SSW / dfwcell; end; procedure TABCNestedForm.ShowResults; var lReport: TStrings; F, PF : double; begin lReport := TStringList.Create; try lReport.Add('Nested ANOVA by Bill Miller'); lReport.Add(''); lReport.Add('File analyzed: %s', [OS3MainFrm.FileNameEdit.Text]); lReport.Add('Factor A: %s', [FactorAEdit.Text]); lReport.Add('Factor B: %s', [FactorBEdit.Text]); lReport.Add('Factor C: %s', [FactorCEdit.Text]); lReport.Add(''); lReport.Add('ANOVA TABLE'); lReport.Add('-------------------------------------------------------------'); lReport.Add('SOURCE D.F. SS MS F PROB. '); lReport.Add('--------- ---- ---------- ---------- --------- ---------'); F := MSA / MSW; PF := ProbF(F,dfA,dfwcell); lReport.Add('A %4d %10.3f %10.3f %9.3f %9.3f', [dfA, SSA, MSA, F, PF]); F := MSB / MSW; PF := ProbF(F,dfBwA,dfwcell); lReport.Add('B(A) %4d %10.3f %10.3f %9.3f %9.3f', [dfBwA, SSB, MSB, F, PF]); F := MSC / MSW; PF := ProbF(F,dfC,dfwcell); lReport.Add('C %4d %10.3f %10.3f %9.3f %9.3f', [dfC, SSC, MSC, F, PF]); F := MSAC / MSW; PF := ProbF(F,dfAC,dfwcell); lReport.Add('AxC %4d %10.3f %10.3f %9.3f %9.3f', [dfAC, SSAC, MSAC, F, PF]); F := MSBwAC / MSW; PF := ProbF(F,dfBwAC,dfwcell); lReport.Add('B(A)xC %4d %10.3f %10.3f %9.3f %9.3f', [dfBwAC, SSBwAC, MSBwAC, F, PF]); lReport.Add('w.cells %4d %10.3f %10.3f', [dfwcell, SSW, MSW]); lReport.Add('Total %4d %10.3f', [dftotal, SSTot]); lReport.Add('-------------------------------------------------------------'); FReportFrame.DisplayReport(lReport); finally lReport.Free; end; end; procedure TABCNestedForm.ReleaseMemory; begin ABSDs := nil; ABCount := nil; // ABMeans := nil; ABSumSqr := nil; ABSS := nil; ACSDs := nil; // ACMeans := nil; ACCount := nil; ACSumSqr := nil; ACSS := nil; CSDs := nil; BSDs := nil; ASDs := nil; CCount := nil; BCount := nil; ACount := nil; // CMeans := nil; // BMeans := nil; // AMeans := nil; CSumSqr := nil; BSumSqr := nil; ASumSqr := nil; CSS := nil; BSS := nil; ASS := nil; CellSDs := nil; CellMeans := nil; CellCount := nil; SumSqr := nil; SS := nil; end; procedure TABCNestedForm.Reset; var i: integer; begin inherited; if FMeansReportFrame <> nil then FMeansReportFrame.Clear; if FChartCombobox <> nil then FChartCombobox.Items.Clear; VarList.Items.Clear; for i := 1 to NoVariables do VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); FactorAEdit.Clear; FactorBEdit.Clear; FactorCEdit.Clear; DepEdit.Clear; UpdateBtnStates; end; procedure TABCNestedForm.SelectPlot(Sender: TObject); var i, j, k, idx: Integer; item: PChartDataItem; begin ChartStyles.Styles.Clear; FSeries.Clear; case FChartComboBox.ItemIndex of 0: begin // Plot means vs factor A FSeries.ListSource.YCount := 1; for i := 0 to NoALevels-1 do FSeries.AddXY(MinA + i, AMeans[i], IntToStr(MinA + i)); FChartFrame.SetXTitle(FactorAEdit.Text + ' codes'); FChartFrame.SetTitle('Factor ' + FactorAEdit.Text); end; 1: begin // Plot means vs factor B FSeries.ListSource.YCount := 1; for i := 0 to NoBLevels-1 do FSeries.AddXY(MinB + i, BMeans[i], IntToStr(MinB + i)); FChartFrame.SetXTitle(FactorBEdit.Text + ' codes'); FChartFrame.SetTitle('Factor ' + FactorBEdit.Text); end; 2: begin // Plot means vs factor C FSeries.ListSource.YCount := 1; for i := 0 to NoCLevels-1 do FSeries.AddXY(MinC + i, CMeans[i], IntToStr(MinC + i)); FChartFrame.SetXTitle(FactorCEdit.Text + ' codes'); FChartFrame.SetTitle('Factor ' + FactorCEdit.Text); end; 3: begin // Plot interaction AxB FSeries.ListSource.YCount := NoALevels; for j := 0 to NoBLevels-1 do begin idx := FSeries.AddXY(MinB + j, NaN, IntToStr(MinB + j)); item := FSeries.Source.Item[idx]; for i := 0 to NoALevels-1 do item^.SetY(i, ABMeans[i, j]); end; FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"', [ FactorAEdit.Text, FactorBEdit.Text])); FChartFrame.SetXTitle(FactorBEdit.Text + ' codes'); for i := 0 to NoALevels-1 do with TChartStyle(ChartStyles.Styles.Add) do begin Brush.Color := DATA_COLORS[i mod Length(DATA_COLORS)]; UseBrush := true; Text := Format('%s = %s', [FactorAEdit.Text, IntToStr(MinA+i)]); end; end; 4: begin // Plot interaction AxC FSeries.ListSource.YCount := NoALevels; for k := 0 to NoCLevels-1 do begin idx := FSeries.AddXY(MinC + k, NaN, IntToStr(MinC + k)); item := FSeries.Source.Item[idx]; for i := 0 to NoALevels-1 do item^.SetY(i, ACMeans[i, k]); end; FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"', [ FactorAEdit.Text, FactorCEdit.Text])); FChartFrame.SetXTitle(FactorCEdit.Text + ' codes'); for i := 0 to NoALevels-1 do with TChartStyle(ChartStyles.Styles.Add) do begin Brush.Color := DATA_COLORS[i mod Length(DATA_COLORS)]; UseBrush := true; Text := Format('%s = %s', [FactorAEdit.Text, IntToStr(MinA+i)]); end; end; end; if (FSeries is TBarSeries) then begin if ChartStyles.Styles.Count > 0 then begin TBarSeries(FSeries).Styles := ChartStyles; 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 TABCNestedForm.TwoWayPlot; begin if not ShowPlotsChk.Checked then begin ChartPage.TabVisible := false; exit; end; 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 Stacked := false; {$IF LCL_FullVersion >= 2010000} DepthBrightnessDelta := -30; {$IFEND} end; if Plot3DChk.Checked then FSeries.Depth := 20; FChartCombobox.Parent.Left := 0; PopulateChartCombobox; SelectPlot(nil); ChartPage.TabVisible := true; end; procedure TABCNestedForm.UpdateBtnStates; begin inherited; if FMeansReportFrame <> nil then FMeansReportFrame.UpdateBtnStates; AInBtn.Enabled := (VarList.ItemIndex > -1) and (FactorAEdit.Text = ''); BInBtn.Enabled := (VarList.ItemIndex > -1) and (FactorBEdit.Text = ''); CInBtn.Enabled := (VarList.ItemIndex > -1) and (FactorCEdit.Text = ''); DepInBtn.Enabled := (VarList.ItemIndex > -1) and (DepEdit.Text = ''); AOutBtn.Enabled := (FactorAEdit.Text <> ''); BOutBtn.Enabled := (FactorBEdit.Text <> ''); COutBtn.Enabled := (FactorCEdit.Text <> ''); DepOutBtn.Enabled := (DepEdit.Text <> ''); end; function TABCNestedForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; begin Result := false; if DepEdit.Text = '' then begin AMsg := 'Dependent variable not specified.'; AControl := VarList; exit; end; if FactorAEdit.Text = '' then begin AMsg := 'Factor A variable not specified.'; AControl := VarList; exit; end; if FactorBEdit.Text = '' then begin AMsg := 'Factor B variable not specified.'; AControl := VarList; exit; end; if FactorCEdit.Text = '' then begin AMsg := 'Factor C variable not specified.'; AControl := VarList; exit; end; Result := true; end; procedure TABCNestedForm.VarListDblClick(Sender: TObject); var index: Integer; s: String; begin index := VarList.ItemIndex; if index > -1 then begin s := VarList.Items[index]; if DepEdit.Text = '' then DepEdit.Text := s else if FactorAEdit.Text = '' then FactorAEdit.Text := s else if FactorBEdit.Text = '' then FactorBEdit.Text := s else if FactorCEdit.Text = '' then FactorCEdit.Text := s; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TABCNestedForm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; end.