diff --git a/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.lfm b/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.lfm index f48c9e130..8c54a8c75 100644 --- a/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.lfm +++ b/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.lfm @@ -10,35 +10,35 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ClientWidth = 940 inherited ParamsPanel: TPanel Height = 529 - Width = 401 + Width = 313 ClientHeight = 529 - ClientWidth = 401 + ClientWidth = 313 inherited CloseBtn: TButton - Left = 346 + Left = 258 Top = 504 end inherited ComputeBtn: TButton - Left = 262 + Left = 174 Top = 504 end inherited ResetBtn: TButton - Left = 200 + Left = 112 Top = 504 end inherited HelpBtn: TButton Tag = 107 - Left = 141 + Left = 53 Top = 504 end inherited ButtonBevel: TBevel Top = 488 - Width = 401 + Width = 313 end object Label3: TLabel[5] AnchorSideTop.Control = OverAllAlphaEdit AnchorSideTop.Side = asrCenter AnchorSideRight.Control = OverAllAlphaEdit - Left = 263 + Left = 175 Height = 30 Top = 297 Width = 79 @@ -56,7 +56,7 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm AnchorSideRight.Control = Factor3Edit AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = ButtonBevel - Left = 350 + Left = 262 Height = 23 Top = 301 Width = 51 @@ -71,7 +71,7 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm AnchorSideTop.Control = PostAlphaEdit AnchorSideTop.Side = asrCenter AnchorSideRight.Control = PostAlphaEdit - Left = 58 + Left = 74 Height = 15 Top = 469 Width = 163 @@ -87,7 +87,7 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm AnchorSideRight.Control = PostHocGroup AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = ButtonBevel - Left = 229 + Left = 245 Height = 23 Top = 465 Width = 51 @@ -99,11 +99,11 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm end object PostHocGroup: TGroupBox[9] AnchorSideLeft.Control = ParamsPanel + AnchorSideLeft.Side = asrCenter AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = PlotOptionsGroup AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = PostAlphaEdit - Left = 0 + Left = 16 Height = 93 Top = 364 Width = 280 @@ -170,7 +170,7 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = OverAllAlphaEdit AnchorSideRight.Side = asrBottom - Left = 298 + Left = 210 Height = 19 Top = 332 Width = 103 @@ -182,41 +182,7 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm Caption = 'Get Interactions' TabOrder = 7 end - object PlotOptionsGroup: TRadioGroup[11] - AnchorSideLeft.Control = PostHocGroup - AnchorSideLeft.Side = asrBottom - AnchorSideTop.Control = PostHocGroup - AnchorSideRight.Side = asrBottom - Left = 296 - Height = 114 - Top = 364 - Width = 145 - AutoFill = True - AutoSize = True - BorderSpacing.Left = 16 - BorderSpacing.Right = 8 - Caption = 'Plot Options' - ChildSizing.LeftRightSpacing = 12 - ChildSizing.TopBottomSpacing = 6 - ChildSizing.VerticalSpacing = 2 - ChildSizing.EnlargeHorizontal = crsHomogenousChildResize - ChildSizing.EnlargeVertical = crsHomogenousChildResize - ChildSizing.ShrinkHorizontal = crsScaleChilds - ChildSizing.ShrinkVertical = crsScaleChilds - ChildSizing.Layout = cclLeftToRightThenTopToBottom - ChildSizing.ControlsPerLine = 1 - ClientHeight = 94 - ClientWidth = 141 - ItemIndex = 0 - Items.Strings = ( - 'no plot' - 'Vertical 3D Bars' - 'Vertical 2D Bars' - 'Horizontal 2D Bars' - ) - TabOrder = 8 - end - object Label1: TLabel[12] + object Label1: TLabel[11] AnchorSideLeft.Control = ParamsPanel AnchorSideTop.Control = ParamsPanel Left = 0 @@ -226,7 +192,7 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm Caption = 'Variables:' ParentColor = False end - object VarList: TListBox[13] + object VarList: TListBox[12] AnchorSideLeft.Control = ParamsPanel AnchorSideTop.Control = Label1 AnchorSideTop.Side = asrBottom @@ -235,20 +201,20 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm Left = 0 Height = 331 Top = 17 - Width = 181 + Width = 137 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Top = 2 BorderSpacing.Right = 6 ItemHeight = 0 OnDblClick = VarListDblClick OnSelectionChange = VarListSelectionChange - TabOrder = 9 + TabOrder = 8 end - object DepIn: TBitBtn[14] + object DepIn: TBitBtn[13] AnchorSideLeft.Control = ParamsPanel AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = VarList - Left = 187 + Left = 143 Height = 26 Top = 17 Width = 26 @@ -256,13 +222,13 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ImageIndex = 1 OnClick = DepInClick Spacing = 0 - TabOrder = 10 + TabOrder = 9 end - object DepOut: TBitBtn[15] + object DepOut: TBitBtn[14] AnchorSideLeft.Control = DepIn AnchorSideTop.Control = DepIn AnchorSideTop.Side = asrBottom - Left = 187 + Left = 143 Height = 26 Top = 47 Width = 26 @@ -271,13 +237,13 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ImageIndex = 0 OnClick = DepOutClick Spacing = 0 - TabOrder = 11 + TabOrder = 10 end - object Fact1In: TBitBtn[16] + object Fact1In: TBitBtn[15] AnchorSideLeft.Control = DepIn AnchorSideTop.Control = DepOut AnchorSideTop.Side = asrBottom - Left = 187 + Left = 143 Height = 26 Top = 89 Width = 26 @@ -286,13 +252,13 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ImageIndex = 1 OnClick = Fact1InClick Spacing = 0 - TabOrder = 12 + TabOrder = 11 end - object Fact1Out: TBitBtn[17] + object Fact1Out: TBitBtn[16] AnchorSideLeft.Control = DepIn AnchorSideTop.Control = Fact1In AnchorSideTop.Side = asrBottom - Left = 187 + Left = 143 Height = 26 Top = 119 Width = 26 @@ -301,13 +267,13 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ImageIndex = 0 OnClick = Fact1OutClick Spacing = 0 - TabOrder = 13 + TabOrder = 12 end - object Fact2In: TBitBtn[18] + object Fact2In: TBitBtn[17] AnchorSideLeft.Control = DepIn AnchorSideTop.Control = Fact1Out AnchorSideTop.Side = asrBottom - Left = 187 + Left = 143 Height = 26 Top = 161 Width = 26 @@ -316,13 +282,13 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ImageIndex = 1 OnClick = Fact2InClick Spacing = 0 - TabOrder = 14 + TabOrder = 13 end - object Fact2Out: TBitBtn[19] + object Fact2Out: TBitBtn[18] AnchorSideLeft.Control = DepIn AnchorSideTop.Control = Fact2In AnchorSideTop.Side = asrBottom - Left = 187 + Left = 143 Height = 26 Top = 191 Width = 26 @@ -331,13 +297,13 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ImageIndex = 0 OnClick = Fact2OutClick Spacing = 0 - TabOrder = 15 + TabOrder = 14 end - object Fact3In: TBitBtn[20] + object Fact3In: TBitBtn[19] AnchorSideLeft.Control = DepIn AnchorSideTop.Control = Fact2Out AnchorSideTop.Side = asrBottom - Left = 187 + Left = 143 Height = 26 Top = 233 Width = 26 @@ -346,13 +312,13 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ImageIndex = 1 OnClick = Fact3InClick Spacing = 0 - TabOrder = 16 + TabOrder = 15 end - object Fact3Out: TBitBtn[21] + object Fact3Out: TBitBtn[20] AnchorSideLeft.Control = DepIn AnchorSideTop.Control = Fact3In AnchorSideTop.Side = asrBottom - Left = 187 + Left = 143 Height = 26 Top = 263 Width = 26 @@ -361,13 +327,13 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm ImageIndex = 0 OnClick = Fact3OutClick Spacing = 0 - TabOrder = 17 + TabOrder = 16 end - object StaticText1: TStaticText[22] + object StaticText1: TStaticText[21] AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideBottom.Control = DepVarEdit - Left = 221 + Left = 177 Height = 16 Top = 20 Width = 103 @@ -376,31 +342,31 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm BorderSpacing.Left = 8 BorderSpacing.Bottom = 2 Caption = 'Dependent Variable' - TabOrder = 18 + TabOrder = 17 end - object DepVarEdit: TEdit[23] + object DepVarEdit: TEdit[22] AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideRight.Control = ParamsPanel AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = DepOut AnchorSideBottom.Side = asrBottom - Left = 221 + Left = 177 Height = 23 Top = 38 - Width = 180 + Width = 136 Anchors = [akLeft, akRight, akBottom] BorderSpacing.Left = 8 BorderSpacing.Bottom = 12 ReadOnly = True - TabOrder = 19 + TabOrder = 18 Text = 'DepVarEdit' end - object StaticText2: TStaticText[24] + object StaticText2: TStaticText[23] AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideBottom.Control = Factor1Edit - Left = 221 + Left = 177 Height = 16 Top = 92 Width = 87 @@ -409,31 +375,31 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm BorderSpacing.Left = 8 BorderSpacing.Bottom = 2 Caption = 'Factor 1 Variable' - TabOrder = 20 + TabOrder = 19 end - object Factor1Edit: TEdit[25] + object Factor1Edit: TEdit[24] AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideRight.Control = ParamsPanel AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = Fact1Out AnchorSideBottom.Side = asrBottom - Left = 221 + Left = 177 Height = 23 Top = 110 - Width = 180 + Width = 136 Anchors = [akLeft, akRight, akBottom] BorderSpacing.Left = 8 BorderSpacing.Bottom = 12 ReadOnly = True - TabOrder = 21 + TabOrder = 20 Text = 'Factor1Edit' end - object StaticText3: TStaticText[26] + object StaticText3: TStaticText[25] AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideBottom.Control = Factor2Edit - Left = 221 + Left = 177 Height = 16 Top = 164 Width = 87 @@ -442,31 +408,31 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm BorderSpacing.Left = 8 BorderSpacing.Bottom = 2 Caption = 'Factor 2 Variable' - TabOrder = 22 + TabOrder = 21 end - object Factor2Edit: TEdit[27] + object Factor2Edit: TEdit[26] AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideRight.Control = ParamsPanel AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = Fact2Out AnchorSideBottom.Side = asrBottom - Left = 221 + Left = 177 Height = 23 Top = 182 - Width = 180 + Width = 136 Anchors = [akLeft, akRight, akBottom] BorderSpacing.Left = 8 BorderSpacing.Bottom = 12 ReadOnly = True - TabOrder = 23 + TabOrder = 22 Text = 'Factor2Edit' end - object StaticText4: TStaticText[28] + object StaticText4: TStaticText[27] AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideBottom.Control = Factor3Edit - Left = 221 + Left = 177 Height = 16 Top = 236 Width = 72 @@ -475,34 +441,34 @@ inherited OneCaseAnovaForm: TOneCaseAnovaForm BorderSpacing.Left = 8 BorderSpacing.Bottom = 2 Caption = 'Factor 3 Clark' - TabOrder = 24 + TabOrder = 23 end - object Factor3Edit: TEdit[29] + object Factor3Edit: TEdit[28] AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideRight.Control = ParamsPanel AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = Fact3Out AnchorSideBottom.Side = asrBottom - Left = 221 + Left = 177 Height = 23 Top = 254 - Width = 180 + Width = 136 Anchors = [akLeft, akRight, akBottom] BorderSpacing.Left = 8 BorderSpacing.Bottom = 12 ReadOnly = True - TabOrder = 25 + TabOrder = 24 Text = 'Factor3Edit' end end inherited ParamsSplitter: TSplitter - Left = 413 + Left = 325 Height = 545 end inherited PageControl: TPageControl - Left = 422 + Left = 334 Height = 529 - Width = 510 + Width = 598 end end diff --git a/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.pas b/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.pas index 8fd0230ed..529e59cc4 100644 --- a/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.pas +++ b/applications/lazstats/source/forms/analysis/comparisons/onecaseanovaunit.pas @@ -6,7 +6,8 @@ interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, - StdCtrls, Buttons, ExtCtrls, + StdCtrls, Buttons, ExtCtrls, ComCtrls, + TASources, TACustomSeries, TAStyles, MainUnit, Globals, DataProcs, GraphLib, ANOVATestsUnit, BasicStatsReportAndChartFormUnit; @@ -35,7 +36,6 @@ type NewmanKeulsChk: TCheckBox; OverAllAlphaEdit: TEdit; PostAlphaEdit: TEdit; - PlotOptionsGroup: TRadioGroup; ScheffeChk: TCheckBox; StaticText1: TStaticText; StaticText2: TStaticText; @@ -58,10 +58,10 @@ type private { private declarations } - NoSelected, N: integer; + N: integer; ColNoSelected: IntDyneVec; - DepVarCol, F1Col, F2Col, F3Col, Nf1cells, Nf2cells, Nf3cells: integer; - minf1, maxf1, minf2, maxf2, minf3, maxf3, nofactors, totcells: integer; + Nf1cells, Nf2cells, Nf3cells, TotalCells: integer; + minf1, maxf1, minf2, maxf2, minf3, maxf3, nofactors: integer; NoGrpsA, NoGrpsB, NoGrpsC: integer; SSDep, SSErr, SSF1, SSF2, SSF3, SSF1F2, SSF1F3, SSF2F3, SSF1F2F3: double; MSDep, MSErr, MSF1, MSF2, MSF3, MSF1F2, MSF1F3, MSF2F3: double; @@ -86,9 +86,12 @@ type SlcSums : DblDyneVec; // 3 way slice sums SlcCount : IntDyneVec; // 3 way slice counts OrdMeansA, OrdMeansB, OrdMeansC : DblDyneVec; // reordered means for f1, f2, f3 + DepValues: DblDyneVec; + F1Values, F2Values, F3Values: DblDyneVec; // ToDo: Make them a StrDyneVec +// F1Labels, F2Labels, F3Labels: StrDyneVec; OverAll, PostHocAlpha : double; // alphas for tests - wsum, wx2 : DblDyneCube; // : DblDyneCube - ncnt : IntDyneCube; // : IntDyneCube; + wsum, wx2: DblDyneCube; + ncnt : IntDyneCube; CompError : boolean; equal_grp : boolean; // check for equal groups for post-hoc tests comparisons : boolean; @@ -96,15 +99,24 @@ type procedure Init; procedure GetLevels; - procedure Calc2Way; + function Calc2Way: Boolean; procedure TwoWayTable(AReport: TStrings); procedure TwoWayContrasts(AReport: TStrings); procedure TwoWayPlot; - procedure Calc3Way; + function Calc3Way: Boolean; procedure ThreeWayTable(AReport: TStrings); procedure ThreeWayContrasts(AReport: TStrings); procedure ThreeWayPlot; + private + FSeries: TChartSeries; + FChartCombobox: TCombobox; + FStyles: TChartStyles; + procedure GetDataIndices(out ix, iy, iz: Integer); + procedure PopulateChartCombobox(ThreeWay: Boolean); + procedure SelectTwoWayPlot(Sender: TObject); + procedure SelectThreeWayPlot(Sender: TObject); + protected procedure AdjustConstraints; override; procedure Compute; override; @@ -125,11 +137,16 @@ implementation {$R *.lfm} uses - Math, MathUnit; + Math, + TACustomSource, TASeries, TALegend, TAChartUtils, + Utils, MathUnit, MatrixUnit, GridProcs, ChartFrameUnit; { TOneCaseAnovaForm } constructor TOneCaseAnovaForm.Create(AOwner: TComponent); +var + panel: TPanel; + lbl: TLabel; begin inherited; @@ -138,6 +155,31 @@ begin OverAllAlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); PostAlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); + + FStyles := TChartStyles.Create(FChartFrame); + + panel := TPanel.Create(FChartFrame.ChartToolbar); + lbl := TLabel.Create(panel); + lbl.Parent := panel; + lbl.Caption := 'Plots:'; + FChartCombobox := TCombobox.Create(panel); + FChartCombobox.Parent := panel; + FChartCombobox.Style := csDropdownList; + FChartCombobox.DropdownCount := 24; + FChartCombobox.Items.Clear; + FChartCombobox.Constraints.MinWidth := 300; + lbl.AnchorSideLeft.Side := asrTop; + lbl.AnchorSideLeft.Control := panel; + lbl.AnchorSideTop.Side := asrCenter; + lbl.AnchorSideTop.Control := FChartCombobox; + FChartCombobox.AnchorSideLeft.Control := lbl; + FChartCombobox.AnchorSideLeft.Side := asrBottom; + FChartCombobox.BorderSpacing.Left := 6; + FChartCombobox.BorderSpacing.Around := 2; + panel.Parent := FChartFrame.ChartToolbar; + panel.AutoSize := true; + panel.BevelInner := bvNone; + panel.BevelOuter := bvNone; end; @@ -148,8 +190,15 @@ begin ParamsPanel.Constraints.MinHeight := InteractChk.Top + InteractChk.Height + PostHocGroup.BorderSpacing.Top + PostHocGroup.Height + PostHocGroup.BorderSpacing.Bottom + PostAlphaEdit.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; - ParamsPanel.Constraints.MinWidth := - PostHocGroup.Width + PlotOptionsGroup.BorderSpacing.Left + PlotOptionsGroup.Width; + ParamsPanel.Constraints.MinWidth := Max( + 4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left, + PostHocGroup.Width + ); + + FChartFrame.ChartToolbar.ButtonHeight := Max( + FChartFrame.ChartToolbar.ButtonHeight, + FChartCombobox.Height + 2*FChartCombobox.BorderSpacing.Around + ); end; @@ -276,24 +325,10 @@ var msg: String; C: TWinControl; begin - NoFactors := 0; - if (Factor1Edit.Text <> '') and (Factor2Edit.Text <> '') then - begin - NoFactors := 2; - if (Factor3Edit.Text <> '') then - NoFactors := 3; - end; - if (NoFactors < 2) then - begin - MessageDlg('Selection of 2 or 3 factors required.', mtError, [mbOK], 0); - exit; - end; - - if not Validate(msg, C) then begin - C.SetFocus; - MessageDlg(msg, mtError, [mbOK], 0); - exit; - end; + if Factor3Edit.Text = '' then + NoFactors := 2 + else + NoFactors := 3; // initialize values Init; @@ -313,23 +348,21 @@ begin SetLength(ColCount, Nf2cells); // 2 way col count SetLength(OrdMeansA, Nf1cells); // ordered means for factor 1 SetLength(OrdMeansB, Nf2cells); // ordered means for factor 2 - Calc2Way; - if not CompError then + if Calc2Way then begin lReport := TStringList.Create; try TwoWayTable(lReport); TwoWayContrasts(lReport); FReportFrame.DisplayReport(lReport); - if PlotOptionsGroup.ItemIndex > 0 then - TwoWayPlot; + TwoWayPlot; finally lReport.Free; end; end; - vars := nil; - sums := nil; - counts := nil; +// vars := nil; +// sums := nil; +// counts := nil; end; 3 : begin // three way anova @@ -345,25 +378,25 @@ begin SetLength(wsum, Nf1cells, Nf2cells, Nf3cells); SetLength(wx2, Nf1cells, Nf2cells, Nf3cells); SetLength(ncnt, Nf1cells, Nf2cells, Nf3cells); - Calc3Way; - if not CompError then + if Calc3Way then begin lReport := TStringList.Create; try ThreeWayTable(lReport); ThreeWayContrasts(lReport); FReportFrame.DisplayReport(lReport); - if PlotOptionsGroup.ItemIndex > 0 then - ThreeWayPlot; + ThreeWayPlot; finally lReport.Free; end; + { ncnt := nil; wx2 := nil; wsum := nil; OrdMeansC := nil; SlcCount := nil; SlcSums := nil; + } end; end; end; // end switch @@ -378,11 +411,6 @@ begin comparisons := ScheffeChk.Checked or TukeyHSDChk.Checked or TukeyBChk.Checked or TukeyKramerChk.Checked or NewmanKeulsChk.Checked; - SetLength(ColNoSelected, NoVariables); - DepVarCol := 0; - F1Col := 0; - F2Col := 0; - F3Col := 0; SSDep := 0.0; SSF1 := 0.0; SSF2 := 0.0; @@ -399,7 +427,6 @@ begin Nf2cells := 0; Nf3cells := 0; //N := 0; - NoSelected := 0; minf1 := 0; maxf1 := 0; minf2 := 0; @@ -408,127 +435,97 @@ begin maxf3 := 0; // Get column numbers of dependent variable and factors - for i := 1 to NoVariables do - begin - cellstring := OS3MainFrm.DataGrid.Cells[i,0]; - if (cellstring = DepVarEdit.Text) then - begin - DepVarCol := i; - NoSelected := NoSelected + 1; - ColNoSelected[NoSelected-1] := DepVarCol; - end else - if (cellstring = Factor1Edit.Text) then - begin - F1Col := i; - NoSelected := NoSelected + 1; - ColNoSelected[NoSelected-1] := F1Col; - end else - if (cellstring = Factor2Edit.Text) then - begin - F2Col := i; - NoSelected := NoSelected + 1; - ColNoSelected[NoSelected-1] := F2Col; - end else - if (cellstring = Factor3Edit.Text) then - begin - F3Col := i; - NoSelected := NoSelected + 1; - ColNoSelected[NoSelected-1] := F3Col; - end; - end; + SetLength(ColNoSelected, NoFactors + 1); // +1 because DepVar is at Index 0 + ColNoSelected[0] := GetVariableIndex(OS3MainFrm.DataGrid, DepVarEdit.Text); + ColNoSelected[1] := GetVariableIndex(OS3MainFrm.DataGrid, Factor1Edit.Text); + ColNoSelected[2] := GetVariableIndex(OS3MainFrm.DataGrid, Factor2Edit.Text); + if Factor3Edit.Text <> '' then + ColNoSelected[3] := GetVariableIndex(OS3MainFrm.DataGrid, Factor3Edit.Text); + OverAll := StrToFloat(OverAllAlphaEdit.Text); PostHocAlpha := StrToFloat(PostAlphaEdit.Text); - end; +end; procedure TOneCaseAnovaForm.GetLevels; var i: integer; intValue: Integer; + mn, mx: Double; begin - minf1 := ceil(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F1Col,1]))); - maxf1 := minf1; - for i := 2 to NoCases do - begin - if not GoodRecord(i,NoSelected,ColNoSelected) then continue; - intValue := floor(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F1Col,i]))); - if (intValue > maxf1) then maxf1 := intValue; - if (intValue < minf1) then minf1 := intValue; - end; - Nf1cells := maxf1 - minf1 + 1; + // Extract dependent variable values + DepValues := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[0], ColNoSelected); - if (nofactors > 1) then - begin - minf2 := floor(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F2Col,1]))); - maxf2 := minf2; - for i := 2 to NoCases do - begin - if not GoodRecord(i,NoSelected,ColNoSelected) then continue; - intValue := floor(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F2Col,i]))); - if (intValue > maxf2) then maxf2 := intValue; - if (intValue < minf2) then minf2 := intValue; - end; - Nf2cells := maxf2 - minf2 + 1; - end; + // 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; // this can be wrong when codes are with gaps. - if (nofactors = 3) then - begin - minf3 := floor(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F3Col,1]))); - maxf3 := minf3; - for i := 2 to NoCases do - begin - if not GoodRecord(i,NoSelected,ColNoSelected) then continue; - intValue := floor(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F3Col,i]))); - if (intValue > maxf3) then maxf3 := intValue; - if (intValue < minf3) then minf3 := intValue; - end; - Nf3cells := maxf3 - minf3 + 1; - end; + // Extract factor 2 values + F2Values := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[2], ColNoSelected); + VecMaxMin(F2Values, mx, mn); + MaxF2 := round(mx); + MinF2 := round(mn); + NF2Cells := MaxF2 - MinF2 + 1; - totcells := Nf1cells + Nf2cells + Nf3cells; + // Extract factor 3 values + if NoFactors >= 3 then + begin + F3Values := CollectVecValues(OS3MainFrm.DataGrid, ColNoSelected[3], ColNoSelected); + VecMaxMin(F3Values, mx, mn); + MaxF3 := round(mx); + MinF3 := round(mn); + NF3Cells := MaxF3 - MinF2 + 1; + end else + NF3Cells := 0; + + + // !!!!!!!!!!! wp: the following is needed only in the *Contrasts methods --> Move to there !!!!!!!!!!!!!!! + + TotalCells := NF1Cells + NF2Cells + NF3Cells; // allocate space - SetLength(cellcnts, totcells); // array of cell counts - SetLength(cellvars, totcells); // arrray of cell sums of squares variances - SetLength(cellsums, totcells); // array of cell sums means + SetLength(CellCnts, TotalCells); // array of cell counts + SetLength(CellVars, TotalCells); // arrray of cell sums of squares variances + SetLength(CellSums, TotalCells); // array of cell sums means // initialize array values - for i := 1 to totcells do + for i := 0 to TotalCells-1 do begin - cellsums[i-1] := 0.0; - cellvars[i-1] := 0.0; - cellcnts[i-1] := 0; + CellSums[i] := 0.0; + CellVars[i] := 0.0; + CellCnts[i] := 0; end; end; -procedure TOneCaseAnovaForm.Calc2Way; +function TOneCaseAnovaForm.Calc2Way: Boolean; var i, j, grpA, grpB: integer; Constant, RowsTotCnt, ColsTotCnt, SSCells: double; - X, rowMean, colmean: Double; + X, Xsq, rowMean, colmean: Double; begin - CompError := false; - // initialize matrix values - NoGrpsA := maxf1 - minf1 + 1; - NoGrpsB := maxf2 - minf2 + 1; - for i := 1 to NoGrpsA do + NoGrpsA := MaxF1 - MinF1 + 1; + NoGrpsB := MaxF2 - MinF2 + 1; + for i := 0 to NoGrpsA-1 do begin - RowSums[i-1] := 0.0; - RowCount[i-1] := 0; - for j := 1 to NoGrpsB do + RowSums[i] := 0.0; + RowCount[i] := 0; + for j := 0 to NoGrpsB-1 do begin - counts[i-1,j-1] := 0; - sums[i-1,j-1] := 0.0; - vars[i-1,j-1] := 0.0; + counts[i, j] := 0; + sums[i, j] := 0.0; + vars[i, j] := 0.0; end; end; - for i := 1 to NoGrpsB do + for i := 0 to NoGrpsB-1 do begin - ColCount[i-1] := 0; - ColSums[i-1] := 0.0; + ColCount[i] := 0; + ColSums[i] := 0.0; end; N := 0; @@ -542,10 +539,29 @@ begin MSNonAdd := 0.0; MSBalance := 0.0; + // Get working totals + for i := 0 to High(DepValues) do + begin + grpA := round(F1Values[i]) - MinF1; + grpB := round(F2Values[i]) - MinF2; + X := DepValues[i]; + Xsq := X * X; + Counts[grpA, grpB] := counts[grpA, grpB] + 1; + sums[grpA, grpB] := sums[grpA, grpB] + X; + vars[grpA, grpB] := vars[grpA, grpB] + Xsq; + RowSums[grpA] := RowSums[grpA] + X; + ColSums[grpB] := ColSums[grpB] + X; + RowCount[grpA] := RowCount[grpA] + 1; + ColCount[grpB] := ColCount[grpB] + 1; + MeanDep := MeanDep + X; + SSDep := SSDep + Xsq; + N := N + 1; + end; + { // get working totals for i := 1 to NoCases do begin - if not GoodRecord(i,NoSelected,ColNoSelected) then continue; + if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; grpA := floor(StrToFloat(OS3MainFrm.DataGrid.Cells[F1Col,i])); grpB := floor(StrToFloat(OS3MainFrm.DataGrid.Cells[F2Col,i])); X := StrToFloat(OS3MainFrm.DataGrid.Cells[DepVarCol,i]); @@ -562,6 +578,7 @@ begin SSDep := SSDep + X * X; N := N + 1; end; + } // Calculate results for i := 0 to NoGrpsA - 1 do @@ -600,8 +617,8 @@ begin SSBalance := SSErr - SSNonAdd; if ((SSF1 < 0) or (SSF2 < 0)) then begin - MessageDlg('A negative SS found. Unbalanced design? Ending analysis.', mtInformation, [mbOK], 0); - CompError := true; + ErrorMsg('A negative SS found. Unbalanced design? Ending analysis.'); + Result := false; exit; end; @@ -637,6 +654,8 @@ begin if (OmegaF1 < 0.0) then OmegaF1 := 0.0; if (OmegaF2 < 0.0) then OmegaF2 := 0.0; if (Omega < 0.0) then Omega := 0.0; + + Result := True; end; @@ -690,16 +709,16 @@ begin begin if (counts[i,j] > 1) then begin - XBar := sums[i][j] / counts[i,j]; - V := vars[i][j] - sqr(sums[i,j]) / counts[i,j]; - V := V / (counts[i,j] - 1.0); + XBar := sums[i, j] / counts[i, j]; + V := vars[i, j] - sqr(sums[i, j]) / counts[i, j]; + V := V / (counts[i, j] - 1.0); S := sqrt(V); sumvars := sumvars + V; if (V > MaxVar) then MaxVar := V; if (V < MinVar) then MinVar := V; - sumDFrecip := sumDFrecip + 1.0 / (counts[i,j] - 1.0); + sumDFrecip := sumDFrecip + 1.0 / (counts[i, j] - 1.0); sumfreqlogvar := sumfreqlogvar + (counts[i,j] - 1.0) * ln(V); - if (counts[i,j] <> groupsize) then equal_grp := false; + if (counts[i, j] <> groupsize) then equal_grp := false; end else XBar := sums[i][j]; @@ -738,6 +757,135 @@ begin end; +procedure TOneCaseAnovaForm.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 + begin + FChartCombobox.Items.Add(c); + 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('Interaction %s*%s vs %s', [a, b, a])); + FChartCombobox.Items.Add(Format('Interaction %s*%s vs %s', [a, b, b])); + end; + + if idx = -1 then + FChartCombobox.ItemIndex := 0 + else + FChartCombobox.ItemIndex := Min(idx, FChartCombobox.Items.Count-1);; +end; + + +procedure TOneCaseAnovaForm.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); + 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); + 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.SetXTitle(Factor1Edit.Text); + 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.SetXTitle(Factor2Edit.Text); + 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; +end; + + +procedure TOneCaseAnovaForm.TwoWayPlot; +begin + FChartFrame.Clear; // this destroys the series + FChartFrame.SetYTitle('Means'); + FSeries := FChartFrame.PlotXY(ptBars, nil, nil, nil, nil, '', DATA_COLORS[0]); + TBarSeries(FSeries).Stacked := false; + PopulateChartCombobox(false); + FChartCombobox.OnChange := @SelectTwoWayPlot; + SelectTwoWayPlot(nil); +end; + +(* procedure TOneCaseAnovaForm.TwoWayPlot; var i, j: integer; @@ -858,16 +1006,14 @@ begin GraphFrm.Xpoints := nil; GraphFrm.Ypoints := nil; end; + *) - -procedure TOneCaseAnovaForm.Calc3Way; +function TOneCaseAnovaForm.Calc3Way: Boolean; var i, j, k, grpA, grpB, grpC: integer; Constant, RowsTotCnt, ColsTotCnt, SlcsTotCnt, SSCells, p, n2: double; - X, rowMean, colMean, sliceMean: Double; + X, Xsq, rowMean, colMean, sliceMean: Double; begin - CompError := false; - // initialize matrix values NoGrpsA := maxf1 - minf1 + 1; NoGrpsB := maxf2 - minf2 + 1; @@ -915,14 +1061,37 @@ begin SSCells := 0.0; SSNonAdd := 0.0; + // Get working totals + for i := 0 to High(DepValues) do + begin + grpA := round(F1Values[i]) - MinF1; + grpB := round(F2Values[i]) - MinF2; + grpC := round(F3Values[i]) - MinF3; + X := DepValues[i]; + Xsq := X * X; + ncnt[grpA, grpB, grpC] := ncnt[grpA, grpB, grpC] + 1; + wsum[grpA, grpB, grpC] := wsum[grpA, grpB, grpC] + X; + wx2[grpA, grpB, grpC] := wx2[grpA, grpB, grpC] + Xsq; + RowSums[grpA] := RowSums[grpA] + X; + ColSums[grpB] := ColSums[grpB] + X; + SlcSums[grpC] := SlcSums[grpC] + X; + RowCount[grpA] := RowCount[grpA] + 1; + Colcount[grpB] := ColCount[grpB] + 1; + SlcCount[grpC] := SlcCount[grpC] + 1; + MeanDep := MeanDep + X; + SSDep := SSDep + Xsq; + N := N + 1; + end; + + { // get working totals for i := 1 to NoCases do begin - if not GoodRecord(i,NoSelected,ColNoSelected) then continue; - grpA := floor(StrToFloat(OS3MainFrm.DataGrid.Cells[F1Col,i])); - grpB := floor(StrToFloat(OS3MainFrm.DataGrid.Cells[F2Col,i])); - grpC := floor(StrToFloat(OS3MainFrm.DataGrid.Cells[F3Col,i])); - X := StrToFloat(OS3MainFrm.DataGrid.Cells[DepVarCol,i]); + if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; + grpA := floor(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNoSelected[1],i])); + grpB := floor(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNoSelected[2],i])); + grpC := floor(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNoSelected[3],i])); + X := StrToFloat(OS3MainFrm.DataGrid.Cells[ColNoSelected[0],i]); grpA := grpA - minf1 + 1; grpB := grpB - minf2 + 1; grpC := grpC - minf3 + 1; @@ -939,12 +1108,13 @@ begin SSDep := SSDep + X * X; N := N + 1; end; + } // Calculate results Constant := (MeanDep * MeanDep) / N; GrandMean := MeanDep / N; - // get ss for rows + // Get ss for rows for i := 0 to NoGrpsA - 1 do begin SSF1 := SSF1 + RowSums[i] * RowSums[i] / RowCount[i]; @@ -952,7 +1122,7 @@ begin end; SSF1 := SSF1 - Constant; - // get ss for columns + // Get ss for columns for j := 0 to NoGrpsB - 1 do begin SSF2 := SSF2 + ColSums[j] * ColSums[j] / ColCount[j]; @@ -960,7 +1130,7 @@ begin end; SSF2 := SSF2 - Constant; - // get ss for slices + // Get ss for slices for k := 0 to NoGrpsC - 1 do begin SSF3 := SSF3 + SlcSums[k] * SlcSums[k] / SlcCount[k]; @@ -968,7 +1138,7 @@ begin end; SSF3 := SSF3 - Constant; - // get ss for row x col interaction + // Get ss for row x col interaction p := 0.0; n2 := 0.0; for i := 0 to NoGrpsA - 1 do @@ -987,7 +1157,7 @@ begin end; SSF1F2 := SSF1F2 - SSF1 - SSF2 - Constant; - // get ss for row x slice interaction + // Get ss for row x slice interaction for i := 0 to NoGrpsA - 1 do begin for k := 0 to NoGrpsC - 1 do @@ -1004,7 +1174,7 @@ begin end; SSF1F3 := SSF1F3 - SSF1 - SSF3 - Constant; - // get ss for columns x slices interaction + // Get ss for columns x slices interaction for j := 0 to NoGrpsB - 1 do begin for k := 0 to NoGrpsC - 1 do @@ -1046,10 +1216,10 @@ begin end; SSDep := SSDep - Constant; - if not InteractChk.Checked then - SSErr := SSDep - (SSF1 + SSF2 + SSF3) + if InteractChk.Checked then + SSErr := SSDep - (SSF1 + SSF2 + SSF3 + SSF1F2 + SSF1F3 + SSF2F3) else - SSErr := SSDep - (SSF1 + SSF2 + SSF3 + SSF1F2 + SSF1F3 + SSF2F3); + SSErr := SSDep - (SSF1 + SSF2 + SSF3); SSNonAdd := SSNonAdd * SSNonAdd / (SSF1 * SSF2 * SSF3); SSNonAdd := SSNonAdd * NoGrpsA * NoGrpsB * NoGrpsC * NoGrpsA * NoGrpsB * NoGrpsC; MSNonAdd := SSNonAdd; @@ -1057,8 +1227,8 @@ begin if ((SSF1 < 0.0) or (SSF2 < 0.0) or (SSF3 < 0.0) or (SSF1F2 < 0.0) or (SSF1F3 < 0.0) or (SSF2F3 < 0.0)) then begin - MessageDlg('A negative SS found. Unbalanced Design? Ending analysis.', mtInformation, [mbOK], 0); - CompError := true; + ErrorMsg('A negative SS found. Unbalanced Design? Ending analysis.'); + Result := false; exit; end; @@ -1069,10 +1239,10 @@ begin DFF1F2 := DFF1 * DFF2; DFF1F3 := DFF1 * DFF3; DFF2F3 := DFF2 * DFF3; - if not InteractChk.Checked then - DFErr := DFTot - DFF1 - DFF2 - DFF3 + if InteractChk.Checked then + DFErr := DFTot - DFF1 - DFF2 - DFF3 - DFF1F2 - DFF1F3 - DFF2F3 else - DFErr := DFTot - DFF1 - DFF2 - DFF3 - DFF1F2 - DFF1F3 - DFF2F3; + DFErr := DFTot - DFF1 - DFF2 - DFF3; DFBalance := DFErr - 1; MSF1 := SSF1 / DFF1; MSF2 := SSF2 / DFF2; @@ -1089,10 +1259,10 @@ begin OmegaF1F2 := (SSF1F2 - DFF1F2 * MSErr) / (SSDep + MSErr); OmegaF1F3 := (SSF1F3 - DFF1F3 * MSErr) / (SSDep + MSErr); OmegaF2F3 := (SSF2F3 - DFF2F3 * MSErr) / (SSDep + MSErr); - if not InteractChk.Checked then - Omega := OmegaF1 + OmegaF2 + OmegaF3 + if InteractChk.Checked then + Omega := OmegaF1 + OmegaF2 + OmegaF3 + OmegaF1F2 + OmegaF1F3 + OmegaF2F3 else - Omega := OmegaF1 + OmegaF2 + OmegaF3 + OmegaF1F2 + OmegaF1F3 + OmegaF2F3; + Omega := OmegaF1 + OmegaF2 + OmegaF3; MeanDep := MeanDep / N; FF1 := abs(MSF1 / MSErr); @@ -1129,6 +1299,8 @@ begin if (OmegaF1F3 < 0.0) then OmegaF1F3 := 0.0; if (OmegaF2F3 < 0.0) then OmegaF2F3 := 0.0; if (Omega < 0.0) then Omega := 0.0; + + Result := true; end; @@ -1238,6 +1410,269 @@ begin end; +procedure TOneCaseAnovaform.GetDataIndices(out ix, iy, iz: Integer); +var + index, i1, i2, i3: Integer; +begin + index := FChartComboBox.ItemIndex; + i1 := 3; + i2 := i1 + NF3Cells; + i3 := i2 + NF3Cells; + if (index >= i1) and (index < i3) then + begin + if index < i2 then + begin + ix := 1; + iy := 2; + iz := index - i1; + end else + begin + ix := 2; + iy := 1; + iz := index - i2; + end; + exit; + end; + + i1 := i3; + i2 := i1 + NF2Cells; + i3 := i2 + NF2Cells; + if (index >= i1) and (index < i3) then + begin + if index < i2 then + begin + ix := 1; + iy := 3; + iz := index - i1; + end else + begin + ix := 3; + iy := 1; + iz := index - i2; + end; + exit; + end; + + i1 := i3; + i2 := i1 + NF1Cells; + i3 := i2 + NF1Cells; + if (index >= i1) and (index < i3) then + begin + if index < i2 then + begin + ix := 2; + iy := 3; + iz := index - i1; + end else + begin + ix := 3; + iy := 2; + iz := index - i2; + end; + end; +end; + + +procedure TOneCaseAnovaForm.SelectThreeWayPlot(Sender: TObject); +var + i, j, k, idx, ix, iy, iz: 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); + 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); + FChartFrame.SetTitle(Factor2Edit.Text); + end; + 2: begin // Plot means vs factor C + FSeries.ListSource.YCount := 1; + for i := 0 to NF3Cells-1 do + FSeries.AddXY(minF3 + i, SlcSums[i] / SlcCount[i], IntToStr(MinF3 + i)); + FChartFrame.SetXTitle(Factor3Edit.Text); + FChartFrame.SetTitle(Factor3Edit.Text); + end; + else + GetDataIndices(ix, iy,iz); + if (ix = 1) and (iy = 2) then + begin + 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, wsum[i,j,iz] / ncnt[i,j,iz]); + end; + FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"' + LineEnding + '"%s" = %d', [ + Factor1Edit.Text, Factor2Edit.Text, Factor3Edit.Text, MinF3 + iz])); + 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 + else + if (ix = 2) and (iy = 1) then + begin + 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, wsum[i, j, iz] / ncnt[i, j, iz]); + end; + FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"' + LineEnding + '"%s" = %d', [ + Factor1Edit.Text, Factor2Edit.Text, Factor3Edit.Text, MinF3 + iz])); + 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 + else + if (ix = 1) and (iy = 3) then + begin + FSeries.ListSource.YCount := NF3Cells; + for i := 0 to NF1Cells-1 do + begin + idx := FSeries.AddXY(minF1 + i, NaN, IntToStr(minF1 + i)); + item := FSeries.Source.Item[idx]; + for k := 0 to NF3Cells-1 do + item^.SetY(k, wsum[i, iz, k] / ncnt[i, iz, k]); + end; + FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"' + LineEnding + '"%s" = %d', [ + Factor1Edit.Text, Factor3Edit.Text, Factor2Edit.Text, MinF2 + iz])); + FChartFrame.SetXTitle(Factor1Edit.Text + ' codes'); + for k := 0 to NF3Cells-1 do + with TChartStyle(FStyles.styles.Add) do + begin + Brush.Color := DATA_COLORS[k mod Length(DATA_COLORS)]; + UseBrush := True; + Text := Format('%s = %s', [Factor3Edit.Text, IntToStr(MinF3 + k)]); + end; + end + else + if (ix = 3) and (iy = 1) then + begin + FSeries.ListSource.YCount := NF1Cells; + for k := 0 to NF3Cells-1 do + begin + idx := FSeries.AddXY(minF3 + k, NaN, IntToStr(minF3 + k)); + item := FSeries.Source.Item[idx]; + for i := 0 to NF1Cells-1 do + item^.SetY(i, wsum[i, iz, k] / ncnt[i, iz, k]); + end; + FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"' + LineEnding + '"%s" = %d', [ + Factor1Edit.Text, Factor3Edit.Text, Factor2Edit.Text, MinF2 + iz])); + FChartFrame.SetXTitle(Factor3Edit.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 + else + if (ix = 2) and (iy = 3) then + begin + FSeries.ListSource.YCount := NF3Cells; + for j := 0 to NF2Cells-1 do + begin + idx := FSeries.AddXY(minF2 + j, NaN, IntToStr(minF2 + j)); + item := FSeries.Source.Item[idx]; + for k := 0 to NF3Cells-1 do + item^.SetY(k, wsum[iz, j, k] / ncnt[iz, j, k]); + end; + FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"' + LineEnding + '"%s" = %d', [ + Factor2Edit.Text, Factor3Edit.Text, Factor1Edit.Text, MinF1 + iz])); + FChartFrame.SetXTitle(Factor2Edit.Text + ' codes'); + for k := 0 to NF3Cells-1 do + with TChartStyle(FStyles.styles.Add) do + begin + Brush.Color := DATA_COLORS[k mod Length(DATA_COLORS)]; + UseBrush := True; + Text := Format('%s = %s', [Factor3Edit.Text, IntToStr(MinF3 + k)]); + end; + end + else + if (ix = 3) and (iy = 2) then + begin + FSeries.ListSource.YCount := NF2Cells; + for k := 0 to NF3Cells-1 do + begin + idx := FSeries.AddXY(minF3 + k, NaN, IntToStr(minF3 + k)); + item := FSeries.Source.Item[idx]; + for j := 0 to NF2Cells-1 do + item^.SetY(j, wsum[iz, j, k] / ncnt[iz, j, k]); + end; + FChartFrame.SetTitle(Format('Factor "%s" x Factor "%s"' + LineEnding + '"%s" = %d', [ + Factor2Edit.Text, Factor3Edit.Text, Factor1Edit.Text, MinF1 + iz])); + FChartFrame.SetXTitle(Factor3Edit.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 else + raise Exception.Create('Fatal error in SelectThreeWayPlot'); + end; + + 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; +end; + + +procedure TOneCaseAnovaForm.ThreeWayPlot; +var + clr: TColor; +begin + if FChartComboBox.ItemIndex = -1 then + clr := DATA_COLORS[0] + else + clr := DATA_COLORS[FChartComboBox.ItemIndex mod Length(DATA_COLORS)]; + FChartFrame.Clear; // this destroys the series + FChartFrame.SetYTitle('Means'); + FSeries := FChartFrame.PlotXY(ptBars, nil, nil, nil, nil, '', clr); + TBarSeries(FSeries).Stacked := false; + PopulateChartCombobox(true); + FChartCombobox.OnChange := @SelectThreeWayPlot; + SelectThreeWayPlot(nil); +end; + +(* procedure TOneCaseAnovaForm.ThreeWayPlot; var i, j, k: integer; @@ -1256,7 +1691,7 @@ begin else raise Exception.Create('Plot type not supported.'); end; - SetLength(XValue, totcells); + SetLength(XValue, TotalCells); // Factor A first GraphFrm.SetLabels[1] := 'FACTOR A'; @@ -1336,7 +1771,7 @@ begin GraphFrm.nosets := 1; GraphFrm.nbars := Nf3cells; GraphFrm.Heading := Factor3Edit.Text; - GraphFrm.XTitle := Factor2Edit.Text + ' Codes'; + GraphFrm.XTitle := Factor2Edit.Text + ' Codes'; // why not Factor3? GraphFrm.YTitle := 'Mean'; GraphFrm.barwideprop := 0.5; GraphFrm.AutoScaled := false; @@ -1475,6 +1910,7 @@ begin GraphFrm.Ypoints := nil; XValue := nil; end; +*) procedure TOneCaseAnovaForm.TwoWayContrasts(AReport: TStrings); @@ -1489,7 +1925,7 @@ begin if CompError then exit; - SetLength(variances, totcells); + SetLength(variances, TotalCells); alpha := StrToFloat(PostAlphaEdit.Text); // row comparisons @@ -1652,7 +2088,7 @@ begin if not (ScheffeChk.Checked or TukeyHSDChk.Checked or TukeyBChk.Checked or TukeyKramerChk.Checked or NewmanKeulsChk.Checked) then exit; - SetLength(variances, totcells); + SetLength(variances, TotalCells); // row comparisons if (Nf1cells > 2) and (ProbF1 < Alpha) then @@ -1951,6 +2387,29 @@ var X: Double; begin Result := false; + if (DepVarEdit.Text = '') then + begin + AMsg := 'Dependent variable is not selected.'; + AControl := VarList; + exit; + end; + + if (Factor1Edit.Text = '') then + begin + AMsg := 'Factor 1 variable is not selected.'; + AControl := VarList; + exit; + end; + + if (Factor2Edit.Text = '') then + begin + AMsg := 'Factor 2 variable is not selected.'; + AControl := VarList; + exit; + end; + + // Factor 3 can be left empty to distinguish betwee two-way and three-way ANOVA. + if (OverAllAlphaEdit.Text = '') then begin AControl := OverAllAlphaEdit; diff --git a/applications/lazstats/source/forms/mainunit.lfm b/applications/lazstats/source/forms/mainunit.lfm index ed7347c7d..3b5aae725 100644 --- a/applications/lazstats/source/forms/mainunit.lfm +++ b/applications/lazstats/source/forms/mainunit.lfm @@ -1,12 +1,12 @@ object OS3MainFrm: TOS3MainFrm Left = 484 - Height = 519 + Height = 520 Top = 194 Width = 725 HelpType = htKeyword HelpKeyword = 'html/TableofContents.htm' Caption = 'LazStats' - ClientHeight = 499 + ClientHeight = 500 ClientWidth = 725 Font.CharSet = ANSI_CHARSET Font.Color = clBlack @@ -120,7 +120,7 @@ object OS3MainFrm: TOS3MainFrm object PanelBottom: TPanel Left = 0 Height = 41 - Top = 458 + Top = 459 Width = 725 Align = alBottom BevelOuter = bvNone @@ -212,7 +212,7 @@ object OS3MainFrm: TOS3MainFrm end object DataGrid: TStringGrid Left = 8 - Height = 423 + Height = 424 Top = 35 Width = 709 Align = alClient