unit TwoPropUnit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, ComCtrls, Buttons, MainUnit, Globals, FunctionsLib, BasicStatsReportFormUnit; type { TTwoPropForm } TTwoPropForm = class(TBasicStatsReportForm) Bevel2: TBevel; Bevel3: TBevel; Bevel5: TBevel; LabelCorner: TLabel; Notebook: TNotebook; Page1: TPage; Page2: TPage; Page3: TPage; Panel3: TPanel; Panel4: TPanel; DepFreq00: TEdit; DepFreq10: TEdit; DepFreq01: TEdit; DepFreq11: TEdit; CIntervalEdit: TEdit; GrpEdit: TEdit; GrpLabel: TLabel; ConfLabel: TLabel; Var1In: TBitBtn; Var2In: TBitBtn; Var1Out: TBitBtn; GrpIn: TBitBtn; Var2Out: TBitBtn; Var2Edit: TEdit; Var2Label: TLabel; Var1Edit: TEdit; IndSize2: TEdit; IndSize1: TEdit; IndFreq2: TEdit; IndFreq1: TEdit; Samp1Label: TLabel; Samp21Label: TLabel; Label11: TLabel; Var1Label: TLabel; Samp2Label: TLabel; Samp1SizeLabel: TLabel; Samp2SizeLabel: TLabel; DepSamp2Label: TLabel; DepSamp1Label: TLabel; Samp10Label: TLabel; Samp11Label: TLabel; Samp20Label: TLabel; GrpOut: TBitBtn; VarList: TListBox; DataSourceGroup: TRadioGroup; DepIndepGroup: TRadioGroup; procedure GrpInClick(Sender: TObject); procedure GrpOutClick(Sender: TObject); procedure DataSourceGroupClick(Sender: TObject); procedure DepIndepGroupClick(Sender: TObject); procedure Var1InClick(Sender: TObject); procedure Var1OutClick(Sender: TObject); procedure Var2InClick(Sender: TObject); procedure Var2OutClick(Sender: TObject); procedure VarListDblClick(Sender: TObject); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); private independent: boolean; griddata: boolean; protected procedure AdjustConstraints; override; procedure Compute; override; procedure UpdateBtnStates; override; function Validate(out AMsg: String; out AControl: TWinControl): Boolean; override; public constructor Create(AOwner: TComponent); override; procedure Reset; override; end; var TwoPropForm: TTwoPropForm; implementation {$R *.lfm} uses Math, GridProcs; { TTwoPropForm } constructor TTwoPropForm.Create(AOwner: TComponent); begin inherited; CIntervalEdit.Text := FormatFloat('0.0', DEFAULT_CONFIDENCE_LEVEL_PERCENT); end; procedure TTwoPropForm.AdjustConstraints; var w: Integer; begin inherited; w := Max(DataSourceGroup.Width, DepIndepGroup.Width); DataSourceGroup.Constraints.MinWidth := w; DepIndepGroup.Constraints.MinWidth := w; ParamsPanel.Constraints.MinWidth := Max( 4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left, 2*w + Bevel5.Width ); ParamsPanel.Constraints.MinHeight := DataSourceGroup.Height + Notebook.BorderSpacing.Top + Var2Out.Top + Var2Out.Height + Notebook.BorderSpacing.Bottom + CIntervalEdit.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; end; procedure TTwoPropForm.Compute; var ConfInt, Prop1, Prop2, zstatistic, zprobability: double; PropDif, stderr, UCL, LCL, value1, value2, ztest: double; P, Q: double; i: Integer; //, v1, v2, NoSelected, f1, f2, f3, f4, ncases1, ncases2: integer; min, max, group, AB, AC, CD, BD: integer; ColNoSelected: IntDyneVec = nil; lReport: TStrings; begin // Initialize output form stderr := 0.0; PropDif := 0.0; // v2 := 0; ztest := 0.0; Prop1 := 0.0; Prop2 := 0.0; // NoSelected := 0; // v1 := 0; zstatistic := 0.0; zprobability := 0.0; UCL := 0.0; LCL := 0.0; SetLength(ColNoSelected,NoVariables); ConfInt := (100.0 - StrToFloat(CIntervalEdit.Text)) / 2.0 ; ConfInt := (100.0 - ConfInt) / 100.0; // one tail ncases1 := 0; ncases2 := 0; f1 := 0; f2 := 0; f3 := 0; f4 := 0; if independent then Var2Edit.Text := GrpEdit.Text; if griddata then // data read from grid begin SetLength(ColNoSelected, 2); ColNoSelected[0] := GetVariableIndex(OS3MainFrm.DataGrid, Var1Edit.Text); ColNoSelected[1] := GetVariableIndex(OS3MainFrm.DataGrid, Var2Edit.Text); { for i := 1 to NoVariables do begin if Var1Edit.Text = OS3MainFrm.DataGrid.Cells[i,0] then begin v1 := i; ColNoSelected[0] := i; end; if Var2Edit.Text = OS3MainFrm.DataGrid.Cells[i,0] then begin v2 := i; ColNoSelected[1] := i; end; end; // next variable } f1 := 0; f2 := 0; nCases1 := 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])); f1 := f1 + round(value1); f2 := f2 + round(value2); end; // next case f3 := nCases1 - f1; f4 := nCases1 - f2; AB := f1 + f2; AC := f1 + f3; CD := f3 + f4; BD := f2 + f4; Prop1 := BD / nCases1; Prop2 := AB / nCases1; stderr := sqrt((f1 / nCases1 + f4 / nCases1) / nCases1); PropDif := Prop1 - Prop2; zstatistic := PropDif / stderr; ztest := InverseZ(ConfInt); zprobability := 1.0 - ProbZ(abs(zstatistic)); UCL := PropDif + stderr * ztest; LCL := PropDif - stderr * ztest; end; // if not independent if independent then begin min := MaxInt; max := min; 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 group < min then min := group; if group > max then max := group; 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])); group := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[ColNoSelected[1], i]))); if group = min then begin f1 := f1 + round(value1); ncases1 := ncases1 + 1; end else begin f2 := f2 + round(value1); ncases2 := ncases2 + 1; end; end; // next case Prop1 := f1 / ncases1; Prop2 := f2 / ncases2; PropDif := Prop1 - Prop2; P := (f1 + f2) / (ncases1 + ncases2); Q := 1.0 - P; stderr := sqrt(P * Q * ((1.0 / ncases1) + (1.0 / ncases2))); zstatistic := (Prop1 - Prop2) / stderr; zprobability := 1.0 - probz(abs(zstatistic)); ztest := inversez(ConfInt); UCL := PropDif + ztest * stderr; LCL := PropDif - ztest * stderr; end; // end if independent end; // if reading grid data if not griddata then // data read from form begin if not independent then // correlated data begin f1 := round(StrToFloat(DepFreq00.Text)); f2 := round(StrToFloat(DepFreq10.Text)); f3 := round(StrToFloat(DepFreq01.Text)); f4 := round(StrToFloat(DepFreq11.Text)); ncases1 := f1 + f2 + f3 + f4; AB := f1 + f2; AC := f1 + f3; CD := f3 + f4; BD := f2 + f4; Prop1 := BD / ncases1; Prop2 := AB / ncases1; stderr := sqrt((f1 / ncases1 + f4 / ncases1) / ncases1); PropDif := Prop1 - Prop2; zstatistic := PropDif / stderr; ztest := inversez(ConfInt); zprobability := 1.0 - probz(abs(zstatistic)); UCL := PropDif + stderr * ztest; LCL := PropDif - stderr * ztest; end; // if not independent if independent then // independent data begin f1 := StrToInt(IndFreq1.Text); f2 := StrToInt(IndFreq2.Text); ncases1 := StrToInt(IndSize1.Text); ncases2 := StrToInt(IndSize2.Text); Prop1 := f1 / ncases1; Prop2 := f2 / ncases2; PropDif := Prop1 - Prop2; P := (f1 + f2) / (ncases1 + ncases2); Q := 1.0 - P; stderr := sqrt(P * Q * ((1.0 / ncases1) + (1.0 / ncases2))); zstatistic := (Prop1 - Prop2) / stderr; zprobability := 1.0 - probz(abs(zstatistic)); ztest := inversez(ConfInt); UCL := PropDif + ztest * stderr; LCL := PropDif - ztest * stderr; end; end; // Print the results lReport := TStringList.Create; try lReport.Add('COMPARISON OF TWO PROPORTIONS'); lReport.Add(''); if not independent then begin lReport.Add('Test for Difference Between Two Dependent Proportions'); lReport.Add(''); lReport.Add('Entered Values'); lReport.Add(''); lReport.Add('Sample 1'); lReport.Add(' 0 1 sum'); lReport.Add(' -----------------------'); lReport.Add(' 0 |%5d %5d %5d |', [f1, f2, AB]); lReport.Add(' 2 --------|-------|------'); lReport.Add(' 1 |%5d %5d %5d |', [f3, f4, CD]); lReport.Add(' --------|-------|------'); lReport.Add(' sum | %5d %5d %5d |', [AC, BD, ncases1]); lReport.Add(''); lReport.Add('Confidence Level selected: %9s', [CIntervalEdit.Text]); lReport.Add('Proportion 1: %9.3f', [Prop1]); lReport.Add('Proportion 2: %9.3f', [Prop2]); lReport.Add('Number of cases: %9d', [nCases1]); lReport.Add('Difference in proportions: %9.3f', [PropDif]); lReport.Add('Standard Error of Difference: %9.3f', [stderr]); lReport.Add('z test statistic: %9.3f', [zStatistic]); lReport.Add(' with probability %9.4f', [zProbability]); lReport.Add('z value for confidence interval: %9.3f', [zTest]); lReport.Add('Confidence Interval: %9.3f ... %.3f', [LCL, UCL]); end; if independent then begin lReport.Add('Test for Difference Between Two Independent Proportions'); lReport.Add(''); lReport.Add('Entered Values'); lReport.Add(''); lReport.Add('Sample 1: Frequency %5d for %5d cases.', [f1, ncases1]); lReport.Add('Sample 2: Frequency %5d for %5d cases.', [f2, ncases2]); lReport.Add(''); lReport.Add('Proportion 1: %9.3f', [Prop1]); lReport.Add('Proportion 2: %9.3f', [Prop2]); lReport.Add('Difference: %9.3f', [PropDif]); lReport.Add('Standard Error of Difference: %9.3f', [stderr]); lReport.Add('Confidence Level selected: %9s', [CIntervalEdit.Text]); lReport.Add('z test statistic: %9.3f', [zStatistic]); lReport.Add(' with probability %9.4f', [zProbability]); lReport.Add('z value for confidence interval: %9.3f', [ztest]); lReport.Add('Confidence Interval: %9.3f ... %.3f', [LCL, UCL]); end; FReportFrame.DisplayReport(lReport); finally lReport.Free; end; end; procedure TTwoPropForm.GrpInClick(Sender: TObject); var index: Integer; begin index := VarList.ItemIndex; if (index > -1) and (GrpEdit.Text = '') then begin GrpEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TTwoPropForm.GrpOutClick(Sender: TObject); begin if GrpEdit.Text <> '' then begin VarList.Items.Add(GrpEdit.Text); GrpEdit.Text := ''; UpdateBtnStates; end; end; procedure TTwoPropForm.DataSourceGroupClick(Sender: TObject); begin griddata := (DataSourceGroup.ItemIndex = 1); DepIndepGroupClick(nil); end; procedure TTwoPropForm.DepIndepGroupClick(Sender: TObject); begin case DepIndepGroup.ItemIndex of 0: begin independent := true; if griddata then Notebook.PageIndex := 0 else Notebook.PageIndex := 1; end; 1: begin independent := false; if griddata then Notebook.PageIndex := 0 else Notebook.PageIndex := 2; end; end; Var2Edit.Visible := independent; Var2Label.Visible := independent; Var2In.Visible := independent; Var2Out.Visible := independent; GrpEdit.Visible := not independent; GrpLabel.Visible := not independent; GrpIn.Visible := not independent; GrpOut.Visible := not independent; end; procedure TTwoPropForm.Reset; var i: integer; begin inherited; DataSourceGroup.ItemIndex := 0; DepIndepGroup.ItemIndex := 0; VarList.Clear; for i := 1 to NoVariables do VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); Var1Edit.Clear; Var2Edit.Clear; independent := true; griddata := false; GrpLabel.Visible := true; GrpEdit.Visible := true; GrpIn.Visible := true; GrpOut.Visible := true; GrpEdit.Clear; Var2Edit.Visible := false; Var2Label.Visible := false; Var2In.Visible := false; Var2Out.Visible := false; DepFreq00.Clear; DepFreq01.Clear; DepFreq10.Clear; DepFreq11.Clear; IndFreq1.Clear; IndFreq2.Clear; IndSize1.Clear; IndSize2.Clear; Notebook.PageIndex := 1; UpdateBtnStates; end; procedure TTwoPropForm.Var1InClick(Sender: TObject); var index: Integer; begin index := VarList.ItemIndex; if (index > -1) and (Var1Edit.Text = '') then begin Var1Edit.Text := VarList.Items[index]; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TTwoPropForm.Var1OutClick(Sender: TObject); begin if Var1Edit.Text <> '' then begin VarList.Items.Add(Var1Edit.Text); Var1Edit.Text := ''; UpdateBtnStates; end; end; procedure TTwoPropForm.Var2InClick(Sender: TObject); var index: Integer; begin index := VarList.ItemIndex; if (index > -1) and (Var2Edit.Text = '') then begin Var2Edit.Text := VarList.Items[index]; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TTwoPropForm.Var2OutClick(Sender: TObject); begin if Var2Edit.Text <> '' then begin VarList.Items.Add(Var2Edit.Text); Var2Edit.Text := ''; UpdateBtnStates; end; end; procedure TTwoPropForm.UpdateBtnStates; begin inherited; Var1In.Enabled := (VarList.ItemIndex > -1) and (Var1Edit.Text = ''); Var2In.Enabled := (VarList.ItemIndex > -1) and (Var2Edit.Text = ''); GrpIn.Enabled := (VarList.ItemIndex > -1) and (GrpEdit.Text = ''); Var1Out.Enabled := Var1Edit.Text <> ''; Var2Out.Enabled := Var2Edit.Text <> ''; GrpOut.Enabled := GrpEdit.Text <> ''; end; function TTwoPropForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; var n: Integer; begin Result := false; AControl := nil; AMsg := ''; if Notebook.PageIndex = 0 then begin if Var1Edit.Text = '' then begin AControl := Var1Edit; AMsg := 'First variable not specified.'; exit; end; case DepIndepGroup.ItemIndex of 0: if (GrpEdit.Text = '') then begin AControl := GrpEdit; AMsg := 'Group code not specified'; exit; end; 1: if (Var2Edit.Text = '') then begin AControl := Var2Edit; AMsg := 'Second variable not specified.'; exit; end; end; end else if Notebook.PageIndex = 1 then begin if (IndFreq1.Text = '') or not TryStrToInt(IndFreq1.Text, n) or (n < 0) then begin AControl := IndFreq1; AMsg := 'Invalid input for Sample 1 frequency'; exit; end; if (IndFreq2.Text = '') or not TryStrToInt(IndFreq2.Text, n) or (n < 0) then begin AControl := IndFreq2; AMsg := 'Invalid input for Sample 2 frequency'; exit; end; if (IndSize1.Text = '') or not TryStrToInt(IndSize1.Text, n) or (n <= 0) then begin AControl := IndSize1; AMsg := 'Invald input for size of sample 1'; exit; end; if (IndSize2.Text = '') or not TryStrToInt(IndSize2.Text, n) or (n <= 0) then begin AControl := IndSize2; AMsg := 'Invalud input for size of sample 2'; exit; end; end else if Notebook.PageIndex = 2 then begin if (DepFreq00.Text = '') or not TryStrToInt(DepFreq00.Text, n) or (n < 0) then AControl := DepFreq00 else if (DepFreq01.Text = '') or not TryStrToInt(DepFreq01.Text, n) or (n < 0) then AControl := DepFreq01 else if (DepFreq10.Text = '') or not TryStrToInt(DepFreq10.Text, n) or (n < 0) then AControl := DepFreq10 else if (DepFreq11.Text = '') or not TryStrToInt(DepFreq11.Text, n) or (n < 0) then AControl := DepFreq11; if AControl <> nil then begin AMsg := 'Invalid input.'; exit; end; end; Result := true; end; procedure TTwoPropForm.VarListDblClick(Sender: TObject); var index: integer; s: String; begin index := VarList.ItemIndex; if index > -1 then begin s := VarList.Items[index]; if Var1Edit.Text = '' then begin Var1Edit.Text := s; VarList.Items.Delete(index); end else if independent and (GrpEdit.Text = '') then begin GrpEdit.Text := s; VarList.Items.Delete(index); end else if (not independent) and (Var2Edit.Text = '') then begin Var2Edit.Text := s; VarList.Items.Delete(index); end; UpdateBtnStates; end; end; procedure TTwoPropForm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; end.