unit WLSUnit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons, ExtCtrls, ComCtrls, Globals, MainUnit, DictionaryUnit, Matrixlib, DataProcs, MathUnit, ReportFrameUnit, ChartFrameUnit, BasicStatsParamsFormUnit; type { TWLSFrm } TWLSFrm = class(TBasicStatsParamsForm) OptionsBevel: TBevel; DepInBtn: TBitBtn; DepOutBtn: TBitBtn; IndInBtn: TBitBtn; IndOutBtn: TBitBtn; PageControl: TPageControl; OLSPage: TTabSheet; ResRegPageControl: TPageControl; ResidualsRegPage: TTabSheet; ResRegReportPage: TTabSheet; UserWeightsChk: TRadioButton; WeightChk: TRadioButton; WLSPage: TTabSheet; WeightInBtn: TBitBtn; WeightOutBtn: TBitBtn; SaveChk: TCheckBox; OriginChk: TCheckBox; Origin2Chk: TCheckBox; DepVarEdit: TEdit; WeightVarEdit: TEdit; OptionsGroup: TGroupBox; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; IndVarList: TListBox; VarList: TListBox; procedure DepInBtnClick(Sender: TObject); procedure DepOutBtnClick(Sender: TObject); procedure IndInBtnClick(Sender: TObject); procedure IndOutBtnClick(Sender: TObject); procedure IndVarListDblClick(Sender: TObject); procedure UserWeightsChkChange(Sender: TObject); procedure VarListDblClick(Sender: TObject); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); procedure WeightInBtnClick(Sender: TObject); procedure WeightOutBtnClick(Sender: TObject); private OLSReportFrame: TReportFrame; ResidualsRegReportFrame: TReportFrame; WLSReportFrame: TReportFrame; procedure CreateOrGetChartFrame(AColIndex: Integer; AVarName: String; out AMemo: TMemo; out AChartFrame: TChartFrame); function GetPageCaption(AVarName: String): String; procedure PlotSquaredResiduals(AIndepCols: IntDyneVec; ANumIndepCols, ADepCol: Integer; AConfLevel: Double); procedure PlotXY(AChartFrame: TChartFrame; const XPoints, YPoints: DblDyneVec; const ARegressionResults: TBivariateRegressionResults; const XLabel, YLabel: String); procedure PredictIt(ColNoSelected: IntDyneVec; NoVars: integer; Means, StdDevs, BetaWeights: DblDyneVec; StdErrEst: double; NoIndepVars: integer); function PrepareData(out ADepCol, ANumIndepCols: Integer; out AIndepCols: IntDyneVec; out AWeightCol: Integer; out ARowLabels: StrDyneVec): Boolean; procedure WriteDescriptiveReport(AMemo: TMemo; const ARegressionResults: TBivariateRegressionResults; const XLabel, YLabel: String); 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 WLSFrm: TWLSFrm; implementation {$R *.lfm} uses Math, TAChartUtils, TAChartAxisUtils, TALegend, TASources, TACustomSeries, Utils, GridProcs; { TWLSFrm } constructor TWLSFrm.Create(AOwner: TComponent); begin inherited; if DictionaryFrm = nil then Application.CreateForm(TDictionaryFrm, DictionaryFrm); OLSReportFrame := TReportFrame.Create(self); OLSReportFrame.Name := ''; OLSReportFrame.Parent := OLSPage; OLSReportFrame.Align := alClient; OLSReportFrame.BorderSpacing.Left := 0; OLSReportFrame.BorderSpacing.Top := 0; OLSReportFrame.BorderSpacing.Bottom := 0; OLSReportFrame.BorderSpacing.Right := 0; InitToolbar(OLSReportFrame.ReportToolbar, tpRight); ResidualsRegReportFrame := TReportFrame.Create(self); ResidualsRegReportFrame.Name := ''; ResidualsRegReportFrame.Parent := ResRegReportPage; ResidualsRegReportFrame.Align := alClient; ResidualsRegReportFrame.BorderSpacing.Left := 0; ResidualsRegReportFrame.BorderSpacing.Top := 0; ResidualsRegReportFrame.BorderSpacing.Bottom := 0; ResidualsRegReportFrame.BorderSpacing.Right := 0; InitToolbar(ResidualsRegReportFrame.ReportToolbar, tpRight); WLSReportFrame := TReportFrame.Create(self); WLSReportFrame.Name := ''; WLSReportFrame.Parent := WLSPage; WLSReportFrame.Align := alClient; WLSReportFrame.BorderSpacing.Left := 0; WLSReportFrame.BorderSpacing.Top := 0; WLSReportFrame.BorderSpacing.Bottom := 0; WLSReportFrame.BorderSpacing.Right := 0; InitToolbar(WLSReportFrame.ReportToolbar, tpRight); end; procedure TWLSFrm.AdjustConstraints; begin ParamsPanel.Constraints.MinHeight := DepInBtn.Top + (IndOutBtn.Top - DepInBtn.Top)*2 + DepInBtn.Top + OptionsGroup.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; ParamsPanel.Constraints.MinWidth := Max( 4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left, OptionsGroup.Width ); end; procedure TWLSFrm.Compute; var i, j, Noindep, DepCol, WghtCol, olddepcol, NCases, pos, col: integer; IndepCols: IntDyneVec = nil; RowLabels: StrDyneVec = nil; X, Y: double; Means: DblDyneVec = nil; Variances: DblDyneVec = nil; StdDevs: DblDyneVec = nil; BWeights: DblDyneVec = nil; BetaWeights: DblDyneVec = nil; BStdErrs: DblDyneVec = nil; BtTests: DblDyneVec = nil; tProbs: DblDyneVec = nil; PrintDesc: boolean = true; predicted: Double; lReport: TStrings; StdErrEst: Double = 0.0; R2: Double = 0.0; errorcode: Boolean = false; begin PrintDesc := true; SetLength(Means, NoVariables + 2); SetLength(Variances, NoVariables + 2); SetLength(StdDevs, NoVariables + 2); SetLength(BWeights, NoVariables + 2); SetLength(BetaWeights, NoVariables + 2); SetLength(BStdErrs, NoVariables + 2); SetLength(Bttests, NoVariables + 2); SetLength(tprobs, NoVariables + 2); // SetLength(RowLabels, NoVariables + 2); // SetLength(IndepCols, NoVariables + 2); // SetLength(ColNoSelected, 2); lReport := TStringList.Create; try NCases := NoCases; // NoIndep := IndVarList.Items.Count; if not PrepareData(depCol, NoIndep, indepCols, wghtCol, RowLabels) then exit; { wp: I think this is not correct: The column index is the last one in this call. And why is row 0 checked? // check variable types if not ValidValue(OS3MainFrm.DataGrid, DepCol, 0) then begin ErrorMsg('Incorrect dependent variable type.'); exit; end; if (WghtCol > -1) then begin if not ValidValue(OS3MainFrm.DataGrid, WghtCol, 0) then begin ErrorMsg('Incorrect weight variable type.'); exit; end; end; for j := 0 to Noindep - 1 do begin if not ValidValue(OS3MainFrm.DataGrid, IndepCols[j],0) then begin ErrorMsg('Incorrect dependent variable type.'); exit; end; end; } IndepCols[NoIndep] := depCol; oldDepCol := DepCol; // save dependent column so we can reuse DepCol // *** Get OLS regression *** lReport.Clear; lReport.Add('ORDINARY LEAST SQUARES (OLS) REGRESSION RESULTS'); lReport.Add(''); MReg(Noindep, IndepCols, DepCol, RowLabels, Means, Variances, StdDevs, BWeights, BetaWeights, BStdErrs, Bttests, tprobs, R2, stderrest, NCases, errorcode, PrintDesc, lReport); // Get predicted z score, residual z score, predicted raw score, // residual raw score and squared raw residual score. Place in the DataGrid PredictIt(IndepCols, Noindep+1, Means, StdDevs, BetaWeights, stderrest, NoIndep); OLSReportFrame.DisplayReport(lReport); lReport.Clear; // *** Regress the squared residuals on the predictors *** depCol := NoVariables; lReport.Clear; lReport.Add('REGRESSION OF SQUARED RESIDUALS ON INDEPENDENT VARIABLES'); lReport.Add(''); MReg(Noindep, IndepCols, DepCol, RowLabels, Means, Variances, StdDevs, BWeights, BetaWeights, BStdErrs, Bttests, tprobs, R2, stderrest, NCases, errorcode, PrintDesc, lReport); ResidualsRegReportFrame.DisplayReport(lReport); lReport.Clear; if WeightChk.Checked then begin // Get predicted squared residuals and save recipricols as weights col := NoVariables + 1; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); DictionaryFrm.NewVar(col); DictionaryFrm.DictGrid.Cells[1,col] := 'PredResid2'; OS3MainFrm.DataGrid.Cells[col,0] := 'PredResid2'; col := NoVariables + 1; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); DictionaryFrm.NewVar(col); DictionaryFrm.DictGrid.Cells[1,col] := 'WEIGHT'; OS3MainFrm.DataGrid.Cells[col,0] := 'WEIGHT'; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); for i := 1 to NoCases do begin if (DataProcs.ValidValue(i, col-2)) then // do we have a valid squared OLS residual? begin predicted := 0.0; for j := 0 to NoIndep - 1 do begin pos := IndepCols[j]; X := StrToFloat(OS3MainFrm.DataGrid.Cells[pos,i]); predicted := predicted + BWeights[j] * X; end; predicted := predicted + BWeights[Noindep]; predicted := abs(predicted); OS3MainFrm.DataGrid.Cells[col-1,i] := Format('%.3f', [predicted]); if (predicted > 0.0) then predicted := 1.0 / sqrt(predicted) else predicted := 0.0; OS3MainFrm.DataGrid.Cells[col,i] := Format('%.3f', [predicted]); end; // if valid case end; // next i end; // if regresChk // *** Display squared residuals for each independent variable *** PlotSquaredResiduals(IndepCols, NoIndep, DepCol, 0.95); if not UserWeightsChk.Checked then begin // Weight variables and do OLS regression on weighted variables DepCol := olddepcol; IndepCols[Noindep] := DepCol; for i := 1 to NoCases do begin Y := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[NoVariables,i])); // weight for j := 0 to Noindep do begin pos := IndepCols[j]; X := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[pos,i])); X := X * Y; OS3MainFrm.DataGrid.Cells[pos,i] := FloatToStr(X); end; end; // get means of variables and subtract from the values if OriginChk.Checked then begin for j := 0 to NoIndep do begin Means[j] := 0.0; NCases := 0; pos := IndepCols[j]; for i := 1 to NoCases do begin if (DataProcs.ValidValue(i,pos)) then begin Means[j] := Means[j] + StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[pos,i])); NCases := NCases + 1; end; end; Means[j] := Means[j] / NCases; for i := 1 to NoCases do begin if (DataProcs.ValidValue(i,pos)) then begin X := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[pos,i])); X := X - Means[j]; OS3MainFrm.DataGrid.Cells[pos,i] := FloatToStr(X); end; end; // next i end; // next j end; // if origin checked lReport.Clear; lReport.Add('WEIGHTED LEAST SQUARES (WLS) REGRESSION RESULTS'); lReport.Add(''); MReg(Noindep, IndepCols, DepCol, RowLabels, Means, Variances, StdDevs, BWeights, BetaWeights, BStdErrs, Bttests, tprobs, R2, stderrest, NCases, errorcode, PrintDesc, lReport); WLSReportFrame.DisplayReport(lReport); lReport.Clear; end // if useweightschk checked else // use the weights entered by the user if (UserWeightsChk.Checked) then begin // Weight variables and do OLS regression on weighted variables DepCol := olddepcol; IndepCols[Noindep] := DepCol; for i := 1 to NoCases do begin Y := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[WghtCol,i])); // weight for j := 0 to Noindep do begin pos := IndepCols[j]; X := StrToFloat(OS3MainFrm.DataGrid.Cells[pos,i]); X := X * Y; OS3MainFrm.DataGrid.Cells[pos,i] := FloatToStr(X); end; end; if (Origin2Chk.Checked) then // get means of variables and subtract from the values begin for j := 0 to Noindep do begin Means[j] := 0.0; NCases := 0; pos := IndepCols[j]; for i := 1 to NoCases do begin if (DataProcs.ValidValue(i,pos)) then begin Means[j] := Means[j] + StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[pos,i])); NCases := NCases + 1; end; end; Means[j] := Means[j] / NCases; for i := 1 to NoCases do begin if (DataProcs.ValidValue(i,pos)) then begin X := StrToFloat(OS3MainFrm.DataGrid.Cells[pos,i]); X := X - Means[j]; OS3MainFrm.DataGrid.Cells[pos,i] := FloatToStr(X); end; end; // next i end; // next j end; // if origin checked lReport.Clear; lReport.Add('WEIGHTED LEAST SQUARES (WLS) REGRESSION RESULTS'); lReport.Add(''); MReg(Noindep, IndepCols, DepCol, RowLabels, Means, Variances, StdDevs, BWeights, BetaWeights, BStdErrs, Bttests, tprobs, R2, stderrest, NCases, errorcode, PrintDesc, lReport); WLSReportFrame.DisplayReport(lReport); lReport.Clear; end; finally lReport.Free; end; end; procedure TWLSFrm.CreateOrGetChartFrame(AColIndex: Integer; AVarName: String; out AMemo: TMemo; out AChartFrame: TChartFrame); var sheetTitle: String; tabSheet: TTabSheet; i: Integer; begin sheetTitle := GetPageCaption(AVarName); // Find existing sheet first. for i := 1 to ResRegPageControl.PageCount-1 do if ResRegPageControl.Pages[i].Caption = sheetTitle then begin tabSheet := ResRegPageControl.Pages[i]; AChartFrame := tabSheet.Controls[0] as TChartFrame; AMemo := tabSheet.Controls[1] as TMemo; exit; end; // Not found: create new sheet ... tabSheet := ResRegPageControl.AddTabSheet; tabSheet.Caption := sheetTitle; tabSheet.Tag := AColIndex; // ... and add ChartFrame AChartFrame := TChartFrame.Create(tabSheet); AChartFrame.Parent := tabSheet; AChartFrame.Align := alClient; AChartFrame.Chart.Legend.Alignment := laBottomCenter; AChartFrame.Chart.Legend.ColumnCount := 3; AChartFrame.Chart.Legend.TextFormat := tfHTML; AChartFrame.Chart.BottomAxis.Intervals.MaxLength := 80; AChartFrame.Chart.BottomAxis.Intervals.MinLength := 30; with AChartFrame.Chart.AxisList.Add do begin Alignment := calRight; Marks.Source := TListChartSource.Create(self); Marks.Style := smsLabel; Grid.Visible := false; TickColor := clNone; end; with AChartFrame.Chart.AxisList.Add do begin Alignment := calTop; Marks.Source := TListChartSource.Create(self); Marks.Style := smsLabel; Grid.Visible := false; TickColor := clNone; end; // ... and add memo AMemo := TMemo.Create(tabSheet); AMemo.Parent := tabSheet; AMemo.Align := alBottom; AMemo.BorderStyle := bsNone; AMemo.Font.Name := 'Courier New'; AMemo.Font.Size := 8; AMemo.ReadOnly := true; AMemo.Scrollbars := ssAutoBoth; AMemo.WordWrap := false; // ... and splitter with TSplitter.Create(tabSheet) do begin Parent := tabSheet; Align := alBottom; end; end; procedure TWLSFrm.DepInBtnClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (DepVarEdit.Text = '') then begin DepVarEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TWLSFrm.DepOutBtnClick(Sender: TObject); begin if (DepVarEdit.Text <> '') then begin VarList.Items.Add(DepVarEdit.Text); DepVarEdit.Text := ''; UpdateBtnStates; end; end; function TWLSFrm.GetPageCaption(AVarName: String): String; begin Result := 'Plot of ' + AVarName; end; procedure TWLSFrm.IndInBtnClick(Sender: TObject); var i: integer; begin i := 0; while (i < VarList.Items.Count) do begin if (VarList.Selected[i]) then begin IndVarList.Items.Add(VarList.Items[i]); VarList.Items.Delete(i); i := 0; end else inc(i); end; UpdateBtnStates; end; procedure TWLSFrm.IndOutBtnClick(Sender: TObject); var i: integer; begin i := 0; while (i < IndVarList.Items.Count) do begin if IndVarlist.Selected[i] then begin VarList.Items.Add(IndVarList.Items[i]); IndVarlist.Items.Delete(i); i := 0; end else inc(i); end; UpdateBtnStates; end; procedure TWLSFrm.IndVarListDblClick(Sender: TObject); var index: Integer; begin index := IndVarList.ItemIndex; if index > -1 then begin VarList.Items.Add(IndVarList.Items[index]); IndVarlist.Items.Delete(index); UpdateBtnStates; end; end; procedure TWLSFrm.PlotSquaredResiduals(AIndepCols: IntDyneVec; ANumIndepCols, ADepCol: Integer; AConfLevel: Double); var xCol, yCol: Integer; xLabel, yLabel: String; i: Integer; colNoSelected: IntDyneVec = nil; xPoints: DblDyneVec = nil; yPoints: DblDyneVec = nil; regressionRes: TBivariateRegressionResults; memo: TMemo; chartFrame: TChartFrame; begin SetLength(colNoSelected, 2); xCol := ADepCol; for i := 0 to ANumIndepCols-1 do begin // Get values yCol := AIndepCols[i]; colNoSelected[0] := xCol; colNoSelected[1] := yCol; xLabel := OS3MainFrm.DataGrid.Cells[xCol, 0]; yLabel := OS3MainFrm.DataGrid.Cells[yCol, 0]; xPoints := CollectValues(OS3MainFrm.DataGrid, xCol, colNoSelected); yPoints := CollectValues(OS3MainFrm.DataGrid, yCol, colNoSelected); SortOnX(xPoints, yPoints); // Regression Calc_BivariateRegression(xPoints, yPoints, AConfLevel, regressionRes); // Create tab with chart and report controls CreateOrGetChartFrame(yCol, yLabel, memo, chartFrame); // Plot PlotXY(chartFrame, xPoints, yPoints, regressionRes, xLabel, yLabel); // Print the descriptive statistics WriteDescriptiveReport(memo, regressionRes, xLabel, yLabel); end; end; procedure TWLSFrm.PlotXY(AChartFrame: TChartFrame; const XPoints, YPoints: DblDyneVec; const ARegressionResults: TBivariateRegressionResults; const XLabel, YLabel: String); var xpts: DblDyneVec = nil; ypts: DblDyneVec = nil; conf: DblDyneVec = nil; ext: TDoubleRect; i: Integer; rightLabels, topLabels: TListChartSource; ser: TChartSeries; begin rightLabels := AChartFrame.Chart.AxisList[2].Marks.Source as TListChartSource; rightLabels.Clear; topLabels := AChartFrame.Chart.AxisList[3].Marks.Source as TListChartSource; topLabels.Clear; AChartFrame.Clear; with ARegressionResults do AChartFrame.SetFooter(Format('R(X,Y) = %.3f, Slope = %.3f, Intercept = %.3f', [ R, Slope, Intercept ])); // Data points AChartFrame.SetXTitle(XLabel); AChartFrame.SetYTitle(YLabel); AChartFrame.PlotXY(ptSymbols, XPoints, YPoints, nil, nil, 'Data', DATA_COLORS[0]); // Regression line SetLength(xpts, 2); SetLengtH(ypts, 2); ext := AChartFrame.Chart.GetFullExtent; xpts[0] := ext.a.x; xpts[1] := ext.b.x; with ARegressionResults do begin ypts[0] := Intercept + Slope * xpts[0]; ypts[1] := Intercept + Slope * xpts[1]; end; AChartFrame.PlotXY(ptLines, xpts, ypts, nil, nil, 'Predicted', clBlack); rightLabels.Add(ypts[1], ypts[1], 'Predicted'); // Upper ... SetLength(conf, ARegressionResults.Count); for i := 0 to High(conf) do conf[i] := ARegressionResults.ConfidenceLimits(XPoints[i], true); ser := AChartFrame.PlotXY(ptLines, XPoints, conf, nil, nil, 'Upper confidence band', clRed); rightLabels.Add(ser.yValue[ser.Count-1], ser.yValue[ser.Count-1], 'UCL'); // ... and lower confidence limit curves for i := 0 to High(conf) do conf[i] := ARegressionResults.ConfidenceLimits(XPoints[i], false); ser := AChartFrame.PlotXY(ptLines, XPoints, conf, nil, nil, 'Lower confidence band', clRed); rightLabels.Add(ser.yValue[ser.Count-1], ser.yValue[ser.Count-1], 'LCL'); // Mean lines with ARegressionResults do begin AChartFrame.VertLine(XMean, clGreen, psDashDot, 'Mean ' + XLabel); topLabels.Add(XMean, XMean, 'Mean ' + XLabel); AChartFrame.HorLine(YMean, clGreen, psDash, 'Mean ' + YLabel); rightLabels.Add(YMean, YMean, 'Mean ' + YLabel); end; end; { Routine obtains predicted raw and standardized scores and their residuals. It is assumed that the dependent variable is last in the list of variable column pointers stored in the ColNoSelected vector. Get the z predicted score and its residual } procedure TWLSFrm.PredictIt(ColNoSelected: IntDyneVec; NoVars: integer; Means, StdDevs, BetaWeights: DblDyneVec; StdErrEst: double; NoIndepVars: integer); var col, i, j, k, Index: integer; predicted, zpredicted, z1, z2, resid, residsqr: double; begin col := NoVariables + 1; // NoVariables := col; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); DictionaryFrm.DictGrid.ColCount := 8; DictionaryFrm.NewVar(col); OS3MainFrm.DataGrid.Cells[col,0] := 'Pred.z'; DictionaryFrm.DictGrid.Cells[1,col] := 'Pred.z'; col := NoVariables + 1; // NoVariables := col; DictionaryFrm.NewVar(col); OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); OS3MainFrm.DataGrid.Cells[col,0] := 'z Resid.'; DictionaryFrm.DictGrid.Cells[1,col] := 'z Resid.'; OS3MainFrm.DataGrid.ColCount := OS3MainFrm.DataGrid.ColCount + 2; for i := 1 to NoCases do begin zpredicted := 0.0; for j := 0 to NoIndepVars - 1 do begin k := ColNoSelected[j]; z1 := (StrToFloat(OS3MainFrm.DataGrid.Cells[k,i]) - Means[j]) / StdDevs[j]; zpredicted := zpredicted + (z1 * BetaWeights[j]); end; OS3MainFrm.DataGrid.Cells[col-1,i] := Format('%.4f',[zpredicted]); if StdDevs[NoVars-1] <> 0.0 then begin Index := ColNoSelected[NoVars-1]; z2 := StrToFloat(OS3MainFrm.DataGrid.Cells[Index,i]); z2 := (z2 - Means[NoVars-1]) / StdDevs[NoVars-1]; // z score OS3MainFrm.DataGrid.Cells[col,i] := Format('%.4f',[z2 - zpredicted]); // z residual end; end; // Get raw predicted and residuals col := NoVariables + 1; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); DictionaryFrm.NewVar(col); DictionaryFrm.DictGrid.Cells[1,col] := 'Pred.Raw'; OS3MainFrm.DataGrid.Cells[col,0] := 'Pred.Raw'; // calculate raw predicted scores and store in DataGrid at col for i := 1 to NoCases do begin // predicted raw obtained from previously predicted z score predicted := StrToFloat(OS3MainFrm.DataGrid.Cells[col-2,i]) * StdDevs[NoVars-1] + Means[NoVars-1]; OS3MainFrm.DataGrid.Cells[col,i] := Format('%.3f',[predicted]); end; // Calculate residuals of predicted raw scores end; col := NoVariables +1; OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); DictionaryFrm.NewVar(col); DictionaryFrm.DictGrid.Cells[1,col] := 'Raw Resid.'; OS3MainFrm.DataGrid.Cells[col,0] := 'Raw Resid.'; for i := 1 to NoCases do begin Index := ColNoSelected[NoVars-1]; resid := StrToFloat(OS3MainFrm.DataGrid.Cells[col-1,i]) - StrToFloat(OS3MainFrm.DataGrid.Cells[Index,i]); OS3MainFrm.DataGrid.Cells[col,i] := Format('%.3f',[resid]); end; // get square of raw residuals col := NoVariables + 1; DictionaryFrm.NewVar(col); OS3MainFrm.NoVarsEdit.Text := IntToStr(NoVariables); DictionaryFrm.DictGrid.Cells[1,col] := 'ResidSqr'; OS3MainFrm.DataGrid.Cells[col,0] := 'ResidSqr'; for i := 1 to NoCases do begin residsqr := StrToFloat(OS3MainFrm.DataGrid.Cells[col-1,i]); residsqr := residsqr * residsqr; OS3MainFrm.DataGrid.Cells[col,i] := Format('%8.3f',[residsqr]); end; end; function TWLSFrm.PrepareData(out ADepCol, ANumIndepCols: Integer; out AIndepCols: IntDyneVec; out AWeightCol: Integer; out ARowLabels: StrDyneVec): Boolean; var i: Integer; msg: String; C: TWinControl; begin Result := false; AIndepCols := nil; ARowLabels := nil; if not Validate(msg, C) then begin C.SetFocus; ErrorMsg(msg); exit; end; ANumIndepCols := IndVarList.Items.Count; ADepCol := GetVariableIndex(OS3MainFrm.DataGrid, DepVarEdit.Text); AWeightCol := GetVariableIndex(OS3MainFrm.DataGrid, WeightVarEdit.Text); // The IndepCols store also other variables, in addition to the "real" // independent variables. Until I know how this works, this array must be // over-dimensions. // ARowLabels alike. SetLength(AIndepCols, NoVariables + 2); SetLength(ARowLabels, NoVariables); for i := 0 to ANumIndepCols-1 do begin AIndepCols[i] := GetVariableIndex(OS3MainFrm.DataGrid, IndVarList.Items[i]); if AIndepCols[i] = -1 then begin ErrorMsg('Dependent variable %s not found.', [IndVarList.Items[i]]); exit; end; ARowLabels[i] := IndVarList.Items[i]; end; Result := true; end; procedure TWLSFrm.Reset; var i: integer; begin inherited; VarList.Clear; for i := 0 to NoVariables - 1 do VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i+1,0]); IndVarList.Clear; DepVarEdit.Clear; WeightVarEdit.Clear; if OLSReportFrame <> nil then OLSReportFrame.Clear; if ResidualsRegReportFrame <> nil then ResidualsRegReportFrame.clear; if WLSReportFrame <> nil then WLSReportFrame.Clear; WeightChk.Checked := true; UserWeightsChk.Checked := false; OriginChk.Checked := false; Origin2Chk.Checked := false; UpdateBtnStates; end; procedure TWLSFrm.UpdateBtnStates; var i: Integer; lSelected: Boolean; begin inherited; lSelected := false; for i:=0 to VarList.Items.Count-1 do if Varlist.Selected[i] then begin lSelected := true; break; end; DepInBtn.Enabled := lSelected and (DepVarEdit.Text = ''); IndInBtn.Enabled := lSelected; WeightInBtn.Enabled := lSelected and (WeightVarEdit.Text = '') and UserWeightsChk.Checked; lSelected := false; for i:=0 to IndVarList.Items.Count-1 do if IndVarList.Selected[i] then begin lSelected := true; break; end; DepOutBtn.Enabled := (DepVarEdit.Text <> ''); IndOutBtn.Enabled := lSelected; WeightOutBtn.Enabled := (WeightVarEdit.Text <> '') and UserWeightsChk.Checked; if OLSReportFrame <> nil then OLSReportFrame.UpdateBtnStates; if ResidualsRegReportFrame <> nil then ResidualsRegReportFrame.UpdateBtnStates; if WLSReportFrame <> nil then WLSReportFrame.UpdateBtnStates; end; procedure TWLSFrm.UserWeightsChkChange(Sender: TObject); begin WeightVarEdit.Enabled := UserWeightsChk.Checked; Label4.Enabled := WeightVarEdit.Enabled; WeightInBtn.Enabled := UserWeightsChk.Checked and (VarList.ItemIndex > -1) and (WeightVarEdit.Text = ''); WeightOutBtn.Enabled := UserWeightsChk.Checked and (WeightVarEdit.Text <> ''); end; function TWLSFrm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; begin Result := false; if DepVarEdit.Text = '' then begin AControl := DepVarEdit; AMsg := 'No dependent variable selected.'; exit; end; if IndVarList.Items.Count = 0 then begin AControl := VarList; AMsg := 'No independent variables selected.'; exit; end; Result := true; end; procedure TWLSFrm.VarListDblClick(Sender: TObject); var index: Integer; begin index := VarList.ItemIndex; if index > -1 then begin if DepVarEdit.Text = '' then DepVarEdit.Text := VarList.Items[index] else IndVarList.Items.Add(VarList.Items[index]); VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TWLSFrm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; procedure TWLSFrm.WeightInBtnClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (WeightVarEdit.Text = '') then begin WeightVarEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); end; UpdateBtnStates; end; procedure TWLSFrm.WeightOutBtnClick(Sender: TObject); begin if (WeightVarEdit.Text <> '') then begin VarList.Items.Add(WeightVarEdit.Text); WeightVarEdit.Text := ''; end; UpdateBtnStates; end; procedure TWLSFrm.WriteDescriptiveReport(AMemo: TMemo; const ARegressionResults: TBivariateRegressionResults; const XLabel, YLabel: String); var lReport: TStrings; begin lReport := TStringList.Create; try { not needed - requires too much space lReport.Add('Data file: %s', [OS3MainFrm.FileNameEdit.Text]); lReport.Add(''); lReport.Add('Variables:'); lReport.Add(' X: %s', [xLabel]); lReport.Add(' Y: %s', [yLabel]); lReport.Add(''); } lReport.Add('Variable Mean Variance Std.Dev.'); lReport.Add('---------- -------- -------- --------'); with ARegressionResults do begin lReport.Add('%-10s %8.2f %8.2f %8.2f', [xLabel, XMean, XVariance, XStdDev]); lReport.Add('%-10s %8.2f %8.2f %8.2f', [yLabel, YMean, YVariance, YStdDev]); lReport.Add(''); lReport.Add('Regression:'); lReport.Add(' Correlation: %8.3f', [R]); lReport.Add(' Slope: %8.3f', [Slope]); lReport.Add(' Intercept: %8.3f', [Intercept]); lReport.Add(' Standard Error of Estimate: %8.3f', [StdErrorPredicted]); lReport.Add(' Number of good cases: %8d', [Count]); end; AMemo.Lines.Assign(lReport); finally lReport.Free; end; end; end.