From 7ea76148e553c7d3731e3a58abd4a12eb59ae332 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Thu, 5 Nov 2020 13:38:59 +0000 Subject: [PATCH] LazStats: Refactor tTestUnit. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7845 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../forms/analysis/comparisons/ttestunit.lfm | 98 ++--- .../forms/analysis/comparisons/ttestunit.pas | 354 +++++++++--------- .../lazstats/source/units/gridprocs.pas | 40 ++ .../lazstats/source/units/matrixunit.pas | 26 ++ 4 files changed, 300 insertions(+), 218 deletions(-) diff --git a/applications/lazstats/source/forms/analysis/comparisons/ttestunit.lfm b/applications/lazstats/source/forms/analysis/comparisons/ttestunit.lfm index be49fb42f..db45fa01d 100644 --- a/applications/lazstats/source/forms/analysis/comparisons/ttestunit.lfm +++ b/applications/lazstats/source/forms/analysis/comparisons/ttestunit.lfm @@ -1,7 +1,7 @@ inherited TtestForm: TTtestForm - Left = 947 + Left = 740 Height = 546 - Top = 176 + Top = 175 Width = 889 HelpType = htKeyword HelpKeyword = 'html/tTests.htm' @@ -40,7 +40,7 @@ inherited TtestForm: TTtestForm end object Label1: TLabel[5] AnchorSideLeft.Control = ParamsPanel - AnchorSideTop.Control = CInterval + AnchorSideTop.Control = CIntervalEdit AnchorSideTop.Side = asrCenter Left = 0 Height = 15 @@ -49,7 +49,7 @@ inherited TtestForm: TTtestForm Caption = 'Percent Confidence Interval:' ParentColor = False end - object CInterval: TEdit[6] + object CIntervalEdit: TEdit[6] AnchorSideLeft.Control = Label1 AnchorSideLeft.Side = asrBottom AnchorSideTop.Control = TailCombo @@ -76,12 +76,12 @@ inherited TtestForm: TTtestForm Height = 335 Top = 154 Width = 368 - PageIndex = 0 + PageIndex = 1 Anchors = [akTop, akLeft, akRight, akBottom] TabOrder = 4 object Page1: TPage object Mean1Label: TLabel - AnchorSideLeft.Control = Mean1 + AnchorSideLeft.Control = Mean1Edit AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Page1 Left = 83 @@ -93,7 +93,7 @@ inherited TtestForm: TTtestForm ParentColor = False end object Mean2Label: TLabel - AnchorSideTop.Control = Mean1 + AnchorSideTop.Control = Mean1Edit AnchorSideTop.Side = asrBottom AnchorSideRight.Control = Mean1Label AnchorSideRight.Side = asrBottom @@ -107,7 +107,7 @@ inherited TtestForm: TTtestForm ParentColor = False end object SD1Label: TLabel - AnchorSideLeft.Control = SD1 + AnchorSideLeft.Control = SD1Edit AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Mean1Label Left = 157 @@ -119,7 +119,7 @@ inherited TtestForm: TTtestForm ParentColor = False end object SD2Label: TLabel - AnchorSideLeft.Control = SD2 + AnchorSideLeft.Control = SD2Edit AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Mean2Label Left = 157 @@ -131,7 +131,7 @@ inherited TtestForm: TTtestForm ParentColor = False end object SampSize1Label: TLabel - AnchorSideLeft.Control = N1 + AnchorSideLeft.Control = N1Edit AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Mean1Label Left = 231 @@ -143,7 +143,7 @@ inherited TtestForm: TTtestForm ParentColor = False end object SampSize2Label: TLabel - AnchorSideLeft.Control = N2 + AnchorSideLeft.Control = N2Edit AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Mean2Label Left = 231 @@ -155,9 +155,9 @@ inherited TtestForm: TTtestForm ParentColor = False end object Cor12Label: TLabel - AnchorSideLeft.Control = Cor12 + AnchorSideLeft.Control = Cor12Edit AnchorSideLeft.Side = asrCenter - AnchorSideTop.Control = SD2 + AnchorSideTop.Control = SD2Edit AnchorSideTop.Side = asrBottom Left = 111 Height = 15 @@ -168,11 +168,11 @@ inherited TtestForm: TTtestForm Caption = 'Correlation Between Scores:' ParentColor = False end - object Mean1: TEdit + object Mean1Edit: TEdit AnchorSideLeft.Side = asrBottom AnchorSideTop.Control = Mean1Label AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = SD1 + AnchorSideRight.Control = SD1Edit Left = 77 Height = 23 Top = 49 @@ -183,13 +183,13 @@ inherited TtestForm: TTtestForm BorderSpacing.Top = 2 BorderSpacing.Right = 32 TabOrder = 0 - Text = 'Mean1' + Text = 'Mean1Edit' end - object Mean2: TEdit - AnchorSideLeft.Control = Mean1 + object Mean2Edit: TEdit + AnchorSideLeft.Control = Mean1Edit AnchorSideTop.Control = Mean2Label AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = Mean1 + AnchorSideRight.Control = Mean1Edit AnchorSideRight.Side = asrBottom Left = 77 Height = 23 @@ -199,12 +199,12 @@ inherited TtestForm: TTtestForm Anchors = [akTop, akRight] BorderSpacing.Top = 2 TabOrder = 1 - Text = 'Mean2' + Text = 'Mean2Edit' end - object SD1: TEdit + object SD1Edit: TEdit AnchorSideLeft.Control = Page1 AnchorSideLeft.Side = asrCenter - AnchorSideTop.Control = Mean1 + AnchorSideTop.Control = Mean1Edit Left = 159 Height = 23 Top = 49 @@ -212,12 +212,12 @@ inherited TtestForm: TTtestForm Alignment = taRightJustify BorderSpacing.Left = 6 TabOrder = 2 - Text = 'SD1' + Text = 'SD1Edit' end - object SD2: TEdit - AnchorSideLeft.Control = SD1 - AnchorSideTop.Control = Mean2 - AnchorSideRight.Control = SD1 + object SD2Edit: TEdit + AnchorSideLeft.Control = SD1Edit + AnchorSideTop.Control = Mean2Edit + AnchorSideRight.Control = SD1Edit AnchorSideRight.Side = asrBottom Left = 159 Height = 23 @@ -226,12 +226,12 @@ inherited TtestForm: TTtestForm Alignment = taRightJustify Anchors = [akTop, akRight] TabOrder = 3 - Text = 'SD2' + Text = 'SD2Edit' end - object N1: TEdit - AnchorSideLeft.Control = SD1 + object N1Edit: TEdit + AnchorSideLeft.Control = SD1Edit AnchorSideLeft.Side = asrBottom - AnchorSideTop.Control = Mean1 + AnchorSideTop.Control = Mean1Edit Left = 241 Height = 23 Top = 49 @@ -239,12 +239,12 @@ inherited TtestForm: TTtestForm Alignment = taRightJustify BorderSpacing.Left = 32 TabOrder = 4 - Text = 'N1' + Text = 'N1Edit' end - object N2: TEdit - AnchorSideLeft.Control = N1 - AnchorSideTop.Control = Mean2 - AnchorSideRight.Control = N1 + object N2Edit: TEdit + AnchorSideLeft.Control = N1Edit + AnchorSideTop.Control = Mean2Edit + AnchorSideRight.Control = N1Edit AnchorSideRight.Side = asrBottom Left = 241 Height = 23 @@ -253,10 +253,10 @@ inherited TtestForm: TTtestForm Alignment = taRightJustify Anchors = [akTop, akRight] TabOrder = 5 - Text = 'N2' + Text = 'N2Edit' end - object Cor12: TEdit - AnchorSideLeft.Control = SD1 + object Cor12Edit: TEdit + AnchorSideLeft.Control = SD1Edit AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Cor12Label AnchorSideTop.Side = asrBottom @@ -267,7 +267,7 @@ inherited TtestForm: TTtestForm Alignment = taRightJustify BorderSpacing.Top = 2 TabOrder = 6 - Text = 'Cor12' + Text = 'Cor12Edit' end end object Page2: TPage @@ -277,8 +277,8 @@ inherited TtestForm: TTtestForm Left = 0 Height = 15 Top = 0 - Width = 83 - Caption = 'Select Variables:' + Width = 97 + Caption = 'Available Variables' ParentColor = False end object VarList: TListBox @@ -307,12 +307,12 @@ inherited TtestForm: TTtestForm Left = 203 Height = 15 Top = 25 - Width = 62 + Width = 59 Alignment = taRightJustify Anchors = [akLeft, akBottom] BorderSpacing.Right = 8 BorderSpacing.Bottom = 2 - Caption = '1st Variable:' + Caption = '1st Variable' ParentColor = False end object Var2Label: TLabel @@ -321,11 +321,11 @@ inherited TtestForm: TTtestForm Left = 203 Height = 15 Top = 101 - Width = 67 + Width = 64 Alignment = taRightJustify Anchors = [akLeft, akBottom] BorderSpacing.Right = 8 - Caption = '2nd Variable:' + Caption = '2nd Variable' ParentColor = False end object GrpLabel: TLabel @@ -335,12 +335,12 @@ inherited TtestForm: TTtestForm Left = 203 Height = 15 Top = 177 - Width = 67 + Width = 77 Alignment = taRightJustify Anchors = [akLeft, akBottom] BorderSpacing.Right = 8 BorderSpacing.Bottom = 2 - Caption = 'Group Code:' + Caption = 'Group Variable' ParentColor = False end object Bevel5: TBevel @@ -651,7 +651,7 @@ inherited TtestForm: TTtestForm end object Bevel3: TBevel[10] AnchorSideLeft.Control = ParamsPanel - AnchorSideTop.Control = CInterval + AnchorSideTop.Control = CIntervalEdit AnchorSideTop.Side = asrBottom AnchorSideRight.Control = ParamsPanel AnchorSideRight.Side = asrBottom diff --git a/applications/lazstats/source/forms/analysis/comparisons/ttestunit.pas b/applications/lazstats/source/forms/analysis/comparisons/ttestunit.pas index b943cacfa..baa833598 100644 --- a/applications/lazstats/source/forms/analysis/comparisons/ttestunit.pas +++ b/applications/lazstats/source/forms/analysis/comparisons/ttestunit.pas @@ -31,8 +31,8 @@ type Page1: TPage; Page2: TPage; Cor12Label: TLabel; - Cor12: TEdit; - CInterval: TEdit; + Cor12Edit: TEdit; + CIntervalEdit: TEdit; GrpEdit: TEdit; Label1: TLabel; Var1In: TBitBtn; @@ -44,16 +44,16 @@ type Var2Label: TLabel; VarList: TListBox; SelVarLabel: TLabel; - N2: TEdit; - N1: TEdit; + N2Edit: TEdit; + N1Edit: TEdit; SampSize2Label: TLabel; SampSize1Label: TLabel; - SD2: TEdit; - SD1: TEdit; + SD2Edit: TEdit; + SD1Edit: TEdit; SD2Label: TLabel; SD1Label: TLabel; - Mean2: TEdit; - Mean1: TEdit; + Mean2Edit: TEdit; + Mean1Edit: TEdit; Mean2Label: TLabel; Mean1Label: TLabel; DataEntryGroup: TRadioGroup; @@ -75,7 +75,13 @@ type private independent: boolean; griddata: boolean; - + + function Eval_DependentGridData(out AMean1, AMean2, AVariance1, AVariance2, + AStdDev1, AStdDev2, ACovar12, r12: Double; out ANumCases1, ANumCases2: Integer): Boolean; + + function Eval_IndependentGridData(out AMean1, AMean2, AVariance1, AVariance2, + AStdDev1, AStdDev2: Double; out ANumCases1, ANumCases2: Integer; + out ALabel1Str, ALabel2Str: String): Boolean; protected procedure AdjustConstraints; override; procedure Compute; override; @@ -97,7 +103,7 @@ implementation uses Math, - MathUnit, GridProcs; + Utils, MathUnit, MatrixUnit, GridProcs; { TTtestForm } @@ -105,7 +111,7 @@ uses constructor TTtestForm.Create(AOwner: TComponent); begin inherited; - CInterval.Text := FormatFloat('0.0', DEFAULT_CONFIDENCE_LEVEL_PERCENT); + CIntervalEdit.Text := FormatFloat('0.0', DEFAULT_CONFIDENCE_LEVEL_PERCENT); end; @@ -119,7 +125,7 @@ begin ); ParamsPanel.Constraints.MinHeight := DataEntryGroup.Height + TailCombo.BorderSpacing.Top + TailCombo.Height + - CInterval.BorderSpacing.Top + CInterval.Height + CInterval.BorderSpacing.Bottom + + CIntervalEdit.BorderSpacing.Top + CIntervalEdit.Height + CIntervalEdit.BorderSpacing.Bottom + Bevel3.Height + Bevel3.BorderSpacing.Bottom + GrpIn.Top + GroupCodeGroup.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; @@ -128,154 +134,58 @@ end; procedure TTtestForm.Compute; var - M1, M2, Dif, stddev1, stddev2, r12, stderr1, stderr2: double; + mean1, mean2, Dif, stddev1, stddev2, r12, stderr1, stderr2: double; tequal, tunequal, cov12, lowci, hici, F, Fp, df1, df2: double; tprobability, value1, value2: double; variance1, variance2, pooled, sedif, df, ConfInt, tconfint: double; i, nCases1, nCases2: integer; group, min, max: integer; + minf, maxf: Double; ColNoSelected: IntDyneVec = nil; label1Str, label2Str: string; lReport: TStrings; begin - SetLength(ColNoSelected,NoVariables); - ncases1 := 0; - ncases2 := 0; - variance1 := 0.0; - variance2 := 0.0; - M1 := 0.0; - M2 := 0.0; - Dif := 0.0; - r12 := 0.0; - stddev1 := 0.0; - stddev2 := 0.0; - - ConfInt := (100.0 - StrToFloat(CInterval.Text)) / 2.0 ; + ConfInt := (100.0 - StrToFloat(CIntervalEdit.Text)) / 2.0 ; ConfInt := (100.0 - ConfInt) / 100.0; // one tail - if independent then - Var2Edit.Text := GrpEdit.Text; - - // data read from grid if griddata then begin - SetLength(ColNoSelected, 2); - ColNoSelected[0] := GetVariableIndex(OS3MainFrm.DataGrid, Var1Edit.Text); - ColNoSelected[1] := GetVariableIndex(OS3MainFrm.DataGrid, Var2Edit.Text); - - ncases1 := 0; - ncases2 := 0; - M1 := 0.0; - M2 := 0.0; - variance1 := 0.0; - variance2 := 0.0; - r12 := 0.0; - if not independent then // correlated data - begin - for i := 1 to NoCases do - begin - if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; - ncases1 := ncases1 + 1; - value1 := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[0], i])); - value2 := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i])); - M1 := M1 + value1; - M2 := M2 + value2; - variance1 := variance1 + value1 * value1; - variance2 := variance2 + value2 * value2; - r12 := r12 + value1 * value2; - end; - - ncases2 := ncases1; - variance1 := variance1 - (M1 * M1 / ncases1); - variance1 := variance1 / (ncases1 - 1); - stddev1 := sqrt(variance1); - variance2 := variance2 - (M2 * M2 / ncases2); - variance2 := variance2 / (ncases2 - 1); - stddev2 := sqrt(variance2); - r12 := r12 - (M1 * M2 / ncases1); - r12 := r12 / (ncases1 - 1); - cov12 := r12; - r12 := r12 / (stddev1 * stddev2); - M1 := M1 / ncases1; - M2 := M2 / ncases2; - Dif := M1 - M2; - end; //if not independent - + // Read and analyze data from grid if independent then begin - if GroupCodeChk.Checked then - begin - min := StrToInt(Grp1CodeEdit.Text); - max := StrToInt(Grp2CodeEdit.Text); - end else - begin - min := MaxInt; - max := min; - end; - - for i := 1 to NoCases do - begin - if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; - group := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i]))); - if not GroupCodeChk.Checked then - begin - if group < min then min := group; - if group > max then max := group; - end; - end; - - for i := 1 to NoCases do - begin - if not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected) then continue; - value1 := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[0], i])); - value2 := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i])); - group := round(value2); - if group = min then - begin - M1 := M1 + value1; - variance1 := variance1 + (value1 * value1); - ncases1 := ncases1 + 1; - end else if group = max then - begin - M2 := M2 + value1; - variance2 := variance2 + (value1 * value1); - ncases2 := Ncases2 + 1; - end; - end; // next case - - variance1 := variance1 - ((M1 * M1) / ncases1); - variance1 := variance1 / (ncases1 - 1); - stddev1 := sqrt(variance1); - variance2 := variance2 - ((M2 * M2) / ncases2); - variance2 := variance2 / (ncases2 - 1); - stddev2 := sqrt(variance2); - M1 := M1 / ncases1; - M2 := M2 / ncases2; - Dif := M1 - M2; - Label1Str := format('Group %d',[min]); - Label2Str := format('Group %d',[max]); - end; // if independent data - end; // if reading grid data - - if not griddata then // data read from form + if not Eval_IndependentGridData( + mean1, mean2, variance1, variance2, stddev1, stddev2, + nCases1, nCases2, Label1Str, Label2Str) + then + exit; + end else + begin + if not Eval_DependentGridData( + mean1, mean2, variance1, variance2, stddev1, stddev2, + cov12, r12, nCases1, nCases2) + then + exit; + end; + end else begin - M1 := StrToFloat(Mean1.Text); - M2 := StrToFloat(Mean2.Text); - stddev1 := StrToFloat(SD1.Text); - stddev2 := StrToFloat(SD2.Text); - ncases1 := round(StrToFloat(N1.Text)); - ncases2 := round(StrToFloat(N2.Text)); + // Read data from form + mean1 := StrToFloat(mean1Edit.Text); + mean2 := StrToFloat(mean2Edit.Text); + stddev1 := StrToFloat(SD1Edit.Text); + stddev2 := StrToFloat(SD2Edit.Text); + ncases1 := round(StrToFloat(N1Edit.Text)); + ncases2 := round(StrToFloat(N2Edit.Text)); variance1 := stddev1 * stddev1; variance2 := stddev2 * stddev2; Label1Str := 'Group 1'; Label2Str := 'Group 2'; - Dif := M1 - M2; if not independent then begin - r12 := StrToFloat(Cor12.Text); + r12 := StrToFloat(Cor12Edit.Text); cov12 := r12 * stddev1 * stddev2; end; end; + dif := mean1 - mean2; // Initialize output form lReport := TStringList.Create; @@ -289,8 +199,8 @@ begin stderr1 := sqrt(variance1 / ncases1); Stderr2 := sqrt(variance2 / ncases2); lReport.Add('Variable Mean Variance Std.Dev. S.E.Mean N'); - lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label1Str, M1, variance1, stddev1, stderr1, ncases1]); - lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label2Str, M2, variance2, stddev2, stderr2, ncases2]); + lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label1Str, mean1, variance1, stddev1, stderr1, ncases1]); + lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label2Str, mean2, variance2, stddev2, stderr2, ncases2]); lReport.Add(''); pooled := ((ncases1-1) * variance1) + ((ncases2-1) * variance2); @@ -349,8 +259,8 @@ begin stderr1 := sqrt(variance1 / ncases1); Stderr2 := sqrt(variance2 / ncases2); lReport.Add('Variable Mean Variance Std.Dev. S.E.Mean N'); - lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label1Str, M1, variance1, stddev1, stderr1, ncases1]); - lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label2Str,M2, variance2, stddev2, stderr2, ncases2]); + lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label1Str, mean1, variance1, stddev1, stderr1, ncases1]); + lReport.Add('%-10s%8.2f %8.2f %8.2f %8.2f %d', [Label2Str, mean2, variance2, stddev2, stderr2, ncases2]); lReport.Add(''); sedif := variance1 + variance2 - (2.0 * cov12); sedif := sqrt(sedif / ncases1); @@ -399,6 +309,9 @@ begin Var2Edit.Visible := false; end; + GroupCodeChk.Visible := independent; + GroupCodeGroup.Visible := independent; + Var2Label.Visible := Var2Edit.Visible; Var2In.Visible := Var2Edit.Visible; Var2Out.Visible := Var2Edit.Visible; @@ -427,11 +340,109 @@ begin Var2In.Visible := not independent; Var2Out.Visible := not independent; - Cor12.Visible := not independent; + Cor12Edit.Visible := not independent; Cor12Label.Visible := not independent; end; +function TtTestForm.Eval_DependentGridData(out AMean1, AMean2, + AVariance1, AVariance2, AStdDev1, AStdDev2, ACovar12, r12: Double; + out ANumCases1, ANumCases2: Integer): Boolean; +var + colNoSelected: IntDyneVec = nil; + values1: DblDyneVec = nil; + values2: DblDyneVec = nil; +begin + SetLength(colNoSelected, 2); + colNoSelected[0] := GetVariableIndex(OS3MainFrm.DataGrid, Var1Edit.Text); + colNoSelected[1] := GetVariableIndex(OS3MainFrm.DataGrid, Var2Edit.Text); + + values1 := CollectVecValues(OS3MainFrm.DataGrid, colNoSelected[0], colNoSelected); + values2 := CollectVecValues(OS3MainFrm.DataGrid, colNoSelected[1], colNoSelected); + + ANumCases1 := Length(values1); + ANumCases2 := Length(values2); + + if ANumCases1 <> ANumCases2 then + begin + ErrorMsg('Both variables must have the same count of cases.'); + Result := false; + exit; + end; + + if ANumCases1 < 2 then + begin + ErrorMsg('There must be at least two cases.'); + Result := false; + exit; + end; + + VecMeanVarStdDev(values1, AMean1, AVariance1, AStdDev1); + VecMeanVarStdDev(values2, AMean2, AVariance2, AStdDev2); + + ACovar12 := (values1 - AMean1) * (values2 - AMean2) / (ANumCases1-1); + r12 := ACovar12 / (AStdDev1 * AStdDev2); + + Result := true; +end; + + +function TtTestForm.Eval_IndependentGridData(out AMean1, AMean2, + AVariance1, AVariance2, AStdDev1, AStdDev2: Double; + out ANumCases1, ANumCases2: Integer; out ALabel1Str, ALabel2Str: String): Boolean; +var + colNoSelected: IntDyneVec = nil; + values1: DblDyneVec = nil; + values2: DblDyneVec = nil; + grp1, grp2: Integer; + minf, maxf: Double; + +begin + SetLength(colNoSelected, 2); + colNoSelected[0] := GetVariableIndex(OS3MainFrm.DataGrid, Var1Edit.Text); + colNoSelected[1] := GetVariableIndex(OS3MainFrm.DataGrid, GrpEdit.Text); + + if GroupCodeChk.Checked then + begin + grp1 := StrToInt(Grp1CodeEdit.Text); + grp2 := StrToInt(Grp2CodeEdit.Text); + end else + begin + GetMinMax(OS3MainFrm.DataGrid, colNoSelected[1], colNoSelected, minf, maxf); + grp1 := round(minf); + grp2 := round(maxf); + end; + + // Values in group with code given by grp1 + values1 := CollectFilteredVecValues(OS3MainFrm.DataGrid, colNoSelected[0], colNoSelected[1], grp1, colNoSelected); + // Values in group with code given by grp2 + values2 := CollectFilteredVecValues(OS3MainFrm.DataGrid, colNoSelected[0], colNoSelected[1], grp2, colNoSelected); + + ANumCases1 := Length(values1); + ANumCases2 := Length(values2); + if ANumCases1 <> ANumCases2 then + begin + ErrorMsg('Both variables must have the same count of cases.'); + Result := false; + exit; + end; + + if ANumCases1 < 2 then begin + ErrorMsg('There must be at least 2 cases.'); + Result := false; + exit; + end; + + VecMeanVarStdDev(values1, AMean1, AVariance1, AStdDev1); + VecMeanVarStdDev(values2, AMean2, AVariance2, AStdDev2); + + ALabel1Str := format('Group %d', [grp1]); + ALabel2Str := format('Group %d', [grp2]); + + Result := true; +end; + + procedure TTtestForm.GroupCodeChkChange(Sender: TObject); begin Grp1CodeEdit.Enabled := GroupCodeChk.Checked; @@ -482,13 +493,13 @@ begin VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); Var1Edit.Clear; Var2Edit.Clear; - Mean1.Clear; - Mean2.Clear; - SD1.Clear; - SD2.Clear; - N1.Clear; - N2.Clear; - Cor12.Clear; + Mean1Edit.Clear; + Mean2Edit.Clear; + SD1Edit.Clear; + SD2Edit.Clear; + N1Edit.Clear; + N2Edit.Clear; + Cor12Edit.Clear; GroupCodeChk.Checked := false; GrpEdit.Clear; Grp1CodeEdit.Clear; @@ -524,39 +535,39 @@ begin AMsg := ''; if Notebook.PageIndex = 0 then begin - if (Mean1.Text = '') or not TryStrToFloat(Mean1.Text, x) then + if (Mean1Edit.Text = '') or not TryStrToFloat(Mean1Edit.Text, x) then begin - AControl := Mean1; + AControl := Mean1Edit; AMsg := 'Invalid input for the mean of sample 1'; exit; end; - if (SD1.Text = '') or not TryStrToFloat(SD1.Text, x) or (x <= 0) then + if (SD1Edit.Text = '') or not TryStrToFloat(SD1Edit.Text, x) or (x <= 0) then begin - AControl := SD1; + AControl := SD1Edit; AMsg := 'Invald input for the standard deviation of sample 1'; exit; end; - if (N1.Text = '') or not TryStrToInt(N1.Text, n) or (n <= 0) then + if (N1Edit.Text = '') or not TryStrToInt(N1Edit.Text, n) or (n <= 0) then begin - AControl := N1; + AControl := N1Edit; AMsg := 'Invald input for the size of sample 1'; exit; end; - if (Mean2.Text = '') or not TryStrToFloat(Mean2.Text, x) then + if (Mean2Edit.Text = '') or not TryStrToFloat(Mean2Edit.Text, x) then begin - AControl := Mean2; + AControl := Mean2Edit; AMsg := 'Invalid input for the mean of sample 2'; exit; end; - if (SD2.Text = '') or not TryStrToFloat(SD2.Text, x) or (x <= 0) then + if (SD2Edit.Text = '') or not TryStrToFloat(SD2Edit.Text, x) or (x <= 0) then begin - AControl := SD2; + AControl := SD2Edit; AMsg := 'Invald input for the standard deviation of sample 2'; exit; end; - if (N2.Text = '') or not TryStrToInt(N2.Text, n) or (n <= 0) then + if (N2Edit.Text = '') or not TryStrToInt(N2Edit.Text, n) or (n <= 0) then begin - AControl := N2; + AControl := N2Edit; AMsg := 'Invald input for the size of sample 2'; exit; end; @@ -581,17 +592,22 @@ begin AMsg := 'Group variable not specified.'; exit; end; - if Grp1CodeEdit.Visible and ((Grp1CodeEdit.Text = '') or not TryStrToInt(Grp1CodeEdit.Text, n))then + if GroupCodeChk.Checked then begin - AControl := Grp1CodeEdit; - AMsg := 'Code for group 1 missing.'; - exit; - end; - if Grp2CodeEdit.Visible and ((Grp2CodeEdit.Text = '') or not TryStrToInt(Grp2CodeEdit.Text, n))then - begin - AControl := Grp2CodeEdit; - AMsg := 'Code for group 2 missing.'; - exit; + if Grp1CodeEdit.Visible and + ((Grp1CodeEdit.Text = '') or not TryStrToInt(Grp1CodeEdit.Text, n)) then + begin + AControl := Grp1CodeEdit; + AMsg := 'Code for group 1 missing.'; + exit; + end; + if Grp2CodeEdit.Visible and + ((Grp2CodeEdit.Text = '') or not TryStrToInt(Grp2CodeEdit.Text, n))then + begin + AControl := Grp2CodeEdit; + AMsg := 'Code for group 2 missing.'; + exit; + end; end; end; Result := true; diff --git a/applications/lazstats/source/units/gridprocs.pas b/applications/lazstats/source/units/gridprocs.pas index 749ecbfe6..ab8c83f3d 100644 --- a/applications/lazstats/source/units/gridprocs.pas +++ b/applications/lazstats/source/units/gridprocs.pas @@ -11,6 +11,9 @@ uses function CollectVecValues(AGrid: TStringGrid; AColIndex: Integer; AColCheck: IntDyneVec = nil): DblDyneVec; +function CollectFilteredVecValues(AGrid: TStringGrid; AColIndex, AFilterColIndex: Integer; + AcceptValue: Double; AColCheck: IntDyneVec = nil): DblDyneVec; + function CollectMatValues(AGrid: TStringGrid; AColIndices: IntDyneVec): DblDyneMat; procedure GetMinMax(AGrid: TStringGrid; AColIndex: Integer; @@ -73,6 +76,43 @@ begin end; +{ Extracts the grid values from the column with index AColIndex, but only those + for which the value in the same row, but column AFilterCalIndex matches the + AcceptValue } +function CollectFilteredVecValues(AGrid: TStringGrid; AColIndex, AFilterColIndex: Integer; + AcceptValue: Double; AColCheck: IntDyneVec = nil): DblDyneVec; +var + row, n: Integer; + val1, val2: Double; +begin + Result := nil; + SetLength(Result, AGrid.RowCount); + n := 0; + for row := 1 to AGrid.RowCount-1 do + begin + if Length(AColCheck) = 0 then + begin + if not ValidValue(AGrid, row, AColIndex) then continue; + if not ValidValue(AGrid, row, AFilterColIndex) then continue; + end else + if not GoodRecord(AGrid, row, AColCheck) then continue; + + if not TryStrToFloat(trim(AGrid.Cells[AColIndex, row]), val1) then + raise ELazStats.CreateFmt('Non-numeric string "%s" in column %d, row %d', + [AGrid.Cells[AColIndex, row]]); + if not TryStrToFloat(trim(AGrid.Cells[AFilterColIndex, row]), val2) then + raise ELazStats.CreateFmt('Non-numeric string "%s" in column %d, row %d', + [AGrid.Cells[AFilterColIndex, row]]); + if val2 = AcceptValue then + begin + Result[n] := val1; + inc(n); + end; + end; + SetLength(Result, n); +end; + + { Extracts the grid values from the columns with indices given by AColIndices and puts them into the columns of the result matrix. This means: The result matrix contains the variables as columns and the diff --git a/applications/lazstats/source/units/matrixunit.pas b/applications/lazstats/source/units/matrixunit.pas index 9c13ae4b8..275c50cd4 100644 --- a/applications/lazstats/source/units/matrixunit.pas +++ b/applications/lazstats/source/units/matrixunit.pas @@ -21,7 +21,9 @@ type // Vectors operator + (A, B: TDblVector): TDblVector; +operator + (A: TDblVector; b: double): TDblVector; operator - (A, B: TDblVector): TDblVector; +operator - (A: TDblVector; b: double): TDblVector; operator * (A, B: TDblVector): Double; operator * (A: TDblVector; b: Double): TDblVector; operator * (a: Double; B: TDblVector): TDblVector; @@ -129,6 +131,18 @@ begin end; +operator + (A: TDblVector; b: double): TDblVector; +var + i, n: Integer; +begin + Result := nil; + n := Length(A); + Setlength(Result, n); + for i := 0 to n-1 do + Result[i] := A[i] + b; +end; + + operator - (A, B: TDblVector): TDblVector; var i, n: Integer; @@ -141,6 +155,18 @@ begin end; +operator - (A: TDblVector; b: double): TDblVector; +var + i, n: Integer; +begin + Result := nil; + n := Length(A); + Setlength(Result, n); + for i := 0 to n-1 do + Result[i] := A[i] - b; +end; + + // Vector dot product operator * (A, B: TDblVector): Double; var