diff --git a/applications/lazstats/source/LazStats.lpi b/applications/lazstats/source/LazStats.lpi index 5abfc0e2c..808d43636 100644 --- a/applications/lazstats/source/LazStats.lpi +++ b/applications/lazstats/source/LazStats.lpi @@ -745,7 +745,7 @@ - + diff --git a/applications/lazstats/source/forms/analysis/nonparametric/kwanovaunit.lfm b/applications/lazstats/source/forms/analysis/nonparametric/kwanovaunit.lfm index 6d059b41a..24004756e 100644 --- a/applications/lazstats/source/forms/analysis/nonparametric/kwanovaunit.lfm +++ b/applications/lazstats/source/forms/analysis/nonparametric/kwanovaunit.lfm @@ -1,288 +1,251 @@ -object KWAnovaFrm: TKWAnovaFrm +inherited KWAnovaForm: TKWAnovaForm Left = 518 - Height = 365 + Height = 265 Top = 283 - Width = 421 + Width = 565 HelpType = htKeyword HelpKeyword = 'html/Kruskal-WallisOne-WayANOVA.htm' - AutoSize = True Caption = 'Kruskal-Wallis One Way ANOVA on Ranks' - ClientHeight = 365 - ClientWidth = 421 - OnActivate = FormActivate - OnCreate = FormCreate - OnShow = FormShow - Position = poMainFormCenter - LCLVersion = '2.1.0.0' - object Label1: TLabel - AnchorSideLeft.Control = Owner - AnchorSideTop.Control = Owner - Left = 8 - Height = 15 - Top = 8 - Width = 97 - BorderSpacing.Left = 8 - BorderSpacing.Top = 8 - Caption = 'Variables Available' - ParentColor = False + ClientHeight = 265 + ClientWidth = 565 + inherited ParamsPanel: TPanel + Height = 249 + Width = 363 + ClientHeight = 249 + ClientWidth = 363 + inherited CloseBtn: TButton + Left = 308 + Top = 224 + end + inherited ComputeBtn: TButton + Left = 224 + Top = 224 + end + inherited ResetBtn: TButton + Left = 162 + Top = 224 + end + inherited HelpBtn: TButton + Left = 103 + Top = 224 + end + inherited ButtonBevel: TBevel + Top = 208 + Width = 363 + end + object Label1: TLabel[5] + AnchorSideLeft.Control = ParamsPanel + AnchorSideTop.Control = ParamsPanel + Left = 0 + Height = 15 + Top = 0 + Width = 97 + Caption = 'Available Variables' + ParentColor = False + end + object Label2: TLabel[6] + AnchorSideLeft.Control = GrpEdit + AnchorSideBottom.Control = GrpEdit + Left = 202 + Height = 15 + Top = 21 + Width = 77 + Anchors = [akLeft, akBottom] + BorderSpacing.Bottom = 2 + Caption = 'Group Variable' + ParentColor = False + end + object Label3: TLabel[7] + AnchorSideLeft.Control = DepEdit + AnchorSideBottom.Control = DepEdit + Left = 202 + Height = 15 + Top = 101 + Width = 102 + Anchors = [akLeft, akBottom] + BorderSpacing.Bottom = 2 + Caption = 'Dependent Variable' + ParentColor = False + end + object VarList: TListBox[8] + AnchorSideLeft.Control = ParamsPanel + AnchorSideTop.Control = Label1 + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = GrpIn + AnchorSideBottom.Control = OptionsGroup + Left = 0 + Height = 130 + Top = 17 + Width = 160 + Anchors = [akTop, akLeft, akRight, akBottom] + BorderSpacing.Top = 2 + BorderSpacing.Right = 8 + BorderSpacing.Bottom = 12 + ItemHeight = 0 + OnDblClick = VarListDblClick + OnSelectionChange = VarListSelectionChange + TabOrder = 4 + end + object GrpIn: TBitBtn[9] + AnchorSideLeft.Control = ParamsPanel + AnchorSideLeft.Side = asrCenter + AnchorSideTop.Control = VarList + Left = 168 + Height = 26 + Top = 17 + Width = 26 + Images = MainDataModule.ImageList + ImageIndex = 1 + OnClick = GrpInClick + Spacing = 0 + TabOrder = 5 + end + object GrpOut: TBitBtn[10] + AnchorSideLeft.Control = ParamsPanel + AnchorSideLeft.Side = asrCenter + AnchorSideTop.Control = GrpIn + AnchorSideTop.Side = asrBottom + Left = 168 + Height = 26 + Top = 47 + Width = 26 + BorderSpacing.Top = 4 + Images = MainDataModule.ImageList + ImageIndex = 0 + OnClick = GrpOutClick + Spacing = 0 + TabOrder = 6 + end + object DepIn: TBitBtn[11] + AnchorSideLeft.Control = ParamsPanel + AnchorSideLeft.Side = asrCenter + AnchorSideTop.Control = GrpOut + AnchorSideTop.Side = asrBottom + Left = 168 + Height = 26 + Top = 97 + Width = 26 + BorderSpacing.Top = 24 + Images = MainDataModule.ImageList + ImageIndex = 1 + OnClick = DepInClick + Spacing = 0 + TabOrder = 7 + end + object DepOut: TBitBtn[12] + AnchorSideLeft.Control = ParamsPanel + AnchorSideLeft.Side = asrCenter + AnchorSideTop.Control = DepIn + AnchorSideTop.Side = asrBottom + Left = 168 + Height = 26 + Top = 127 + Width = 26 + BorderSpacing.Top = 4 + Images = MainDataModule.ImageList + ImageIndex = 0 + OnClick = DepOutClick + Spacing = 0 + TabOrder = 8 + end + object GrpEdit: TEdit[13] + AnchorSideLeft.Control = GrpIn + AnchorSideLeft.Side = asrBottom + AnchorSideRight.Control = ParamsPanel + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = GrpOut + AnchorSideBottom.Side = asrBottom + Left = 202 + Height = 23 + Top = 38 + Width = 161 + Anchors = [akLeft, akRight, akBottom] + BorderSpacing.Left = 8 + BorderSpacing.Bottom = 12 + ReadOnly = True + TabOrder = 9 + Text = 'GrpEdit' + end + object DepEdit: TEdit[14] + AnchorSideLeft.Control = DepOut + AnchorSideLeft.Side = asrBottom + AnchorSideRight.Control = ParamsPanel + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = DepOut + AnchorSideBottom.Side = asrBottom + Left = 202 + Height = 23 + Top = 118 + Width = 161 + Anchors = [akLeft, akRight, akBottom] + BorderSpacing.Left = 8 + BorderSpacing.Bottom = 12 + ReadOnly = True + TabOrder = 10 + Text = 'DepEdit' + end + object Label5: TLabel[15] + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = AlphaEdit + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = AlphaEdit + Left = 242 + Height = 15 + Top = 181 + Width = 45 + Anchors = [akTop, akRight] + BorderSpacing.Right = 8 + Caption = 'Alpha = ' + ParentColor = False + end + object AlphaEdit: TEdit[16] + AnchorSideTop.Control = DepOut + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = ParamsPanel + AnchorSideRight.Side = asrBottom + Left = 295 + Height = 23 + Top = 177 + Width = 68 + Alignment = taRightJustify + Anchors = [akTop] + BorderSpacing.Top = 24 + TabOrder = 11 + Text = 'AlphaEdit' + end + object OptionsGroup: TGroupBox[17] + AnchorSideLeft.Control = ParamsPanel + AnchorSideBottom.Control = ButtonBevel + Left = 0 + Height = 49 + Top = 159 + Width = 180 + Anchors = [akLeft, akBottom] + AutoSize = True + Caption = 'Post Hoc Comparisons Option' + ChildSizing.LeftRightSpacing = 12 + ChildSizing.TopBottomSpacing = 2 + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ClientHeight = 29 + ClientWidth = 176 + TabOrder = 12 + object MWUChk: TCheckBox + AnchorSideLeft.Control = OptionsGroup + AnchorSideTop.Control = OptionsGroup + Left = 12 + Height = 19 + Top = 2 + Width = 140 + BorderSpacing.Top = 2 + BorderSpacing.Bottom = 8 + Caption = 'Mann-Whitney U Tests' + TabOrder = 0 + end + end end - object Label2: TLabel - AnchorSideLeft.Control = GrpEdit - AnchorSideBottom.Control = GrpEdit - Left = 232 - Height = 15 - Top = 33 - Width = 77 - Anchors = [akLeft, akBottom] - BorderSpacing.Bottom = 2 - Caption = 'Group Variable' - ParentColor = False - end - object Label3: TLabel - AnchorSideLeft.Control = DepEdit - AnchorSideBottom.Control = DepEdit - Left = 232 - Height = 15 - Top = 125 - Width = 102 - Anchors = [akLeft, akBottom] - BorderSpacing.Bottom = 2 - Caption = 'Dependent Variable' - ParentColor = False - end - object VarList: TListBox - AnchorSideLeft.Control = Owner - AnchorSideTop.Control = Label1 - AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = GrpIn - AnchorSideBottom.Control = AlphaEdit - Left = 8 - Height = 260 - Top = 25 - Width = 180 - Anchors = [akTop, akLeft, akRight, akBottom] - BorderSpacing.Left = 8 - BorderSpacing.Top = 2 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 8 - ItemHeight = 0 - OnSelectionChange = VarListSelectionChange - TabOrder = 0 - end - object GrpIn: TBitBtn - AnchorSideLeft.Control = Owner - AnchorSideLeft.Side = asrCenter - AnchorSideTop.Control = VarList - Left = 196 - Height = 28 - Top = 25 - Width = 28 - Images = MainDataModule.ImageList - ImageIndex = 1 - OnClick = GrpInClick - Spacing = 0 - TabOrder = 1 - end - object GrpOut: TBitBtn - AnchorSideLeft.Control = Owner - AnchorSideLeft.Side = asrCenter - AnchorSideTop.Control = GrpIn - AnchorSideTop.Side = asrBottom - Left = 196 - Height = 28 - Top = 57 - Width = 28 - BorderSpacing.Top = 4 - Images = MainDataModule.ImageList - ImageIndex = 0 - OnClick = GrpOutClick - Spacing = 0 - TabOrder = 2 - end - object DepIn: TBitBtn - AnchorSideLeft.Control = Owner - AnchorSideLeft.Side = asrCenter - AnchorSideTop.Control = GrpOut - AnchorSideTop.Side = asrBottom - Left = 196 - Height = 28 - Top = 117 - Width = 28 - BorderSpacing.Top = 32 - Images = MainDataModule.ImageList - ImageIndex = 1 - OnClick = DepInClick - Spacing = 0 - TabOrder = 4 - end - object DepOut: TBitBtn - AnchorSideLeft.Control = Owner - AnchorSideLeft.Side = asrCenter - AnchorSideTop.Control = DepIn - AnchorSideTop.Side = asrBottom - Left = 196 - Height = 28 - Top = 149 - Width = 28 - BorderSpacing.Top = 4 - Images = MainDataModule.ImageList - ImageIndex = 0 - OnClick = DepOutClick - Spacing = 0 - TabOrder = 5 - end - object GrpEdit: TEdit - AnchorSideLeft.Control = GrpIn - AnchorSideLeft.Side = asrBottom - AnchorSideRight.Control = Owner - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = GrpOut - AnchorSideBottom.Side = asrBottom - Left = 232 - Height = 23 - Top = 50 - Width = 181 - Anchors = [akLeft, akRight, akBottom] - BorderSpacing.Left = 8 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 12 - ReadOnly = True - TabOrder = 3 - Text = 'GrpEdit' - end - object DepEdit: TEdit - AnchorSideLeft.Control = DepOut - AnchorSideLeft.Side = asrBottom - AnchorSideRight.Control = Owner - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = DepOut - AnchorSideBottom.Side = asrBottom - Left = 232 - Height = 23 - Top = 142 - Width = 181 - Anchors = [akLeft, akRight, akBottom] - BorderSpacing.Left = 8 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 12 - ReadOnly = True - TabOrder = 6 - Text = 'DepEdit' - end - object ResetBtn: TButton - AnchorSideRight.Control = ComputeBtn - AnchorSideBottom.Control = Owner - AnchorSideBottom.Side = asrBottom - Left = 208 - Height = 25 - Top = 332 - Width = 54 - Anchors = [akRight, akBottom] - AutoSize = True - BorderSpacing.Left = 12 - BorderSpacing.Top = 8 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 8 - Caption = 'Reset' - OnClick = ResetBtnClick - TabOrder = 9 - end - object ComputeBtn: TButton - AnchorSideRight.Control = CloseBtn - AnchorSideBottom.Control = Owner - AnchorSideBottom.Side = asrBottom - Left = 270 - Height = 25 - Top = 332 - Width = 76 - Anchors = [akRight, akBottom] - AutoSize = True - BorderSpacing.Top = 8 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 8 - Caption = 'Compute' - OnClick = ComputeBtnClick - TabOrder = 10 - end - object CloseBtn: TButton - AnchorSideRight.Control = Owner - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = Owner - AnchorSideBottom.Side = asrBottom - Left = 354 - Height = 25 - Top = 332 - Width = 55 - Anchors = [akRight, akBottom] - AutoSize = True - BorderSpacing.Top = 8 - BorderSpacing.Right = 12 - BorderSpacing.Bottom = 8 - Caption = 'Close' - ModalResult = 11 - TabOrder = 11 - end - object MWUChk: TCheckBox - AnchorSideLeft.Control = DepEdit - AnchorSideTop.Control = Label4 - AnchorSideTop.Side = asrBottom - Left = 232 - Height = 19 - Top = 226 - Width = 140 - BorderSpacing.Top = 2 - Caption = 'Mann-Whitney U Tests' - TabOrder = 7 - end - object Label4: TLabel - AnchorSideLeft.Control = DepEdit - AnchorSideTop.Control = DepOut - AnchorSideTop.Side = asrBottom - Left = 232 - Height = 15 - Top = 209 - Width = 161 - BorderSpacing.Top = 32 - Caption = 'Post Hoc Comparisons Option' - ParentColor = False - end - object Label5: TLabel - AnchorSideLeft.Side = asrBottom - AnchorSideTop.Control = AlphaEdit - AnchorSideTop.Side = asrCenter - AnchorSideRight.Control = AlphaEdit - Left = 51 - Height = 15 - Top = 297 - Width = 45 - Anchors = [akTop, akRight] - BorderSpacing.Right = 8 - Caption = 'Alpha = ' - ParentColor = False - end - object AlphaEdit: TEdit - AnchorSideRight.Control = VarList - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = Bevel1 - Left = 104 - Height = 23 - Top = 293 - Width = 84 - Alignment = taRightJustify - Anchors = [akLeft, akRight, akBottom] - TabOrder = 8 - Text = 'AlphaEdit' - end - object Bevel1: TBevel - AnchorSideLeft.Control = Owner - AnchorSideRight.Control = Owner - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = CloseBtn - Left = 0 - Height = 8 - Top = 316 - Width = 421 - Anchors = [akLeft, akRight, akBottom] - Shape = bsBottomLine + inherited ParamsSplitter: TSplitter + Left = 375 + Height = 265 end end diff --git a/applications/lazstats/source/forms/analysis/nonparametric/kwanovaunit.pas b/applications/lazstats/source/forms/analysis/nonparametric/kwanovaunit.pas index 875e87ced..0d52e03d4 100644 --- a/applications/lazstats/source/forms/analysis/nonparametric/kwanovaunit.pas +++ b/applications/lazstats/source/forms/analysis/nonparametric/kwanovaunit.pas @@ -7,23 +7,19 @@ unit KWANOVAUnit; interface uses - Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons, ExtCtrls, - MainUnit, OutputUnit, FunctionsLib, Globals, DataProcs; + MainUnit, FunctionsLib, Globals, DataProcs, BasicStatsReportFormUnit; type - { TKWAnovaFrm } + { TKWAnovaForm } - TKWAnovaFrm = class(TForm) + TKWAnovaForm = class(TBasicStatsReportForm) AlphaEdit: TEdit; - Bevel1: TBevel; - Label4: TLabel; + OptionsGroup: TGroupBox; Label5: TLabel; MWUChk: TCheckBox; - ResetBtn: TButton; - ComputeBtn: TButton; - CloseBtn: TButton; GrpEdit: TEdit; DepEdit: TEdit; GrpIn: TBitBtn; @@ -34,289 +30,226 @@ type Label2: TLabel; Label3: TLabel; VarList: TListBox; - procedure ComputeBtnClick(Sender: TObject); procedure DepInClick(Sender: TObject); procedure DepOutClick(Sender: TObject); - procedure FormActivate(Sender: TObject); - procedure FormCreate(Sender: TObject); - procedure FormShow(Sender: TObject); procedure GrpInClick(Sender: TObject); procedure GrpOutClick(Sender: TObject); - procedure ResetBtnClick(Sender: TObject); + procedure VarListDblClick(Sender: TObject); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); private - { private declarations } - FAutoSized: Boolean; - procedure UpdateBtnStates; + + protected + procedure AdjustConstraints; override; + procedure Compute; override; + procedure UpdateBtnStates; override; + function Validate(out AMsg: String; out AControl: TWinControl): Boolean; override; + public - { public declarations } + procedure Reset; override; end; var - KWAnovaFrm: TKWAnovaFrm; + KWAnovaForm: TKWAnovaForm; + implementation +{$R *.lfm} + uses - Math; + Math, + Utils, GridProcs, MatrixUnit; -{ TKWAnovaFrm } -procedure TKWAnovaFrm.ResetBtnClick(Sender: TObject); +{ TKWAnovaForm } + +procedure TKWAnovaForm.AdjustConstraints; +begin + inherited; + ParamsPanel.Constraints.MinWidth := Max( + 4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left, + 2*OptionsGroup.Width); + ParamsPanel.Constraints.MinHeight := DepOut.Top + DepOut.Height + + VarList.BorderSpacing.Bottom + OptionsGroup.Height + + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; +end; + + +procedure TKWAnovaForm.Compute; var - i: integer; + i, j, k, m, ind_var, dep_var, min_grp, max_grp, group, total_n : integer; + NoTies, NoTieGroups, nogroups, NoSelected, npairs, n1, n2 : integer; + largestN : integer; + ColNoSelected : IntdyneVec = nil; + group_count : IntDyneVec = nil; + Ranks: DblDyneMat = nil; + X : DblDyneMat = nil; + RankSums: DblDyneVec = nil; + score, t, SumT, Avg, Probchi, H, CorrectedH, value : double; + Correction, Temp, TieSum, alpha, U, U2, SD, z, prob : double; + cellstring, outline: string; + lReport: TStrings; begin - GrpEdit.Text := ''; - DepEdit.Text := ''; - AlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); - MWUChk.Checked := false; - VarList.Items.Clear; - for i := 1 to NoVariables do - VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); - UpdateBtnStates; -end; + alpha := StrToFloat(AlphaEdit.Text); -procedure TKWAnovaFrm.FormActivate(Sender: TObject); -var - w: Integer; -begin - if FAutoSized then - exit; + // Allocate array memory + SetLength(Ranks, NoCases, 2); + SetLength(X, NoCases, 2); - w := MaxValue([ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]); - ResetBtn.Constraints.MinWidth := w; - ComputeBtn.Constraints.MinWidth := w; - CloseBtn.Constraints.MinWidth := w; + // Get column numbers of the independent and dependent variables + ind_var := GetVariableIndex(OS3MainFrm.DataGrid, GrpEdit.Text); + dep_var := GetVariableIndex(OS3MainFrm.DataGrid, DepEdit.Text); + NoSelected := 2; + SetLength(ColNoSelected, 2); + ColNoSelected[0] := ind_var; + ColNoSelected[1] := dep_var; - VarList.Constraints.MinHeight := MWUChk.Top + MWUChk.Height - VarList.Top; + // Get minimum and maximum group codes + total_n := 0; + min_grp := 10000; //atoi(MainForm.Grid.Cells[ind_var,1].c_str); + max_grp := -10000; + for i := 1 to NoCases do + begin + if (not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected)) then continue; + group := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ind_var,i]))); + if (group < min_grp) then min_grp := group; + if (group > max_grp) then max_grp := group; + total_n := total_n + 1; + end; + nogroups := max_grp - min_grp + 1; - Constraints.MinWidth := Label4.Width * 2 + GrpIn.Width + 4* VarList.BorderSpacing.Left; - Constraints.MinHeight := Height; + NoTieGroups := 0; + SumT := 0.0; - FAutoSized := True; -end; + // Initialize arrays + SetLength(RankSums,nogroups); + SetLength(group_count,nogroups); + for i := 0 to nogroups-1 do + begin + group_count[i] := 0; + RankSums[i] := 0.0; + end; -procedure TKWAnovaFrm.FormCreate(Sender: TObject); -begin - Assert(OS3Mainfrm <> nil); -end; + // Setup for printer output + lReport := TStringList.Create; + try + lReport.Add('KRUSKAL-WALLIS ONE-WAY ANALYSIS OF VARIANCE'); + lReport.Add('See pages 184-194 in S. Siegel: Nonparametric Statistics for the Behavioral Sciences'); + lReport.Add(''); -procedure TKWAnovaFrm.FormShow(Sender: TObject); -begin - ResetBtnClick(self); -end; - -procedure TKWAnovaFrm.ComputeBtnClick(Sender: TObject); -var - i, j, k, m, ind_var, dep_var, min_grp, max_grp, group, total_n : integer; - NoTies, NoTieGroups, nogroups, NoSelected, npairs, n1, n2 : integer; - largestn : integer; - ColNoSelected : IntdyneVec = nil; - group_count : IntDyneVec = nil; - score, t, SumT, Avg, Probchi, H, CorrectedH, value : double; - Correction, Temp, TieSum, alpha, U, U2, SD, z, prob : double; - Ranks, X : DblDyneMat; - RankSums : DblDyneVec = nil; - cellstring, outline: string; - lReport: TStrings; -begin - // Check for data - if (NoVariables < 1) then - begin - MessageDlg('You must have grid data!', mtError, [mbOK], 0); - exit; - end; - - if GrpEdit.Text = '' then - begin - MessageDlg('Group variable not specified.', mtError, [mbOK], 0); - exit; - end; - - if DepEdit.Text = '' then - begin - MessageDlg('Dependent variable not selected.', mtError, [mbOK], 0); - exit; - end; - - if AlphaEdit.Text = '' then - begin - AlphaEdit.SetFocus; - MessageDlg('Alpha level not specified.', mtError, [mbOK], 0); - exit; - end; - if not TryStrToFloat(AlphaEdit.Text, alpha) or (alpha <= 0) or (alpha >= 1) then - begin - AlphaEdit.Setfocus; - MessageDlg('Alpha level must be a valid number between 0 and 1.', mtError, [mbOK], 0); - end; - - // allocate space - SetLength(ColNoSelected,NoVariables); - SetLength(Ranks,NoCases,2); - SetLength(X,NoCases,2); - - // Get column numbers of the independent and dependent variables - ind_var := 0; - dep_var := 0; - for i := 1 to NoVariables do - begin - cellstring := GrpEdit.Text; - if (cellstring = OS3MainFrm.DataGrid.Cells[i,0]) then ind_var := i; - cellstring := DepEdit.Text; - if (cellstring = OS3MainFrm.DataGrid.Cells[i,0]) then dep_var := i; - end; - ColNoSelected[0] := ind_var; - ColNoSelected[1] := dep_var; - - //get minimum and maximum group codes - total_n := 0; - NoSelected := 2; - min_grp := 10000; //atoi(MainForm.Grid.Cells[ind_var,1].c_str); - max_grp := -10000; + // Get data for i := 1 to NoCases do begin - if (not GoodRecord(i,NoSelected,ColNoSelected)) then continue; - group := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ind_var,i]))); - if (group < min_grp) then min_grp := group; - if (group > max_grp) then max_grp := group; - total_n := total_n + 1; + if (not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected)) then continue; + score := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[dep_var, i])); + group := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ind_var, i]))); + group := group - min_grp + 1; + if (group > nogroups) then + begin + ErrorMsg('Group codes must be sequential like 1 and 2!'); + exit; + end; + group_count[group-1] := group_count[group-1] + 1; + X[i-1, 0] := score; + X[i-1, 1] := group; end; - nogroups := max_grp - min_grp + 1; - NoTieGroups := 0; - SumT := 0.0; - H := 0.0; - // Initialize arrays - SetLength(RankSums,nogroups); - SetLength(group_count,nogroups); - for i := 0 to nogroups-1 do + //Sort all scores in ascending order + for i := 1 to total_n - 1 do begin - group_count[i] := 0; - RankSums[i] := 0.0; + for j := i + 1 to total_n do + begin + if (X[i-1,0] > X[j-1,0]) then + begin + Exchange(X[i-1, 0], X[j-1, 0]); + Exchange(X[i-1, 1], X[j-1, 1]); + end; + end; end; - // Setup for printer output - lReport := TStringList.Create; - try - lReport.Add('KRUSKAL-WALLIS ONE-WAY ANALYSIS OF VARIANCE'); - lReport.Add('See pages 184-194 in S. Siegel: Nonparametric Statistics for the Behavioral Sciences'); - lReport.Add(''); + // Store ranks + for i := 0 to total_n-1 do + begin + Ranks[i,0] := i+1; + Ranks[i,1] := X[i,1]; + end; - // Get data - for i := 1 to NoCases do + //Check for ties in ranks - replace with average rank and calculate + //T for each tie and sum of the T's + i := 1; + while i < total_n do + begin + j := i + 1; + TieSum := 0; + NoTies := 0; + while (j < total_n) do begin - if (not GoodRecord(i,NoSelected,ColNoSelected)) then continue; - score := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[dep_var,i])); - group := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ind_var,i]))); - group := group - min_grp + 1; - if (group > nogroups) then + if (X[j-1,0] > X[i-1,0]) then + break; + if (X[j-1,0] = X[i-1,0]) then // match begin - MessageDlg('Group codes must be sequential like 1 and 2!', mtError, [mbOk], 0); - exit; + TieSum := TieSum + round(Ranks[j-1,0]); + NoTies := NoTies + 1; end; - group_count[group-1] := group_count[group-1] + 1; - X[i-1,0] := score; - X[i-1,1] := group; + j := j + 1; end; - //Sort all scores in ascending order - for i := 1 to total_n - 1 do + if (NoTies > 0) then //At least one tie found begin - for j := i + 1 to total_n do - begin - if (X[i-1,0] > X[j-1,0]) then - begin - Temp := X[i-1,0]; - X[i-1,0] := X[j-1,0]; - X[j-1,0] := Temp; - Temp := X[i-1,1]; - X[i-1,1] := X[j-1,1]; - X[j-1,1] := Temp; - end; - end; + TieSum := TieSum + Ranks[i-1,0]; + NoTies := NoTies + 1; + Avg := TieSum / NoTies; + for j := i to i + NoTies - 1 do Ranks[j-1,0] := Avg; + t := Power(NoTies,3) - NoTies; + SumT := SumT + t; + NoTieGroups := NoTieGroups + 1; + i := i + (NoTies - 1); end; + i := i + 1; + end; // next i - // Store ranks - for i := 0 to total_n-1 do - begin - Ranks[i,0] := i+1; - Ranks[i,1] := X[i,1]; - end; + // Calculate sum of ranks in each group + for i := 0 to total_n-1 do + begin + group := round(Ranks[i, 1]); + RankSums[group-1] := RankSums[group-1] + Ranks[i, 0]; + end; - //Check for ties in ranks - replace with average rank and calculate - //T for each tie and sum of the T's - i := 1; - while i < total_n do - begin - j := i + 1; - TieSum := 0; - NoTies := 0; - while (j < total_n) do - begin - if (X[j-1,0] > X[i-1,0]) then - break; - if (X[j-1,0] = X[i-1,0]) then // match - begin - TieSum := TieSum + round(Ranks[j-1,0]); - NoTies := NoTies + 1; - end; - j := j + 1; - end; + // Calculate statistics + H := 0.0; + for j := 0 to nogroups-1 do + H := H + (RankSums[j] * RankSums[j] / (group_count[j])); + H := H * (12.0 / ( total_n * (total_n + 1)) ); + H := H - (3.0 * (total_n + 1)); + Correction := 1.0 - ( SumT / (Power(total_n,3) - total_n) ); + CorrectedH := H / Correction; + k := max_grp - min_grp; + probChi := 1.0 - ChiSquaredProb(H, k); - if (NoTies > 0) then //At least one tie found - begin - TieSum := TieSum + Ranks[i-1,0]; - NoTies := NoTies + 1; - Avg := TieSum / NoTies; - for j := i to i + NoTies - 1 do Ranks[j-1,0] := Avg; - t := Power(NoTies,3) - NoTies; - SumT := SumT + t; - NoTieGroups := NoTieGroups + 1; - i := i + (NoTies - 1); - end; - i := i + 1; - end; // next i - - // Calculate sum of ranks in each group - for i := 1 to total_n do - begin - group := round(Ranks[i-1,1]); - RankSums[group-1] := RankSums[group-1] + Ranks[i-1,0]; - end; - - // Calculate statistics - for j := 0 to nogroups-1 do H := H + (RankSums[j] * RankSums[j] / (group_count[j])); - H := H * (12.0 / ( total_n * (total_n + 1)) ); - H := H - (3.0 * (total_n + 1)); - Correction := 1.0 - ( SumT / (Power(total_n,3) - total_n) ); - CorrectedH := H / Correction; - k := max_grp - min_grp; - Probchi := 1.0 - chisquaredprob(H, k); - - // Report results - lReport.Add(' Score Rank Group'); - lReport.Add(''); - for i := 1 to total_n do - lReport.Add('%10.2f %10.2f %10.0f', [X[i-1,0], Ranks[i-1,0], Ranks[i-1,1]]); - lReport.Add(''); - lReport.Add('Sum of Ranks in each Group'); - lReport.Add('Group Sum No. in Group'); - for i := 1 to nogroups do - lReport.Add('%3d %10.2f %5d', [i+min_grp-1, RankSums[i-1], group_count[i-1]]); - lReport.Add(''); - lReport.Add('No. of tied rank groups = %3d', [NoTieGroups]); - lReport.Add('Statistic H uncorrected for ties: %8.4f', [H]); - lReport.Add('Correction for Ties: %8.4f', [Correction]); - lReport.Add('Statistic H corrected for ties: %8.4f', [CorrectedH]); - lReport.Add('Corrected H is approx. chi-square with %d D.F. and probability %.4f', [k, Probchi]); + // Report results + lReport.Add(' Score Rank Group'); + lReport.Add(''); + for i := 0 to total_n-1 do + lReport.Add('%10.2f %10.2f %10.0f', [X[i,0], Ranks[i,0], Ranks[i,1]]); + lReport.Add(''); + lReport.Add('Sum of Ranks in each Group'); + lReport.Add('Group Sum No. in Group'); + for i := 0 to noGroups-1 do + lReport.Add('%3d %10.2f %5d', [i+min_grp, RankSums[i], group_count[i]]); + lReport.Add(''); + lReport.Add('No. of tied rank groups %8d', [NoTieGroups]); + lReport.Add('Statistic H uncorrected for ties: %8.4f', [H]); + lReport.Add('Correction for Ties: %8.4f', [Correction]); + lReport.Add('Statistic H corrected for ties: %8.4f', [CorrectedH]); + lReport.Add('Corrected H is approx. chi-square with %d degrees of freedom and probability %.4f', [k, ProbChi]); if MWUChk.Checked then begin lReport.Add(''); lReport.Add('------------------------------------------------------------------------'); lReport.Add(''); - // do Mann-Whitney U tests on group pairs + + // Do Mann-Whitney U tests on group pairs alpha := StrToFloat(AlphaEdit.Text); npairs := nogroups * (nogroups - 1) div 2; alpha := alpha / npairs; @@ -340,9 +273,9 @@ begin total_n := 0; for k := 1 to NoCases do begin - if (not GoodRecord(k,NoSelected,ColNoSelected)) then continue; - score := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[dep_var,k])); - value := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ind_var,k])); + if (not GoodRecord(OS3MainFrm.DataGrid, k, ColNoSelected)) then continue; + score := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[dep_var, k])); + value := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ind_var, k])); if round(value) = i then begin X[total_n,0] := score; @@ -377,10 +310,10 @@ begin end; // get ranks for these two groups - for k := 1 to total_n do + for k := 0 to total_n-1 do begin - Ranks[k-1,0] := k; - Ranks[k-1,1] := X[k-1,1]; + Ranks[k,0] := k+1; + Ranks[k,1] := X[k,1]; end; //Check for ties in ranks - replace with average rank and calculate @@ -444,7 +377,7 @@ begin else z := (U - (n1 * n2 / 2)) / SD; prob := 1.0 - probz(z); - //Report results + // Report results lReport.Add(' Score Rank Group'); lReport.Add(''); for k := 1 to total_n do @@ -458,8 +391,8 @@ begin lReport.Add('%3d %10.3f %5d', [j, RankSums[group], group_count[1]]); lReport.Add(''); lReport.Add( 'No. of tied rank groups: %8d', [NoTieGroups]); - if (n1 > n2) then largestn := n1 else largestn := n2; - if (largestn < 20) then + if (n1 > n2) then largestN := n1 else largestN := n2; + if (largestN < 20) then outline := Format( 'Statistic U: %8.4f',[U]) else begin @@ -480,20 +413,16 @@ begin end; // next group i end; - if lReport.Count > 0 then - DisplayReport(lReport); + if lReport.Count > 0 then + FReportFrame.DisplayReport(lReport); - finally - lReport.Free; - group_count := nil; - RankSums := nil; - X := nil; - Ranks := nil; - ColNoSelected := nil; - end; + finally + lReport.Free; + end; end; -procedure TKWAnovaFrm.DepInClick(Sender: TObject); + +procedure TKWAnovaForm.DepInClick(Sender: TObject); var index: integer; begin @@ -506,7 +435,8 @@ begin UpdateBtnStates; end; -procedure TKWAnovaFrm.DepOutClick(Sender: TObject); + +procedure TKWAnovaForm.DepOutClick(Sender: TObject); begin if DepEdit.Text <> '' then begin @@ -516,7 +446,8 @@ begin UpdateBtnStates; end; -procedure TKWAnovaFrm.GrpInClick(Sender: TObject); + +procedure TKWAnovaForm.GrpInClick(Sender: TObject); var index: integer; begin @@ -529,7 +460,8 @@ begin UpdateBtnStates; end; -procedure TKWAnovaFrm.GrpOutClick(Sender: TObject); + +procedure TKWAnovaForm.GrpOutClick(Sender: TObject); begin if GrpEdit.Text <> '' then begin @@ -539,21 +471,102 @@ begin UpdateBtnStates; end; -procedure TKWAnovaFrm.UpdateBtnStates; + +procedure TKWAnovaForm.Reset; +var + i: integer; begin + inherited; + + GrpEdit.Clear; + DepEdit.Clear; + AlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); + MWUChk.Checked := false; + VarList.Items.Clear; + for i := 1 to NoVariables do + VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); + UpdateBtnStates; +end; + + +procedure TKWAnovaForm.UpdateBtnStates; +begin + inherited; GrpIn.Enabled := (VarList.Items.Count > 0) and (GrpEdit.Text = ''); DepIn.Enabled := (VarList.Items.Count > 0) and (DepEdit.Text = ''); GrpOut.Enabled := (GrpEdit.Text <> ''); DepOut.Enabled := (DepEdit.Text <> ''); end; -procedure TKWAnovaFrm.VarListSelectionChange(Sender: TObject; User: boolean); + +function TKWAnovaForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; +var + x: Double; +begin + Result := false; + + if (NoVariables < 1) then + begin + AMsg := 'You must have grid data!'; + AControl := VarList; + exit; + end; + + if GrpEdit.Text = '' then + begin + AMsg := 'Group variable not specified.'; + AControl := GrpEdit; + exit; + end; + + if DepEdit.Text = '' then + begin + AMsg := 'Dependent variable not selected.'; + AControl := DepEdit; + exit; + end; + + if AlphaEdit.Text = '' then + begin + AMsg := 'Alpha level not specified.'; + AControl := AlphaEdit; + exit; + end; + if not TryStrToFloat(AlphaEdit.Text, x) or (x <= 0) or (x >= 1) then + begin + AMsg := 'Alpha level must be a valid number between 0 and 1.'; + AControl := AlphaEdit; + exit; + end; + + Result := true; +end; + + +procedure TKWAnovaForm.VarListDblClick(Sender: TObject); +var + index: Integer; + s: String; +begin + index := VarList.ItemIndex; + if index > -1 then + begin + s := VarList.Items[index]; + if GrpEdit.Text = '' then + GrpEdit.Text := s + else if DepEdit.Text = '' then + DepEdit.Text := s; + VarList.Items.Delete(index); + UpdateBtnStates; + end; +end; + + +procedure TKWAnovaForm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; -initialization - {$I kwanovaunit.lrs} end. diff --git a/applications/lazstats/source/forms/mainunit.pas b/applications/lazstats/source/forms/mainunit.pas index a420c04eb..cf64347c9 100644 --- a/applications/lazstats/source/forms/mainunit.pas +++ b/applications/lazstats/source/forms/mainunit.pas @@ -1859,9 +1859,9 @@ end; // Menu "Analysis" > "Nonparametric" > "Kruskal-Wallis One-Way mnuAnalysisComp_Anova" procedure TOS3MainFrm.mnuAnalysisNonPar_KruskalWallisClick(Sender: TObject); begin - if KWAnovaFrm = nil then - Application.CreateForm(TKWAnovaFrm, KWAnovaFrm); - KWAnovaFrm.ShowModal; + if KWAnovaForm = nil then + Application.CreateForm(TKWAnovaForm, KWAnovaForm); + KWAnovaForm.Show; end; // Menu "Analysis" > "Nonparametric" > "Matched Pairs Signed Ranks Test"