// Use file "ancova.laz" for testing. // Y --> Dependent Variable // Group --> Fixed Factors // X, Z ---> Covariables unit ANCOVAUnit; {$mode objfpc}{$H+} {$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined} {.$DEFINE ANCOVA_DEBUG} interface uses {$IFDEF ANCOVA_DEBUG} LazLogger, {$ENDIF} Classes, SysUtils, Forms, Controls, Graphics, StdCtrls, Buttons, ExtCtrls, LCLVersion, ComCtrls, MainUnit, FunctionsLib, Globals, DataProcs, MatrixLib, DictionaryUnit, ReportFrameUnit, BasicStatsReportAndChartFormUnit; type { TAncovaForm } TAncovaForm = class(TBasicStatsReportAndChartForm) Bevel1: TBevel; Bevel2: TBevel; Plot3DChk: TCheckBox; MultCompChk: TCheckBox; PlotMeansChk: TCheckBox; PrintInverseMatChk: TCheckBox; CorrelationMatsChk: TCheckBox; DescriptiveStatsChk: TCheckBox; DepIn: TBitBtn; DepOut: TBitBtn; FixedIn: TBitBtn; FixedOut: TBitBtn; CovIn: TBitBtn; CovOut: TBitBtn; DepVar: TEdit; OptionsGroup: TGroupBox; Label1: TLabel; Label2: TLabel; Label3: TLabel; FixedList: TListBox; Label4: TLabel; CovList: TListBox; MultCompPage: TTabSheet; VarList: TListBox; procedure CovInClick(Sender: TObject); procedure CovListDblClick(Sender: TObject); procedure CovOutClick(Sender: TObject); procedure DepInClick(Sender: TObject); procedure DepOutClick(Sender: TObject); procedure FixedInClick(Sender: TObject); procedure FixedListDblClick(Sender: TObject); procedure FixedOutClick(Sender: TObject); procedure Plot3DChkChange(Sender: TObject); procedure PlotMeansChkChange(Sender: TObject); procedure VarListDblClick(Sender: TObject); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); private NCases, NoSelected, NoFixed, NoCovs, DepColNo : integer; ColNoSelected : IntDyneVec; // Grid col. no's of predictors RowLabels, ColLabels : StrDyneVec; CorMat : DblDyneMat; // correlation matrix IndMat : DblDyneMat; // correlation matrix among independent variables BetaWeights : DblDyneVec; // standardized regression weights Means, Variances, StdDevs : DblDyneVec; probout : double; // probability for removing a variable Testout : boolean; // true if testing for retention of variables plot : boolean; // if true, plot group means StdErrEst : double; // standard error of estimate multcomp : boolean; // if true make multiple comparisons R2 : double; // squared multiple correlation coefficient FixedCols : IntDyneVec; // grid columns of fixed variables CovCols : IntDyneVec; // grid columns of covariates mingrp, maxgrp : IntDyneVec; // min and max group codes Block : IntDyneMat; // descriptors for group codings // values 1 to 5 contain group min, max, startcol, endcol and no. of vectors NoBlocks : integer; // number of vector blocks created for groups and inter. errorcode : boolean; // returned by routines that use an errorcode IndepIndex : IntDyneVec; // sequential number of predictors in corr. matrix BlockLabel : StrDyneVec; NoTestVecs : integer; // no. of vectors for group interactions with covariates constant : double; // regression constant noind : integer; // no. of independent variables in a regression analysis BWeights : DblDyneVec; // raw regression weights // BStdErrs : DblDyneVec; // standard errors of regression weights // BTtests : DblDyneVec; procedure GetParms; procedure CodeGroups; procedure GenInteractions; procedure DoRegs(AReport: TStrings); procedure CleanUp; procedure EntryOpt1(AReport: TStrings); procedure GenCovInteracts; procedure AdjustMeans(AReport: TStrings); procedure MultCompare; private FMultCompReportFrame: TReportFrame; FChartCombobox: TCombobox; FMeansPlotData: DblDyneMat; FAdjMeansPlotData: DblDyneMat; 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 AncovaForm: TAncovaForm; implementation {$R *.lfm} uses Math, TAChartUtils, TACustomSeries, TASeries, TAGraph, Utils, MathUnit, ChartFrameUnit; { TAncovaForm } constructor TAncovaForm.Create(AOwner: TComponent); begin inherited; InitToolbar(FReportFrame.ReportToolbar, tpTop); FReportFrame.ClearBorderSpacings; FMultCompReportFrame := TReportFrame.Create(self); FMultCompReportFrame.Parent := MultCompPage; FMultCompReportFrame.Align := alClient; 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; ChartPage.PageIndex := PageControl.PageCount-1; PageControl.ActivePageIndex := 0; end; procedure TAncovaForm.AdjustConstraints; begin inherited; ParamsPanel.Constraints.MinHeight := DepIn.Top + 6*DepIn.Height + 3* DepOut.BorderSpacing.Top + 2*DepOut.BorderSpacing.Bottom + Label4.Height * 2 + // not 100% exact... VarList.BorderSpacing.Bottom + OptionsGroup.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; ParamsPanel.Constraints.MinWidth := Max( OptionsGroup.Width, 4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left ); end; procedure TAncovaForm.Compute; var lReport: TStrings; begin NoFixed := FixedList.Items.Count; NoCovs := CovList.Items.Count; lReport := TStringList.Create; try GetParms; CodeGroups; GenInteractions; GenCovInteracts; DoRegs(lReport); FReportFrame.DisplayReport(lReport); finally CleanUp; lReport.Free; end; end; procedure TAncovaForm.CovInClick(Sender: TObject); var i: integer; begin i := 0; while i < VarList.Items.Count do begin if VarList.Selected[i] then begin CovList.Items.Add(VarList.Items[i]); VarList.Items.Delete(i); i := 0; end else inc(i); end; UpdateBtnStates; end; procedure TAncovaForm.CovListDblClick(Sender: TObject); var index: Integer; begin index := CovList.ItemIndex; if index > -1 then begin VarList.Items.Add(CovList.Items[index]); CovList.Items.Delete(index); UpdateBtnStates; end; end; procedure TAncovaForm.CovOutClick(Sender: TObject); var i: Integer; begin i := 0; while i < CovList.Items.Count do begin if CovList.Selected[i] then begin Varlist.Items.Add(CovList.Items[i]); CovList.Items.Delete(i); i := 0; end else inc(i); end; UpdateBtnStates; end; procedure TAncovaForm.DepInClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (DepVar.Text = '') then begin DepVar.Text := VarList.Items[index]; VarList.Items.Delete(index); end; UpdateBtnStates; end; procedure TAncovaForm.DepOutClick(Sender: TObject); begin if DepVar.Text <> '' then begin VarList.Items.Add(DepVar.Text); DepVar.Text := ''; end; UpdateBtnStates; end; procedure TAncovaForm.FixedInClick(Sender: TObject); var i: integer; begin i := 0; while i < VarList.Items.Count do begin if VarList.Selected[i] then begin FixedList.Items.Add(VarList.Items[i]); VarList.Items.Delete(i); i := 0; end else inc(i); end; UpdateBtnStates; end; procedure TAncovaForm.FixedListDblClick(Sender: TObject); var index: Integer; begin index := FixedList.ItemIndex; if index > -1 then begin VarList.Items.Add(FixedList.Items[index]); FixedList.Items.Delete(index); UpdateBtnStates; end; end; procedure TAncovaForm.FixedOutClick(Sender: TObject); var i : integer; begin i := 0; while i < FixedList.Items.Count do begin if FixedList.Selected[i] then begin VarList.Items.Add(FixedList.Items[i]); FixedList.Items.Delete(i); i := 0; end else inc(i); end; UpdateBtnStates; end; procedure TAncovaForm.Plot3DChkChange(Sender: TObject); var ser: TBasicChartSeries; begin if (FChartFrame = nil) or (FChartFrame.Chart.SeriesCount = 0) then exit; ser := FChartFrame.Chart.Series[0]; if (ser is TBarSeries) then begin if Plot3dChk.Checked then TBarSeries(ser).Depth := 20 else TBarSeries(ser).Depth := 0; end; end; procedure TAncovaForm.PlotMeansChkChange(Sender: TObject); begin Plot3DChk.Enabled := PlotMeansChk.Checked; end; procedure TAncovaForm.GetParms; var i, j: integer; begin SetLength(ColNoSelected, NoVariables); SetLength(FixedCols, NoFixed); SetLength(CovCols, NoCovs); SetLength(mingrp, NoFixed); SetLength(maxgrp, NoFixed); SetLength(Block, 100, 5); SetLength(BlockLabel, 100); NoSelected := 0; NoBlocks := 0; plot := PlotMeansChk.Checked; multcomp := MultCompChk.Checked; for i := 1 to NoVariables do begin if DepVar.Text = OS3MainFrm.DataGrid.Cells[i,0] then begin DepColNo := i; ColNoSelected[0] := i; NoSelected := 1; break; end; end; for i := 0 to NoFixed - 1 do begin for j := 1 to NoVariables do begin if FixedList.Items.Strings[i] = OS3MainFrm.DataGrid.Cells[j,0] then begin FixedCols[i] := j; ColNoSelected[NoSelected] := j; NoSelected := NoSelected + 1; break; end; end; end; for i := 0 to NoCovs - 1 do begin for j := 1 to NoVariables do begin if CovList.Items.Strings[i] = OS3MainFrm.DataGrid.Cells[j,0] then begin CovCols[i] := j; ColNoSelected[NoSelected] := j; NoSelected := NoSelected + 1; break; end; end; end; // create a "Block" for each covariate for i := 0 to NoCovs-1 do begin NoBlocks := NoBlocks + 1; Block[i,0] := 0; // group min Block[i,1] := 0; // group max Block[i,2] := CovCols[i]; // start column in grid Block[i,3] := CovCols[i]; // end column in grid Block[i,4] := 1; // no. of vectors BlockLabel[i] := 'Cov' + IntToStr(i); end; end; procedure TAncovaForm.CodeGroups; var col, i, j, value: integer; factlabel, cellstring: string; startcol: Integer = 0; // to silence the compiler endcol: Integer = 0; noVectors: Integer = 0; begin // create a block for code vectors of each fixed variable for i := 0 to NoFixed-1 do begin col := FixedCols[i]; factlabel := chr(ord('A')+i); mingrp[i] := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[col,1]))); maxgrp[i] := mingrp[i]; for j := 1 to NoCases do begin if not GoodRecord(j,NoSelected,ColNoSelected) then continue; cellstring := Trim(OS3MainFrm.DataGrid.Cells[col,j]); value := round(StrToFloat(cellstring)); if value < mingrp[i] then mingrp[i] := value; if value > maxgrp[i] then maxgrp[i] := value; end; // create fixed effect coding for levels - 1 of the fixed effect var. EffectCode(col, mingrp[i], maxgrp[i], factlabel, startcol, endcol, novectors); NoBlocks := NoBlocks + 1; Block[NoBlocks-1,0] := mingrp[i]; Block[NoBlocks-1,1] := maxgrp[i]; Block[NoBlocks-1,2] := startcol; Block[NoBlocks-1,3] := endcol; Block[NoBlocks-1,4] := novectors; BlockLabel[NoBlocks-1] := factlabel; end; // next factor block end; procedure TAncovaForm.GenInteractions; type Twoway = array[0..9,0..1] of integer; Threeway = array[0..9,0..2] of integer; Fourway = array[0..4,0..3] of integer; const Twoways: Twoway = ( (1,2), (1,3), (2,3), (1,4), (2,4), (3,4), (1,5), (2,5), (3,5), (4,5) ); Threeways: Threeway = ( (1,2,3), (1,2,4), (1,3,4), (2,3,4), (1,2,5), (1,3,5), (1,4,5), (2,3,5), (2,4,5), (3,4,5) ); Fourways: Fourway = ( (1,2,3,4), (1,2,3,5), (1,2,4,5), (1,3,4,5), (2,3,4,5) ); var i, j, k, l, m, n, col, value: integer; labelstr: string; startcol, endcol, novectors, oldnovars: integer; cell1, cell2, cell3, cell4: string; TwoWayCombos, ThreeWayCombos, FourwayCombos: double; Block1, Block2, Block3, Block4, Start1, End1, Start2, End2, Start3, End3: integer; Start4, End4: integer; begin novectors := 0; // Do two-way interactions if NoFixed < 2 then exit; TwoWayCombos := round(combos(2.0, NoFixed)); oldnovars := NoVariables; for i := 0 to round(TwoWayCombos)-1 do begin Block1 := TwoWays[i,0] + NoCovs - 1; Block2 := TwoWays[i,1] + NoCovs - 1; Start1 := Block[Block1,2]; End1 := Block[Block1,3]; Start2 := Block[Block2,2]; End2 := Block[Block2,3]; oldnovars := NoVariables; startcol := Block[NoBlocks-1,3] + 1; col := NoVariables; for j := Start1 to End1 do begin for k := Start2 to End2 do begin col := col + 1; novectors := novectors + 1; DictionaryFrm.NewVar(col); labelstr := OS3MainFrm.DataGrid.Cells[j,0] + 'x'; labelstr := labelstr + OS3MainFrm.DataGrid.Cells[k,0]; OS3MainFrm.DataGrid.Cells[col,0] := labelstr; DictionaryFrm.DictGrid.Cells[1,col] := labelstr; for m := 1 to NoCases do begin if not GoodRecord(m,NoSelected,ColNoSelected) then Continue; cell1 := Trim(OS3MainFrm.DataGrid.Cells[j,m]); cell2 := Trim(OS3MainFrm.DataGrid.Cells[k,m]); value := round(StrToFloat(cell1)) * round(StrToFloat(cell2)); OS3MainFrm.DataGrid.Cells[col,m] := IntToStr(value); end; end; endcol := col; NoBlocks := NoBlocks + 1; Block[NoBlocks-1,0] := 0; // zeroes for interactions Block[NoBlocks-1,1] := 0; // zeroes for interactions Block[NoBlocks-1,2] := startcol; // grid start col for 2-way interactions Block[NoBlocks-1,3] := endcol; // grid end col for 2-way interactions Block[NoBlocks-1,4] := novectors; // no. of vectors for 2-way interaction BlockLabel[NoBlocks-1] := BlockLabel[Block1] + 'x' + BlockLabel[Block2]; NoVariables := oldnovars + novectors; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); novectors := 0; end; // end of interaction of fixed effect vectors j and fixed effect vectors k end; // end of 2 way interactions // do 3-way interactions using group vectors and two way interaction vectors if (NoFixed < 3) then exit; ThreeWayCombos := Combos(3.0, NoFixed); for i := 0 to round(ThreeWayCombos)-1 do begin startcol := Block[NoBlocks-1,3] + 1; // next column after last block col := NoVariables; Block1 := ThreeWays[i,0] + NoCovs - 1; Block2 := ThreeWays[i,1] + NoCovs - 1; Block3 := ThreeWays[i,2] + NoCovs - 1; Start1 := Block[Block1,2]; End1 := Block[Block1,3]; Start2 := Block[Block2,2]; End2 := Block[Block2,3]; Start3 := Block[Block3,2]; End3 := Block[Block3,3]; oldnovars := NoVariables; novectors := 0; for j := Start1 to End1 do begin for k := Start2 to End2 do begin for l := Start3 to End3 do // no. vectors in first factor begin col := col + 1; novectors := novectors + 1; DictionaryFrm.NewVar(col); labelstr := OS3MainFrm.DataGrid.Cells[j,0] + 'x'; labelstr := labelstr + OS3MainFrm.DataGrid.Cells[k,0]; labelstr := labelstr + 'x' + OS3MainFrm.DataGrid.Cells[l,0]; OS3MainFrm.DataGrid.Cells[col,0] := labelstr; DictionaryFrm.DictGrid.Cells[1,col] := labelstr; for m := 1 to NoCases do begin if not GoodRecord(m,NoSelected,ColNoSelected) then Continue; cell1 := Trim(OS3MainFrm.DataGrid.Cells[j,m]); cell2 := Trim(OS3MainFrm.DataGrid.Cells[k,m]); cell3 := Trim(OS3MainFrm.DataGrid.Cells[l,m]); value := round(StrToFloat(cell1)) * round(StrToFloat(cell2)) * round(StrToFloat(cell3)); OS3MainFrm.DataGrid.Cells[col,m] := IntToStr(value); end; // next case m end; // next third variable end; // next second variable end; // end of interaction of fixed effects vectors for j, k and l endcol := col; // last grid column containing three-way interaction vectors NoBlocks := NoBlocks + 1; Block[NoBlocks-1,0] := 0; // zeroes for interactions Block[NoBlocks-1,1] := 0; // zeroes for interactions Block[NoBlocks-1,2] := startcol; // grid start col for 2-way interactions Block[NoBlocks-1,3] := endcol; // grid end col for 2-way interactions Block[NoBlocks-1,4] := novectors; // no. of vectors for 2-way interaction BlockLabel[NoBlocks-1] := BlockLabel[Block1] + 'x' + BlockLabel[Block2] + 'x' + BlockLabel[Block3]; NoVariables := oldnovars + novectors; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); end; // end of three way interactions // do 4-way interactions using group and 3-way interaction vectors if (NoFixed < 4) then exit; FourWayCombos := combos(4.0,NoFixed); for i := 0 to round(FourWayCombos) - 1 do begin startcol := Block[NoBlocks-1][3] + 1; col := NoVariables; Block1 := FourWays[i][0] + NoCovs - 1; // block # for first fixed effect Block2 := FourWays[i][1] + NoCovs - 1; // block # for second fixed effect Block3 := FourWays[i][2] + NoCovs - 1; // block # for third fixed effect Block4 := FourWays[i][3] + NoCovs - 1; // block # for fourth fixed effect Start1 := Block[Block1][2]; End1 := Block[Block1][3]; Start2 := Block[Block2][2]; End2 := Block[Block2][3]; Start3 := Block[Block3][2]; End3 := Block[Block3][3]; Start4 := Block[Block4][2]; End4 := Block[Block4][3]; oldnovars := NoVariables; novectors := 0; for j := Start1 to End1 do // vector in first fixed factor begin for k := Start2 to End2 do // vector in second fixed factor begin for l := Start3 to End3 do // vector in third fixed factor begin for m := Start4 to End4 do // vecotr in fourth fixed factor begin col := col + 1; novectors := novectors + 1; DictionaryFrm.NewVar(col); labelstr := OS3MainFrm.DataGrid.Cells[j,0] + 'x'; labelstr := labelstr + OS3MainFrm.DataGrid.Cells[k,0]; labelstr := labelstr + 'x' + OS3MainFrm.DataGrid.Cells[l,0]; OS3MainFrm.DataGrid.Cells[col,0] := labelstr; DictionaryFrm.DictGrid.Cells[1,col] := labelstr; for n := 1 to NoCases do begin cell1 := Trim(OS3MainFrm.DataGrid.Cells[j,n]); cell2 := Trim(OS3MainFrm.DataGrid.Cells[k,n]); cell3 := Trim(OS3MainFrm.DataGrid.Cells[l,n]); cell4 := Trim(OS3MainFrm.DataGrid.Cells[m,n]); value := round(StrToFloat(cell1)) * round(StrToFloat(cell2)) * round(StrToFloat(cell3)) * round(StrToFloat(cell4)); OS3MainFrm.DataGrid.Cells[col,n] := IntToStr(value); end; // next case n end; // next fourth vector m end; // next third vector end; // next second vector end; // end of interaction of fixed effects vectors for j, k and l and m endcol := col; // last grid column containing four-way interaction vectors NoBlocks := NoBlocks + 1; Block[NoBlocks-1][0] := 0; // zeroes for interactions Block[NoBlocks-1][1] := 0; // zeroes for interactions Block[NoBlocks-1][2] := startcol; // grid start col for 4-way interactions Block[NoBlocks-1][3] := endcol; // grid end col for 4-way interactions Block[NoBlocks-1][4] := novectors; // no. of vectors for 2-way interaction BlockLabel[NoBlocks-1] := BlockLabel[Block1] + 'x' + BlockLabel[Block2] + 'x' + BlockLabel[Block3] + 'x' + BlockLabel[Block4]; NoVariables := oldnovars + novectors; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); end; // end of four-way combinations end; procedure TAncovaForm.DoRegs(AReport: TStrings); var count: integer; i, j: integer; begin {$IFDEF ANCOVA_DEBUG} DebugLn('ENTER DoRegs'); {$ENDIF} // get count of variables used count := 0; for i := 0 to NoBlocks - 1 do for j := 0 to Block[i,4] do count := count + 1; {$IFDEF ANCOVA_DEBUG} WriteLn('DoRegs: Count = ', count); {$ENDIF} SetLength(BetaWeights,count+1); SetLength(BWeights,count+2); // SetLength(BStdErrs,count+1); // SetLength(BTtests,count+1); SetLength(Means,count+1); SetLength(Variances,count+1); SetLength(StdDevs,count+1); SetLength(RowLabels,count+1); SetLength(ColLabels,count+1); SetLength(Cormat,count+1,count+1); SetLength(Indmat,count+1,count+1); SetLength(IndepIndex,count+1); SetLength(ColNoSelected,count+1); Testout := false; Probout := 0.99; AReport.Add(DIVIDER_AUTO); AReport.Add('ANALYSIS OF COVARIANCE USING MULTIPLE REGRESSION'); AReport.Add(DIVIDER_AUTO); AReport.Add(''); AReport.Add('File Analyzed: ' + OS3MainFrm.FileNameEdit.Text); AReport.Add(''); EntryOpt1(AReport); // factors, interactions and covariats concurrently IndepIndex := nil; {$IFDEF ANCOVA_DEBUG} DebugLn('EXIT DoRegs'); {$ENDIF} end; procedure TAncovaForm.CleanUp; begin Indmat := nil; Cormat := nil; ColLabels := nil; RowLabels := nil; StdDevs := nil; Variances := nil; Means := nil; // BTtests := nil; // BStdErrs := nil; BWeights := nil; BetaWeights := nil; maxgrp := nil; mingrp := nil; CovCols := nil; FixedCols := nil; ColNoSelected := nil end; procedure TAncovaForm.EntryOpt1(AReport: TStrings); var i, j, k, col, count: integer; Title: string; FullR2: double; F: double; Prob: double; df1, df2: double; SSGroups: double; MSGroups: double; SSError: double; MSError: double; SSTotal: double; // SSExplained: double; SSGrpTot: double = 0.0; tProbs: DblDyneVec = nil; BTtests: DblDyneVec = nil; BStdErrs: DblDyneVec = nil; // standard errors of regression weights localReport: TStrings; begin {$IFDEF ANCOVA_DEBUG} DebugLn('ENTER EntryOpt1'); {$ENDIF} // Factors, interactions and covariates concurrently (full model) // Get grid column numbers of all vectors and dependent variable AReport.Add(''); AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add('MODEL FOR TESTING ASSUMPTION OF ZERO INTERACTIONS WITH COVARIATES'); AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add(''); count := 0; for i := 1 to NoBlocks do // no. of vector blocks begin for j := 1 to Block[i-1,4] do // no of vectors in block begin col := Block[i-1,2] + j - 1; // count from beginning col. count := count + 1; ColNoSelected[count-1] := col; IndepIndex[count-1] := count; RowLabels[count-1] := OS3MainFrm.DataGrid.Cells[col,0]; end; end; count := count + 1; noind := count - 1; ColNoSelected[count-1] := DepColNo; IndepIndex[count-1] := count; RowLabels[count-1] := OS3MainFrm.DataGrid.Cells[DepColNo,0]; // Get correlation matrix (note: dependent is last variable) Correlations(count,ColNoSelected,CorMat, Means, Variances, StdDevs, errorcode, NCases); if CorrelationMatsChk.Checked then begin title := 'CORRELATION MATRIX'; MatPrint(CorMat, count, count, title, RowLabels, RowLabels, NCases, AReport); end; if DescriptiveStatsChk.Checked then begin AReport.Add(DIVIDER_SMALL_AUTO); DynVectorPrint(means, count, 'MEANS', RowLabels, NoCases, AReport); AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add(''); DynVectorPrint(variances, count, 'VARIANCES', RowLabels, NoCases, AReport); AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add(''); DynVectorPrint(stdDevs, count, 'STANDARD DEVIATIONS', RowLabels, NoCases, AReport); end; // Get regression SetLength(tProbs, count); SetLength(BStdErrs, noind+1); SetLength(BTtests, noind+1); MReg( noind, ColNoSelected, DepColNo, RowLabels ,Means, Variances, StdDevs, BWeights, BetaWeights, BStdErrs, BTtests, tProbs, R2, StdErrEst, NCases, errorcode, false, AReport ); if not ErrorCode then begin FullR2 := R2; SSTotal := Variances[count-1] * (NCases - 1); SSGroups := FullR2 * SSTotal; SSError := (1.0 - FullR2) * SSTotal; df1 := noind; df2 := NCases - noind - 1; MSGroups := SSGroups / df1; MSError := SSError / df2; F := MSGroups / MSError; Prob := ProbF(F, df1, df2); AReport.Add(''); AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add(''); AReport.Add('Analysis of Variance for the Model to Test Regression Homogeneity'); AReport.Add(' SOURCE Deg.F. SS MS F Prob>F'); AReport.Add('%10s %10.0f %10.2f %10.2f %10.3f %10.4f', ['Explained', df1, SSGroups, MSGroups, F, Prob]); AReport.Add('%10s %10.0f %10.2f %10.2f', ['Error', df2, SSError, MSError]); AReport.Add('%10s %10d %10.2f', ['Total', NCases-1, SSTotal]); AReport.Add(''); AReport.Add('%12s %10.3f',['R Squared = ',R2]); AReport.Add(''); end; // Now do analysis without the interactions (Ancova model) AReport.Add(''); AReport.Add(DIVIDER_AUTO); AReport.Add('MODEL FOR ANALYSIS OF COVARIANCE'); AReport.Add(DIVIDER_AUTO);; AReport.Add(''); count := 0; for i := 1 to NoBlocks - 1 do // no. of vector blocks begin for j := 1 to Block[i-1,4] do // no of vectors in block begin col := Block[i-1,2] + j - 1; // count from beginning col. count := count + 1; ColNoSelected[count-1] := col; IndepIndex[count-1] := count; RowLabels[count-1] := OS3MainFrm.DataGrid.Cells[col,0]; end; end; count := count + 1; noind := count - 1; ColNoSelected[count-1] := DepColNo; IndepIndex[count-1] := count; RowLabels[count-1] := OS3MainFrm.DataGrid.Cells[DepColNo,0]; // Get correlation matrix (note dependent is last variable) Correlations(count,ColNoSelected,Cormat,Means,Variances,StdDevs,errorcode,NCases); // save in IndMat for i := 0 to count-1 do for j := 0 to count - 1 do IndMat[i,j] := Cormat[i,j]; if CorrelationMatsChk.Checked then begin AReport.Add(''); Title := 'Correlation Matrix'; MatPrint(Cormat, count, count, title, RowLabels, RowLabels, NCases, AReport); end; if DescriptiveStatsChk.Checked then begin AReport.Add(''); AReport.Add(DIVIDER_SMALL_AUTO); DynVectorPrint(Means, count, 'MEANS', RowLabels, NCases, AReport); AReport.Add(DIVIDER_SMALL_AUTO); DynVectorPrint(Variances, count, 'VARIANCES', RowLabels, NCases, AReport); AReport.Add(DIVIDER_SMALL_AUTO); DynVectorPrint(StdDevs, count, 'STANDARD DEVIATIONS', RowLabels, NCases, AReport); AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add(''); end; // Get regression SetLength(BStdErrs, noind+1); SetLength(BTtests, noind+1); MReg( noind, ColNoSelected, DepColNo, RowLabels, Means, Variances, StdDevs, BWeights, BetaWeights, BStdErrs, BTtests, tProbs, R2, StdErrEst, NCases, errorcode, false, AReport ); if not ErrorCode then begin // test differences between previous and current models (= beta test) constant := BWeights[noind]; df1 := NoTestVecs; F := ((FullR2 - R2) / df1) / ((1.0 - FullR2) / df2); Prob := probf(F,df1,df2); AReport.Add(''); AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add(''); AReport.Add('Test for Homogeneity of Group Regression Coefficients'); AReport.Add('Change in R2 = %6.4f. F = %10.3f Prob.> F = %6.4f with d.f. %8.0f and %8.0f', [(FullR2 - R2), F, Prob, df1, df2]); AReport.Add(''); AReport.Add('%12s %10.3f',['R Squared = ', R2]); FullR2 := R2; SSTotal := Variances[count-1] * (NCases - 1); SSGroups := FullR2 * SSTotal; SSError := (1.0 - FullR2) * SSTotal; df1 := noind; df2 := NCases - noind - 1; MSGroups := SSGroups / df1; MSError := SSError / df2; // obtain Adjusted means // AdjustMeans(self); // Make Comparisons among means // if multcomp then MultCompare(self); F := MSGroups / MSError; Prob := probf(F,df1,df2); AReport.Add(''); AReport.Add(DIVIDER_AUTO); AReport.Add(''); AReport.Add('Analysis of Variance for the ANCOVA Model'); AReport.Add(' SOURCE Deg.F. SS MS F Prob>F '); AReport.Add('---------- ---------- ---------- ---------- ---------- ----------'); AReport.Add('%10s %10.0f %10.2f %10.2f %10.3f %10.4f', ['Explained', df1, SSGroups, MSGroups, F, Prob]); AReport.Add('%10s %10.0f %10.2f %10.2f', ['Error', df2, SSError, MSError]); AReport.Add('%10s %10d %10.2f', ['Total', NCases-1, SSTotal]); AReport.Add(''); AReport.Add(''); end; // Obtain adjusted means AReport.Add(DIVIDER_AUTO); AReport.Add(''); AdjustMeans(AReport); // Make comparisons among groups if multcomp then begin MultCompPage.TabVisible := true; MultCompare; end else MultCompPage.TabVisible := false; // Do regression, eliminating each block to test effects of that term AReport.Add(DIVIDER_AUTO); AReport.Add(''); AReport.Add('TEST FOR EACH SOURCE OF VARIANCE - Type III SS'); AReport.Add(''); AReport.Add('--------------------------------------------------------------------------'); AReport.Add(' SOURCE Deg.F. SS MS F Prob>F'); AReport.Add('--------------------------------------------------------------------------'); localReport := TStringList.Create; try for i := 1 to NoBlocks - 1 do // covariates, fixed effects, interactions begin count := 0; for j := 1 to NoBlocks-1 do begin if j = i then continue; // exclude the factor to be tested for k := 1 to Block[j-1,4] do // no of vectors in block begin col := Block[j-1,2] + k - 1; // count from beginning col. count := count + 1; ColNoSelected[count-1] := col; IndepIndex[count-1] := count; RowLabels[count-1] := OS3MainFrm.DataGrid.Cells[col,0]; end; end; // get next block of vectors for factors to be included count := count + 1; noind := count - 1; ColNoSelected[count-1] := DepColNo; IndepIndex[count-1] := count; RowLabels[count-1] := OS3MainFrm.DataGrid.Cells[DepColNo,0]; Correlations(count,ColNoSelected,Cormat,Means,Variances,StdDevs,errorcode,NCases); // Get regression SetLength(BStdErrs, noind+1); SetLength(BTtests, noind+1); localReport.Add(Blocklabel[i-1]); localReport.Add(''); MReg( noind, ColNoSelected, DepColNo, RowLabels, Means, Variances, StdDevs, BWeights, BetaWeights, BStdErrs, BTtests, tProbs, R2, StdErrEst, NCases, errorcode, false, localReport ); localReport.Add(''); df1 := Block[i-1,4]; SSGroups := (FullR2 - R2)* SSTotal; SSGrpTot := SSGrpTot + SSGroups; MSGroups := SSGroups / df1; F := MSGroups / MSError; Prob := probf(F,df1,df2); AReport.Add('%10s %10.0f %10.2f %10.2f %10.3g %10.4f', [BlockLabel[i-1], df1, SSGroups, MSGroups, F, Prob]); end; // get next Block to eliminate AReport.Add(''); AReport.Add('----------------------------------------------------------------------'); AReport.Add('%10s %10.0f %10.2f %10.2f', ['ERROR', df2, SSError, MSError]); AReport.Add('----------------------------------------------------------------------'); AReport.Add('%10s %10d %10.2f', ['TOTAL', NCases-1, SSTotal]); AReport.Add(''); AReport.AddStrings(localReport); finally localReport.Free; end; { df1 := NoCovs; SSGroups := SSExplained - SSGrpTot; MSGroups := SSGroups / df1; F := MSGroups / MSError; Prob := probf(F,df1,df2); outline := format('%10s %10.0f %10.2f %10.2f %10.3f %10.4f', ['Covariates',df1,SSGroups,MSGroups,F,Prob]); OutputFrm.RichEdit.Lines.Add(outline); outline := format('%10s %10.0f %10.2f %10.2f', ['Error',df2,SSError,MSError]); OutputFrm.RichEdit.Lines.Add(outline); outline := format('%10s %10d %10.2f', ['Total',NCases-1,SSTotal]); OutputFrm.RichEdit.Lines.Add(outline); OutPutFrm.RichEdit.Lines.Add(''); } tProbs := nil; BTTests := nil; BStdErrs := nil; {$IFDEF ANCOVA_DEBUG} DebugLn('EXIT EntryOpt1'); {$ENDIF} end; procedure TAncovaForm.GenCovInteracts; var i, j, k, m, vect1col, vect2col, col: integer; value: double; labelstr, cell1, cell2: string; startcol, endcol, novectors, oldnovars: integer; lastblock, firstblock: integer; begin col := NoVariables; oldnovars := NoVariables; novectors := 0; NoTestVecs := 0; startcol := Block[NoBlocks-1,3] + 1; lastblock := NoBlocks; firstblock := NoCovs + 1; // product vectors for each covariate for i := 1 to NoCovs do begin vect1col := Block[i-1,2]; for j := firstblock to lastblock do begin for k := 1 to Block[j-1,4] do begin vect2col := Block[j-1,2] + k - 1; // first vector col. of B col := col + 1; novectors := novectors + 1; NoTestVecs := NoTestVecs + 1; DictionaryFrm.NewVar(col); labelstr := OS3MainFrm.DataGrid.Cells[vect1col,0] + 'x'; labelstr := labelstr + OS3MainFrm.DataGrid.Cells[vect2col,0]; OS3MainFrm.DataGrid.Cells[col,0] := labelstr; DictionaryFrm.DictGrid.Cells[1,col] := labelstr; for m := 1 to NoCases do begin if not GoodRecord(m,NoSelected,ColNoSelected) then Continue; cell1 := Trim(OS3MainFrm.DataGrid.Cells[vect1col,m]); cell2 := Trim(OS3MainFrm.DataGrid.Cells[vect2col,m]); value := StrToFloat(cell1) * StrToFloat(cell2); OS3MainFrm.DataGrid.Cells[col,m] := FloatToStr(value); end; // next case m end; // next l vector end; // next fixed effects factor j and interactions end; // next covariate i endcol := col; // last grid column containing two-way interaction vectors NoBlocks := NoBlocks + 1; Block[NoBlocks-1,0] := 0; // zeroes for interactions Block[NoBlocks-1,1] := 0; // zeroes for interactions Block[NoBlocks-1,2] := startcol; // grid start col for 2-way interactions Block[NoBlocks-1,3] := endcol; // grid end col for 2-way interactions Block[NoBlocks-1,4] := novectors; // no. of vectors for 2-way interaction BlockLabel[NoBlocks-1] := BlockLabel[i-1] + 'xFixed'; NoVariables := oldnovars + novectors; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); end; procedure TAncovaForm.AdjustMeans(AReport: TStrings); var sum : double; GrpCovMeans : DblDyneMat = nil; AdjMeans : DblDyneVec = nil; Intercepts : DblDyneVec = nil; i, j, k, col, grp, nogrps : integer; value : double; Labels : StrDyneVec = nil; noingrp : IntDyneVec = nil; XValue : DblDyneVec = nil; cell1 : string; begin SetLength(GrpCovMeans,noind,noind); SetLength(AdjMeans,noind); SetLength(Intercepts,noind); SetLength(Labels,noind); SetLength(noingrp,noind); SetLength(XValue,noind); SetLength(FMeansPlotData, NoFixed, NoCases); SetLength(FAdjMeansPlotData, NoFixed, NoCases); FChartCombobox.Parent.Left := 0; PopulateChartCombobox; // SelectPlot(nil); // Get means for groups and covariates for j := 1 to NoFixed do // for each fixed variable begin nogrps := maxgrp[j-1] - mingrp[j-1] + 1; for i := 0 to nogrps-1 do begin XValue[i] := i+1; noingrp[i] := 0; for k := 0 to NoCovs-1 do begin GrpCovMeans[i, k] := 0.0; Labels[k] := 'Group ' + IntToStr(k+1); end; end; for i := 0 to nogrps-1 do AdjMeans[i] := 0.0; for i := 1 to NoCases do begin cell1 := Trim(OS3MainFrm.DataGrid.Cells[FixedCols[j-1],i]); if cell1 = '' then continue; grp := round(StrToFloat(cell1)); grp := grp - mingrp[j-1]; noInGrp[grp] := noInGrp[grp] + 1; for k := 0 to NoCovs-1 do begin col := CovCols[k]; cell1 := Trim(OS3MainFrm.DataGrid.Cells[col,i]); if cell1 = '' then continue; value := StrToFloat(cell1); GrpCovMeans[grp, k] := GrpCovMeans[grp, k] + value; end; cell1 := Trim(OS3MainFrm.DataGrid.Cells[DepColNo, i]); if cell1 = '' then continue; value := StrToFloat(cell1); AdjMeans[grp] := AdjMeans[grp] + value; end; // next case i SetLength(FMeansPlotData[j-1], NoGrps); for k := 1 to nogrps do begin AdjMeans[k-1] := AdjMeans[k-1] / noingrp[k-1]; FMeansPlotData[j-1, k-1] := AdjMeans[k-1]; for i := 1 to NoCovs do GrpCovMeans[k-1,i-1] := GrpCovMeans[k-1,i-1] / noingrp[k-1]; end; // print unadjusted means AReport.Add('Unadjusted Group Means for Group Variable ' + OS3MainFrm.DataGrid.Cells[FixedCols[j-1] ,0]); DynVectorPrint(AdjMeans, nogrps, 'Means', Labels, NCases, AReport); // Get intercepts for group equations for this fixed effect variable sum := 0.0; for k := 1 to nogrps - 1 do // no. vectors is 1 less than no. groups begin intercepts[k-1] := constant + BWeights[NoCovs+k-1]; sum := sum + BWeights[NoCovs+k-1]; end; intercepts[nogrps-1] := constant - sum; // get adjusted means SetLength(FAdjMeansPlotData[j-1], noGrps); for k := 1 to nogrps do begin sum := 0.0; for i := 1 to NoCovs do sum := sum + BWeights[i-1] * (GrpCovMeans[k-1,i-1]-Means[i-1]); AdjMeans[k-1] := AdjMeans[k-1] - sum; FAdjMeansPlotData[j-1, k-1] := AdjMeans[k-1]; end; // print results for intercepts AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add(''); AReport.Add('Intercepts for Each Group Regression Equation for Variable: ' + OS3MainFrm.DataGrid.Cells[FixedCols[j-1] ,0]); DynVectorPrint(Intercepts, nogrps, 'Intercepts', Labels, NCases, AReport); // print adjusted means AReport.Add(DIVIDER_SMALL_AUTO); AReport.Add(''); AReport.Add('Adjusted Group Means for Group Variable ' + OS3MainFrm.DataGrid.Cells[FixedCols[j-1] ,0]); DynVectorPrint(AdjMeans, nogrps, 'Means', Labels, NCases, AReport); end; if plot then begin ChartPage.TabVisible := true; SelectPlot(nil); end else ChartPage.TabVisible := false; end; procedure TAncovaForm.MultCompare; var lReport: TStrings; i, j, size: integer; covmat: DblDyneMat = nil; title: string; Labels: StrDyneVec = nil; sum: double; df1, df2, F, Prob: double; begin SetLength(covmat, noind, noind); SetLength(Labels, noind); lReport := TStringList.Create; try lReport.Add(DIVIDER_SMALL_AUTO); lReport.Add('Multiple Comparisons Among Group Means'); lReport.Add(DIVIDER_SMALL_AUTO); lReport.Add(''); SVDInverse(IndMat,noind); size := noind - NoCovs; title := 'Inverse of Independents Matrix'; for i := 0 to noind-1 do Labels[i] := 'Group ' + IntToStr(i+1); for i := 0 to noind-NoCovs-1 do for j := 0 to noind-NoCovs-1 do covmat[i, j] := sqr(StdErrEst) * IndMat[NoCovs+i,NoCovs+j] / (Variances[NoCovs+j] * (NoCases-1)); for i := 0 to size do Labels[i] := 'Group ' + IntToStr(i+1); // augment matrix for i := 0 to size-1 do begin sum := 0.0; for j := 0 to size-1 do sum := sum + covmat[i, j]; covmat[i, size] := -sum; covmat[size, i] := -sum; end; sum := 0.0; for i := 0 to size-1 do sum := sum + covmat[i, size]; covmat[size,size] := -sum; if PrintInverseMatChk.Checked then begin title := 'Augmented Covariance Among Group Vectors'; for i := 0 to size-1 do Labels[i] := 'Group ' + IntToStr(i+1); MatPrint(covmat, size+1, size+1, title, Labels, Labels, NoCases, lReport); lReport.Add(DIVIDER_SMALL_AUTO); end; // Contrast the b coefficients // Get last B weight from effect coding as - sum of other B weights BWeights[noind] := 0.0; for i := 0 to noind-1 do BWeights[noind] := BWeights[noind] - BWeights[i]; for i := 1 to size do begin for j := i + 1 to size + 1 do begin df1 := 1.0; df2 := NoCases - noind - 1; F := sqr(BWeights[NoCovs+i-1] - BWeights[NoCovs+j-1]); F := F / (covmat[i-1,i-1] + covmat[j-1,j-1] - (covmat[i-1,j-1] + covmat[j-1,i-1])); Prob := probf(F,df1,df2); lReport.Add('Comparison of Group %d with Group %d', [i,j]); lReport.Add('F: %.3f, probability: %.3f with degrees of freedom %.0f and %.0f', [F, Prob, df1, df2]); lReport.Add(''); end; end; FMultCompReportFrame.DisplayReport(lReport); finally lReport.Free; end; end; procedure TAncovaForm.PopulateChartCombobox; var idx: Integer; i: Integer; begin idx := FChartCombobox.ItemIndex; FChartCombobox.Items.Clear; for i := 0 to FixedList.Count-1 do begin FChartCombobox.Items.Add(Format('%s (unadjusted)', [FixedList.Items[i]])); FChartCombobox.Items.Add(Format('%s (adjusted)', [FixedList.Items[i]])); end; FChartCombobox.ItemIndex := EnsureRange(idx, 0, FChartCombobox.Items.Count-1); end; procedure TAncovaForm.Reset; var i: integer; begin inherited; if FMultCompReportFrame <> nil then FMultCompReportFrame.Clear; if FChartCombobox <> nil then FChartCombobox.Items.Clear; DepVar.Clear; VarList.Clear; for i := 1 to NoVariables do VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); CovList.Clear; FixedList.Clear; NoBlocks := 0; UpdateBtnStates; end; procedure TAncovaForm.SelectPlot(Sender: TObject); var fixedIdx: Integer; isAdjusted: Boolean; ser: TChartSeries; clr: TColor; begin // The combobox contains unadjusted and adjusted means in alternating order // even = unadjusted, odd = adjusted fixedIdx := FChartCombobox.ItemIndex div 2; isAdjusted := odd(FChartCombobox.ItemIndex); clr := DATA_COLORS[fixedIdx mod Length(DATA_COLORS)]; FChartFrame.Clear; FChartFrame.SetXTitle('Group'); FChartFrame.SetYTitle('Mean'); if isAdjusted then begin FChartFrame.SetTitle('Unadjusted Means'); ser := FChartFrame.PlotXY(ptBars, nil, FMeansPlotData[fixedIdx], nil, nil, '', clr); end else begin FChartFrame.SetTitle('Adjusted Means'); ser := FChartFrame.PlotXY(ptBars, nil, FAdjMeansPlotData[fixedIdx], nil, nil, '', clr); end; with TBarSeries(ser) do begin {$IF LCL_FullVersion >= 2010000} DepthBrightnessDelta := -30; {$IFEND} end; FChartFrame.Chart.BottomAxis.Marks.Source := ser.Source; FChartFrame.Chart.BottomAxis.Marks.Style := smsXValue; FChartFrame.Chart.Legend.Visible := false; FChartFrame.UpdateBtnStates; end; procedure TAncovaForm.UpdateBtnStates; var lSelected: Boolean; i: Integer; begin inherited; if FMultCompReportFrame <> nil then FMultCompReportFrame.UpdateBtnStates; DepIn.Enabled := (VarList.ItemIndex > -1) and (DepVar.Text = ''); DepOut.Enabled := (DepVar.Text <> ''); lSelected := false; for i := 0 to VarList.Items.Count-1 do if VarList.Selected[i] then begin lSelected := true; break; end; FixedIn.Enabled := lSelected; CovIn.Enabled := lSelected; lSelected := false; for i := 0 to FixedList.Items.Count-1 do if FixedList.Selected[i] then begin lSelected := true; break; end; FixedOut.Enabled := lSelected; lSelected := false; for i := 0 to CovList.Items.Count-1 do if CovList.Selected[i] then begin lSelected := true; break; end; CovOut.Enabled := lSelected; end; function TAncovaForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; begin Result := false; if DepVar.Text = '' then begin AMsg := 'No dependent variable selected.'; AControl := VarList; exit; end; if (FixedList.Items.Count <= 0) or (CovList.Items.Count <= 0) then begin AMsg := 'You must have at least one group variable and one covariate'; AControl := VarList; exit; end; Result := true; end; procedure TAncovaForm.VarListDblClick(Sender: TObject); var index: Integer; s: String; begin index := VarList.ItemIndex; if index > -1 then begin s := VarList.Items[index]; if DepVar.Text = '' then begin DepVar.Text := s; VarList.Items.Delete(index); UpdateBtnStates; end else begin FixedList.Items.Add(s); VarList.Items.Delete(index); UpdateBtnStates; end; end; end; procedure TAncovaForm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; end.