unit RIDITUnit; {$mode objfpc}{$H+} {$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons, ExtCtrls, ComCtrls, MainUnit, Globals, FunctionsLib, MatrixLib, ReportFrameUnit, BasicStatsReportFormUnit; type { TRIDITForm } TRIDITForm = class(TBasicStatsReportForm) Bevel2: TBevel; BonChk: TCheckBox; AlphaEdit: TEdit; PageControl: TPageControl; Panel1: TPanel; AlphaLabel: TLabel; ObsChk: TCheckBox; ExpChk: TCheckBox; PropChk: TCheckBox; ChiChk: TCheckBox; RefGrp: TRadioGroup; ResultsPage: TTabSheet; FrequenciesPage: TTabSheet; RowColPropsPage: TTabSheet; CellChiSqrPage: TTabSheet; YatesChk: TCheckBox; DetailsChk: TCheckBox; ColList: TListBox; OptionsGroup: TGroupBox; RefEdit: TEdit; Label4: TLabel; RowEdit: TEdit; Label2: TLabel; Label3: TLabel; RowIn: TBitBtn; RowOut: TBitBtn; ColIn: TBitBtn; ColOut: TBitBtn; Label1: TLabel; VarList: TListBox; procedure ColInClick(Sender: TObject); procedure ColListClick(Sender: TObject); procedure ColListDblClick(Sender: TObject); procedure ColOutClick(Sender: TObject); procedure RefGrpClick(Sender: TObject); procedure RowInClick(Sender: TObject); procedure RowOutClick(Sender: TObject); procedure VarListDblClick(Sender: TObject); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); private FFrequenciesReportFrame: TReportFrame; FRowColPropsReportFrame: TReportFrame; FCellChiSqrReportFrame: TReportFrame; procedure Analyze(RefCol: integer; RowLabels, ColLabels: StrDyneVec; NoToAnalyze: integer; Freq: IntDyneMat; Props: DblDyneMat; NoRows: integer; AReport: TStrings); function CalcColProportions(const AFrequencies: IntDyneMat): DblDyneMat; function CalcRowProportions(const AFrequencies: IntDyneMat): DblDyneMat; procedure GetExpectedAndCellChiSqr(const AFrequencies: IntDyneMat; out AExpected, ACellChiSqr: DblDyneMat; out AChiSqr, AdjChiSqr: Double; out ADegreesOfFreedom: Integer; out AYates: Boolean); procedure GetFrequencies(const AColNoSelected: IntDyneVec; ANumRows, ANumCols: Integer; out ANumCases: Integer; out AFrequencies: IntDyneMat); 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 RIDITForm: TRIDITForm; implementation {$R *.lfm} uses Math, Utils, GridProcs, MatrixUnit; { TRIDITForm } constructor TRIDITForm.Create(AOwner: TComponent); begin inherited; FReportFrame.Parent := ResultsPage; FFrequenciesReportFrame := TReportFrame.Create(self); FFrequenciesReportFrame.Name := ''; FFrequenciesReportFrame.Parent := FrequenciesPage; FFrequenciesReportFrame.Align := alClient; FFrequenciesReportFrame.BorderSpacing.Left := 0; FFrequenciesReportFrame.BorderSpacing.Top := 0; FFrequenciesReportFrame.BorderSpacing.Bottom := 0; FFrequenciesReportFrame.BorderSpacing.Right := 0; InitToolbar(FFrequenciesReportFrame.ReportToolbar, tpRight); FRowColPropsReportFrame := TReportFrame.Create(self); FRowColPropsReportFrame.Name := ''; FRowColPropsReportFrame.Parent := RowColPropsPage; FRowColPropsReportFrame.Align := alClient; FRowColPropsReportFrame.BorderSpacing.Left := 0; FRowColPropsReportFrame.BorderSpacing.Top := 0; FRowColPropsReportFrame.BorderSpacing.Bottom := 0; FRowColPropsReportFrame.BorderSpacing.Right := 0; InitToolbar(FRowColPropsReportFrame.ReportToolbar, tpRight); FCellChiSqrReportFrame := TReportFrame.Create(self); FCellChiSqrReportFrame.Name := ''; FCellChiSqrReportFrame.Parent := CellChiSqrPage; FCellChiSqrReportFrame.Align := alClient; FCellChiSqrReportFrame.BorderSpacing.Left := 0; FCellChiSqrReportFrame.BorderSpacing.Top := 0; FCellChiSqrReportFrame.BorderSpacing.Bottom := 0; FCellChiSqrReportFrame.BorderSpacing.Right := 0; InitToolbar(FCellChiSqrReportFrame.ReportToolbar, tpRight); PageControl.ActivePageIndex := 0; end; procedure TRIDITForm.AdjustConstraints; begin inherited; ParamsPanel.Constraints.MinWidth := MaxValue([ CloseBtn.Width * 4 + CloseBtn.BorderSpacing.Left * 3, OptionsGroup.Width, AlphaEdit.Left + AlphaEdit.Width ]); ParamsPanel.Constraints.MinHeight := ColOut.Top + ColOut.Height + Label4.BorderSpacing.Top + Label4.Height + Label4.BorderSpacing.Bottom + RefEdit.Height + Panel1.BorderSpacing.Bottom + OptionsGroup.Height + RefGrp.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; end; procedure TRIDITForm.Analyze(RefCol: integer; RowLabels, ColLabels: StrDyneVec; NoToAnalyze: integer; Freq: IntDyneMat; Props: DblDyneMat; NoRows: integer; AReport: TStrings); var probdists : DblDyneMat = nil; refprob : DblDyneMat = nil; sizes : DblDyneVec = nil; meanridits : DblDyneVec = nil; StdErr : DblDyneVec = nil; CRatios : DblDyneVec = nil; OverMeanRidit : double; chisquare : double; probchi : double; alpha : double; Bonferroni : double; i, j : integer; outline : string; details : boolean; term1,term2,term3,term4 : double; begin SetLength(probdists,NoRows,NoToAnalyze); SetLength(refprob,NoRows,4); SetLength(sizes,NoToAnalyze); SetLength(meanridits,NoToAnalyze); SetLength(CRatios,NoToAnalyze); SetLength(StdErr,NoToAnalyze); alpha := StrToFloat(AlphaEdit.Text); details := DetailsChk.Checked; AReport.Add('ANALYSIS FOR STANDARD %s', [ColLabels[RefCol]]); AReport.Add(''); { --- wp: these data are displayed in separate tabs, no need to repeat them. // print frequencies outline := 'Frequencies Observed'; IntArrayPrint(Freq, NoRows, NoToAnalyze, 'Frequencies', RowLabels, ColLabels, outline, AReport); // print column proportions outline := 'Column Proportions Observed'; MatPrint(Props, NoRows, NoToAnalyze, outline, RowLabels, ColLabels, NoCases, AReport); } // Get sizes in each column for i := 0 to NoToAnalyze - 1 do sizes[i] := Freq[NoRows,i]; // Get the reference variable probabilities for all variables for j := 0 to NoToAnalyze - 1 do begin for i := 0 to NoRows - 1 do begin refprob[i,0] := Props[i,j]; refprob[i,1] := Props[i,j] / 2.0; end; refprob[0,2] := 0.0; // for i := 1 to NoRows - 1 do refprob[i,2] := refprob[i-1,2] + refprob[i-1,0]; for i := 0 to NoRows - 2 do refprob[i+1, 2] := refprob[i, 2] + refprob[i, 0]; for i := 0 to NoRows - 1 do refprob[i, 3] := refprob[i, 1] + refprob[i, 2]; // Print calculations table if details then begin outline := 'Ridit calculations for ' + ColLabels[j]; MatPrint(refprob, NoRows, 4, outline, RowLabels, ColLabels, NoCases, AReport); AReport.Add(DIVIDER_SMALL_AUTO); end; // store results in probdists for i := 0 to NoRows - 1 do probdists[i,j] := refprob[i,3]; end; outline := 'Ridits for all variables'; MatPrint(probdists, NoRows, NoToAnalyze, outline, RowLabels, ColLabels, NoCases, AReport); AReport.Add(DIVIDER_SMALL_AUTO); // obtain mean ridits for the all variables using the reference variable for i := 0 to NoToAnalyze - 1 do begin meanridits[i] := 0.0; for j := 0 to NoRows - 1 do meanridits[i] := meanridits[i] + (probdists[j,RefCol] * Freq[j,i]); meanridits[i] := meanridits[i] / sizes[i]; end; // print the means using the reference variable outline := 'Mean RIDITS Using the Reference Values'; DynVectorPrint(meanridits, NoToAnalyze, outline, ColLabels, NoCases, AReport); AReport.Add(DIVIDER_SMALL_AUTO); // obtain the weighted grand mean ridit OverMeanRidit := 0.0; for i := 0 to NoToAnalyze - 1 do if (i <> RefCol) then OverMeanRidit := OverMeanRidit + sizes[i] * meanridits[i]; OverMeanRidit := OverMeanRidit / (Freq[NoRows,NoToAnalyze] - sizes[RefCol]); AReport.Add('Overall mean for RIDITS in non-reference groups: %8.4f', [OverMeanRidit]); // obtain chisquare chisquare := 0.0; term4 := sqr(OverMeanRidit - 0.5); term3 := 0.0; for i := 0 to NoToAnalyze - 1 do if (i <> RefCol) then term3 := term3 + sizes[i] * sizes[i]; term3 := 12.0 * term3; term2 := Freq[NoRows,NoToAnalyze]; term1 := 0.0; for i := 0 to NoToAnalyze - 1 do if (i <> RefCol) then term1 := term1 + sizes[i] * sqr(meanridits[i] - 0.5); term1 := term1 * 12.0; chisquare := term1 - (term3 / term2) * term4; probchi := 1.0 - ChiSquaredProb(chisquare, NoToAnalyze-1); AReport.Add('Chisquared: %8.4f', [chisquare]); AReport.Add(' with probability < %8.4f', [probchi]); // do pairwise comparisons CRatios[RefCol] := 0.0; for i := 0 to NoToAnalyze - 1 do if (i <> RefCol) then begin StdErr[i] := sqrt(sizes[RefCol] + sizes[i]) / (2.0 * sqrt(3.0 * sizes[RefCol] * sizes[i])); CRatios[i] := (meanridits[i] - 0.5) / StdErr[i]; end; outline := 'z critical ratios'; DynVectorPrint(CRatios, NoToAnalyze, outline, ColLabels, NoCases, AReport); alpha := alpha / 2.0; if BonChk.Checked then begin alpha := alpha / (NoToAnalyze - 1); Bonferroni := InverseZ(1.0 - alpha); AReport.Add('Significance level used for comparisons: %8.3f', [Bonferroni]); AReport.Add(''); end; for i := 0 to NoToAnalyze - 1 do if (i <> RefCol) then begin if (abs(CRatios[i]) > Bonferroni) then AReport.Add('%s vs %s: significant', [ColLabels[i], ColLabels[RefCol]]) else AReport.Add('%s vs %s: not significant', [ColLabels[i], ColLabels[RefCol]]); end; end; function TRIDITForm.CalcColProportions(const AFrequencies: IntDyneMat): DblDyneMat; var i, j, n, m, numRows, numCols: Integer; begin MatSize(AFrequencies, n,m); numRows := n-1; numCols := m-1; Result := nil; SetLength(Result, n,m); for j := 0 to numCols do begin for i := 0 to numRows-1 do begin if (AFrequencies[numRows, j] > 0.0) then Result[i, j] := AFrequencies[i, j] / AFrequencies[numRows, j] else Result[i, j] := 0.0; end; if (AFrequencies[numRows, j] > 0.0) then Result[numRows, j] := 1.0 else Result[numRows, j] := 0.0; end; end; function TRIDITForm.CalcRowProportions(const AFrequencies: IntDyneMat): DblDyneMat; var i, j, n, m, numRows, numCols: Integer; begin MatSize(AFrequencies, n,m); numRows := n-1; numCols := m-1; Result := nil; SetLength(Result, n,m); for i := 0 to numRows do begin for j := 0 to numCols-1 do begin if (AFrequencies[i, numCols] > 0.0) then Result[i, j] := AFrequencies[i, j] / AFrequencies[i, numCols] else Result[i, j] := 0.0; end; if (AFrequencies[i, numCols] > 0.0) then Result[i, numCols] := 1.0 else Result[i, numCols] := 0.0; end; end; procedure TRIDITForm.ColInClick(Sender: TObject); var i: integer; begin i := 0; while (i < VarList.Items.Count) do begin if VarList.Selected[i] then begin ColList.Items.Add(VarList.Items[i]); VarList.Items.Delete(i); i := 0; end else i := i + 1; end; UpdateBtnStates; end; procedure TRIDITForm.ColListClick(Sender: TObject); var index: integer; begin index := ColList.ItemIndex; if index > -1 then RefEdit.Text := ColList.Items[index]; UpdateBtnStates; end; procedure TRIDITForm.ColListDblClick(Sender: TObject); var index: Integer; begin index := ColList.ItemIndex; if index > -1 then begin VarList.Items.Add(ColList.Items[index]); ColList.Items.Delete(index); UpdateBtnStates; end; end; procedure TRIDITForm.ColOutClick(Sender: TObject); var index: integer; begin index := ColList.ItemIndex; if index > -1 then begin VarList.Items.Add(ColList.Items[index]); ColList.Items.Delete(index); end; UpdateBtnStates; end; procedure TRIDITForm.Compute; var RowProp: DblDyneMat = nil; ColProp: DblDyneMat = nil; Expected: DblDyneMat = nil; CellChi: DblDyneMat = nil; ColNoSelected: IntDyneVec = nil; Freq : IntDyneMat = nil; RowLabels: StrDyneVec = nil; ColLabels: StrDyneVec = nil; AllRefs : boolean; i, j, RowNo, RefColNo, NoToAnalyze : integer; Ncases, Nrows, Ncols, df : integer; ChiSquare, ProbChi : double; yates : boolean; Adjchisqr, Adjprobchi: double; likelihood, problikelihood, phi: double; pearsonr, VarX, VarY, SumX, SumY, MantelHaenszel, MHProb, CoefCont: double; CramerV: double; lReport: TStrings; begin AllRefs := RefGrp.ItemIndex = 0; SetLength(ColNoSelected, NoVariables+1); // +1 due to RowNo in first element RowNo := GetVariableIndex(OS3MainFrm.DataGrid, RowEdit.Text); if (RowNo = -1) then begin RowEdit.Setfocus; ErrorMsg('A variable for the row labels was not entered.'); ColNoSelected := nil; exit; end; ColNoSelected[0] := RowNo; // Get row labels NRows := NoCases; SetLength(RowLabels, NRows+1); // wp: why +1? for i := 1 to NRows do RowLabels[i-1] := OS3MainFrm.DataGrid.Cells[RowNo,i]; // Get Column labels NCols := ColList.Items.Count; SetLength(ColLabels, NCols+1); // wp: why +1? for i := 0 to ColList.Items.Count-1 do begin ColLabels[i] := ColList.Items[i]; ColNoSelected[i+1] := GetVariableIndex(OS3MainFrm.DataGrid, ColLabels[i]); end; RowLabels[NRows] := 'Total'; ColLabels[NCols] := 'Total'; // Get frequencies GetFrequencies(ColNoSelected, NRows, NCols, NCases, Freq); // Calculate expected values and cell chi-squares GetExpectedAndCellChiSqr(Freq, Expected, cellChi, chiSquare, AdjChiSqr, df, yates); probChi := 1.0 - ChiSquaredProb(ChiSquare, df); // prob. larger chi if yates then AdjProbChi := 1.0 - ChiSquaredProb(AdjChiSqr, df); // Calculate column proportions (needed by Analyze() routine) ColProp := CalcColProportions(Freq); // Print results to output form lReport := TStringList.Create; try // Print frequencies tables if requested by user if ObsChk.Checked or ExpChk.Checked then begin FrequenciesPage.TabVisible := true; if ObsChk.Checked then begin IntArrayPrint(Freq, NRows+1, NCols+1, 'Frequencies', RowLabels, ColLabels, 'OBSERVED FREQUENCIES', lReport); if ExpChk.Checked then begin lReport.Add(DIVIDER_SMALL_AUTO); lReport.Add(''); end; end; if ExpChk.Checked then MatPrint(Expected, Nrows, Ncols, 'EXPECTED FREQUENCIES', RowLabels, ColLabels, NoCases, lReport); FFrequenciesReportFrame.DisplayReport(lReport); lReport.Clear; end else FrequenciesPage.TabVisible := false; // Print row/col proportions if PropChk.Checked then begin RowColPropsPage.TabVisible := true; RowProp := CalcRowProportions(Freq); MatPrint(RowProp, NRows+1, NCols+1, 'ROW PROPORTIONS', RowLabels, ColLabels, NoCases, lReport); RowProp := nil; // not needed any more lReport.Add(DIVIDER_SMALL_AUTO); lReport.Add(''); MatPrint(ColProp, NRows+1, NCols+1, 'COLUMN PROPORTIONS', RowLabels, ColLabels, NoCases, lReport); FRowColPropsReportFrame.DisplayReport(lReport); lReport.Clear; end else RowColPropsPage.TabVisible := false; // Print cell chi-sqr values if requested by user if ChiChk.Checked then begin CellChiSqrPage.TabVisible := true; MatPrint(CellChi, Nrows, Ncols, 'CHI-SQUARED VALUE FOR CELLS', RowLabels, ColLabels, NoCases, lReport); FCellChiSqrReportFrame.DisplayReport(lReport); lReport.Clear; end else CellChiSqrPage.TabVisible := false; lReport.Add('CHI-SQUARE ANALYSIS RESULTS'); lReport.Add('No. of Cases: %d', [Ncases]); lReport.Add(''); lReport.Add(''); lReport.Add( 'Chi-square: %8.3f', [ChiSquare]); lReport.Add( ' with D.F. %8d', [df]); lReport.Add( ' and probability > value: %8.3f', [ProbChi]); lReport.Add(''); if yates then begin lReport.Add('Chi-square using Yates correction: %8.3f', [AdjChiSqr]); lReport.Add(' and probability > value: %8.3f', [AdjProbChi]); end; likelihood := 0.0; for i := 0 to Nrows - 1 do for j := 0 to Ncols - 1 do if (Freq[i,j] > 0.0) then likelihood := likelihood + Freq[i,j] * (ln(Expected[i,j] / Freq[i,j])); likelihood := -2.0 * likelihood; problikelihood := 1.0 - ChiSquaredProb(likelihood, df); lReport.Add( 'Likelihood Ratio: %8.3f', [likelihood]); lReport.Add( ' with probability > value: %8.3f', [probLikelihood]); lReport.Add(''); if ((Nrows > 1) and (Ncols > 1)) then begin phi := sqrt(ChiSquare / Ncases); lReport.Add('phi Correlation: %8.3f', [phi]); pearsonr := 0.0; SumX := 0.0; SumY := 0.0; VarX := 0.0; VarY := 0.0; for i := 0 to Nrows - 1 do SumX := SumX + ( (i+1) * Freq[i,Ncols] ); for j := 0 to Ncols - 1 do SumY := SumY + ( (j+1) * Freq[Nrows,j] ); for i := 0 to Nrows - 1 do VarX := VarX + ( ((i+1)*(i+1)) * Freq[i,Ncols] ); for j := 0 to Ncols - 1 do VarY := VarY + ( ((j+1)*(j+1)) * Freq[Nrows,j] ); VarX := VarX - ((SumX * SumX) / Ncases); VarY := VarY - ((SumY * SumY) / Ncases); for i := 0 to Nrows - 1 do for j := 0 to Ncols - 1 do pearsonr := pearsonr + ((i+1)*(j+1) * Freq[i,j]); pearsonr := pearsonr - (SumX * SumY / Ncases); pearsonr := pearsonr / sqrt(VarX * VarY); lReport.Add('Pearson Correlation r: %8.3f', [pearsonr]); MantelHaenszel := (Ncases-1) * (pearsonr * pearsonr); MHprob := 1.0 - chisquaredprob(MantelHaenszel,1); lReport.Add('Mantel-Haenszel Test of Linear Association: %8.3f', [MantelHaenszel]); lReport.Add(' with probability > value %8.3f', [MHprob]); CoefCont := sqrt(ChiSquare / (ChiSquare + Ncases)); lReport.Add('The coefficient of contingency: %8.3f', [CoefCont]); if (Nrows < Ncols) then CramerV := sqrt(ChiSquare / (Ncases * ((Nrows-1)))) else CramerV := sqrt(ChiSquare / (Ncases * ((Ncols-1)))); lReport.Add('Cramers V: %8.3f', [CramerV]); end; lReport.Add(''); lReport.Add(DIVIDER_AUTO); lReport.Add(''); // Now do RIDIT analysis NoToAnalyze := ColList.Items.Count; // do an analysis for each variable as a reference variable if AllRefs then begin NoToAnalyze := ColList.Items.Count; for i := 0 to NoToAnalyze - 1 do begin RefColNo := ColNoSelected[i+1] - 2; Analyze(RefColNo, RowLabels,ColLabels, NoToAnalyze, Freq, ColProp, Nrows, lReport); if i < NoToAnalyze-1 then begin lReport.Add(''); lReport.Add(DIVIDER_AUTO); lReport.Add(''); end; end; end else // only one selected reference variable begin NoToAnalyze := ColList.Items.Count; // get column of reference variable for i := 1 to NoVariables do if (RefEdit.Text = OS3MainFrm.DataGrid.Cells[i,0]) then RefColNo := i; for j := 0 to NoToAnalyze - 1 do if (ColNoSelected[j+1] = RefColNo) then RefColNo := j; Analyze(RefColNo, RowLabels,ColLabels, NoToAnalyze, Freq, ColProp, Nrows, lReport); end; FReportFrame.DisplayReport(lReport); finally lReport.Free; end; end; procedure TRIDITForm.GetExpectedAndCellChiSqr(const AFrequencies: IntDyneMat; out AExpected, ACellChiSqr: DblDyneMat; out AChiSqr, AdjChiSqr: Double; out ADegreesOfFreedom: Integer; out AYates: Boolean); var i, j, n, m: Integer; numRows, numCols, numCases: Integer; begin MatSize(AFrequencies, n,m); // n,m contain totals row and column numRows := n-1; numCols := m-1; numCases := AFrequencies[numRows, numCols]; AExpected := nil; ACellChiSqr := nil; SetLength(AExpected, numRows, numCols); SetLength(ACellChiSqr, numRows, numCols); AChiSqr := 0; AdjChiSqr := -1; // -1 indicates non-existing value in case of AYates=false AYates := YatesChk.Checked and (numRows = 2) and (numCols = 2); if (numRows > 1) and (numCols > 1) then begin for i := 0 to numRows-1 do begin for j := 0 to numCols-1 do begin AExpected[i, j] := AFrequencies[numRows, j] * AFrequencies[i, numCols] / numCases; if (AExpected[i, j] > 0) then ACellChiSqr[i, j] := sqr(AFrequencies[i, j] - AExpected[i, j]) / AExpected[i, j] else begin ErrorMsg('Zero expected value found.'); ACellChiSqr[i, j] := 0; end; AChiSqr := AChiSqr + ACellChiSqr[i, j]; end; end; ADegreesOfFreedom := (numRows - 1) * (numCols - 1); // 2x2 corrected chi-square if AYates then begin AdjChiSqr := abs((AFrequencies[0, 0] * AFrequencies[1, 1]) - (AFrequencies[0, 1] * AFrequencies[1, 0])); AdjChiSqr := sqr(AdjChiSqr - numCases / 2.0) * numCases; // numerator AdjChiSqr := AdjChiSqr / (AFrequencies[0, 2] * AFrequencies[1,2] * AFrequencies[2,0] * AFrequencies[2,1]); end; end; // Equal probability if (numRows = 1) then begin for j := 0 to numCols - 1 do begin AExpected[0, j] := numCases / numCols; if (AExpected[0, j] > 0) then ACellChiSqr[0, j] := sqr(AFrequencies[0, j] - AExpected[0, j]) / AExpected[0, j]; AChiSqr := AChiSqr + ACellChiSqr[0, j]; end; ADegreesOfFreedom := numCols - 1; end; // Equal probability if (numCols = 1) then begin for i := 0 to numRows - 1 do begin AExpected[i, 0] := numCases / numRows; if (AExpected[i, 0] > 0) then ACellChiSqr[i, 0] := sqr(AFrequencies[i, 0] - AExpected[i, 0]) / AExpected[i, 0]; AChiSqr := AChiSqr + ACellChiSqr[i, 0]; end; ADegreesOfFreedom := numRows - 1; end; end; procedure TRIDITForm.GetFrequencies(const AColNoSelected: IntDyneVec; ANumRows, ANumCols: Integer; out ANumCases: Integer; out AFrequencies: IntDyneMat); var i, j, row, col: Integer; begin // over-dimension the matrix because row and column totals will be in // last column and row. AFrequencies := nil; SetLength(AFrequencies, ANumRows+1, ANumCols+1); for i := 0 to ANumRows do for j := 0 to ANumCols do AFrequencies[i, j] := 0; // Get cell data ANumCases := 0; for i := 0 to NoCases-1 do begin row := i+1; for j := 0 to ANumCols-1 do begin col := AColNoSelected[j+1]; // +1 because row index is at index 0 AFrequencies[i, j] := StrToInt(OS3MainFrm.DataGrid.Cells[col, row]); ANumCases := ANumCases + AFrequencies[i, j]; end; end; AFrequencies[ANumRows, ANumCols] := ANumCases; // Calculate row totals for i := 0 to ANumRows-1 do for j := 0 to ANumCols-1 do AFrequencies[i, ANumCols] := AFrequencies[i, ANumCols] + AFrequencies[i, j]; // Calculate col totals for j := 0 to ANumCols-1 do for i := 0 to ANumRows-1 do AFrequencies[ANumRows, j] := AFrequencies[ANumRows, j] + AFrequencies[i, j]; end; procedure TRIDITForm.RefGrpClick(Sender: TObject); begin RefEdit.Visible := RefGrp.ItemIndex > 0; Label4.Visible := RefEdit.Visible; end; procedure TRIDITForm.Reset; var i: integer; begin inherited; FrequenciesPage.TabVisible := false; RowColPropsPage.TabVisible := false; CellChiSqrPage.TabVisible := false; if FFrequenciesReportFrame <> nil then FFrequenciesReportFrame.Clear; if FRowColPropsReportFrame <> nil then FRowColPropsReportframe.Clear; if FCellChiSqrReportFrame <> nil then FCellChiSqrReportFrame.Clear; VarList.Clear; for i := 1 to NoVariables do VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i, 0]); ColList.Clear; RowEdit.Clear; RefEdit.Clear; AlphaEdit.Text := FormatFloat('0.00', DEFAULT_ALPHA_LEVEL); BonChk.Checked := false; Label4.Visible := false; RefEdit.Visible := false; RefGrp.ItemIndex := 0; UpdateBtnStates; end; procedure TRIDITForm.RowInClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (RowEdit.Text = '') then begin RowEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); end; UpdateBtnStates; end; procedure TRIDITForm.RowOutClick(Sender: TObject); begin if RowEdit.Text <> '' then begin VarList.Items.Add(RowEdit.Text); RowEdit.Text := ''; end; UpdateBtnStates; end; procedure TRIDITForm.UpdateBtnStates; begin inherited; if FFrequenciesReportFrame <> nil then FFrequenciesReportFrame.UpdateBtnStates; if FRowColPropsReportFrame <> nil then FRowColPropsReportframe.UpdateBtnStates; if FCellChiSqrReportFrame <> nil then FCellChiSqrReportFrame.UpdateBtnStates; RowIn.Enabled := (VarList.ItemIndex > -1) and (RowEdit.Text = ''); RowOut.Enabled := (RowEdit.Text <> ''); ColIn.Enabled := AnySelected(VarList); ColOut.Enabled := (ColList.ItemIndex > -1); end; function TRIDITForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; var tmp: Double; begin Result := false; if ColList.Count = 0 then begin AControl := ColList; AMsg := 'No column variable(s) selected.'; exit; end; if AlphaEdit.Text = '' then begin AControl := AlphaEdit; AMsg := 'Alpha level not specified.'; exit; end; if not TryStrToFloat(AlphaEdit.Text, tmp) then begin AControl := AlphaEdit; AMsg := 'Numeric input required for alpha level.'; exit; end; if (tmp <= 0) or (tmp >= 1) then begin AControl := AlphaEdit; AMsg := 'Alpha level must be > 0 and < 1'; exit; end; Result := true; end; procedure TRIDITForm.VarListDblClick(Sender: TObject); var index: Integer; s: String; begin index := VarList.ItemIndex; if index > -1 then begin s := VarList.Items[index]; if RowEdit.Text = '' then RowEdit.Text := s else ColList.Items.Add(s); VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TRIDITForm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; end.