// Use file "cansas.laz" for testing unit PlotXYUnit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Buttons, ComCtrls, Grids, MainUnit, Globals, MathUnit, BasicStatsReportAndChartFormUnit, ReportFrameUnit, ChartFrameUnit; type { TPlotXYForm } TPlotXYForm = class(TBasicStatsReportAndChartForm) ConfEdit: TEdit; Label4: TLabel; LineChk: TCheckBox; MeansChk: TCheckBox; ConfChk: TCheckBox; OptionsGroup: TGroupBox; YEdit: TEdit; Label3: TLabel; XEdit: TEdit; Label2: TLabel; XInBtn: TBitBtn; XOutBtn: TBitBtn; YInBtn: TBitBtn; YOutBtn: TBitBtn; Label1: TLabel; VarList: TListBox; procedure VarListDblClick(Sender: TObject); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); procedure XInBtnClick(Sender: TObject); procedure XOutBtnClick(Sender: TObject); procedure YInBtnClick(Sender: TObject); procedure YOutBtnClick(Sender: TObject); private procedure PlotXY(XPoints, YPoints: DblDyneVec; const ARegressionResults: TBivariateRegressionResults); function PrepareData(ADataGrid: TStringGrid; out xCol, ycol: Integer; out XData, YData: DblDyneVec; out ColNoSelected: IntDyneVec): Boolean; procedure WriteToReport(ARegressionResults: TBivariateRegressionResults); 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 PlotXYForm: TPlotXYForm; implementation {$R *.lfm} uses TAChartUtils, TAChartAxisUtils, TALegend, TASources, TACustomSeries, TASeries, GridProcs, Utils; { TPlotXYForm } constructor TPlotXYForm.Create(AOwner: TComponent); begin inherited; FChartFrame.Chart.Legend.Alignment := laBottomCenter; FChartFrame.Chart.Legend.ColumnCount := 3; FChartFrame.Chart.Legend.TextFormat := tfHTML; with FChartFrame.Chart.AxisList.Add do begin Alignment := calRight; Marks.Source := TListChartSource.Create(self); Marks.Style := smsLabel; Grid.Visible := false; TickColor := clNone; end; with FChartFrame.Chart.AxisList.Add do begin Alignment := calTop; Marks.Source := TListChartSource.Create(self); Marks.Style := smsLabel; Grid.Visible := false; TickColor := clNone; end; PageControl.ActivePage := ChartPage; end; procedure TPlotXYForm.AdjustConstraints; begin ParamsPanel.Constraints.MinHeight := OptionsGroup.Top + OptionsGroup.Height + OptionsGroup.BorderSpacing.Bottom + ButtonBevel.Height + CloseBtn.Height + CloseBtn.BorderSpacing.Top; ParamsPanel.Constraints.MinWidth := OptionsGroup.Width * 2 - XInBtn.Width div 2 - XInBtn.BorderSpacing.Left; Constraints.MinHeight := ParamsPanel.Constraints.MinHeight + ParamsPanel.BorderSpacing.Around*2; Constraints.MinWidth := ParamsPanel.Constraints.MinWidth + 200; end; procedure TPlotXYForm.Compute; var xValues: DblDyneVec = nil; yValues: DblDyneVec = nil; ColNoSelected: IntDyneVec= nil; xCol, yCol: Integer; confBand: Double; regressionRes: TBivariateRegressionResults; C: TWinControl; msg: String; begin // Validation: Make sure that XEdit and YEdit are not empty if not Validate(msg, C) then begin C.SetFocus; ErrorMsg(msg); ModalResult := mrNone; exit; end; // Extract data from Grid if not PrepareData(OS3MainFrm.DataGrid, xCol, yCol, xValues, yValues, ColNoSelected) then exit; // Sort on x values SortOnX(xValues, yValues); // Calculate regression confBand := StrToFloat(ConfEdit.Text) / 100.0; Calc_BivariateRegression(xValues, yValues, confBand, regressionRes); // Print the descriptive statistics to the output frame WriteToReport(regressionRes); // Plot the values (and optional line and confidence band if elected) PlotXY(xValues, yValues, regressionRes); end; procedure TPlotXYForm.PlotXY(XPoints, YPoints: DblDyneVec; const ARegressionResults: TBivariateRegressionResults); var tmpX: DblDyneVec = nil; tmpY: DblDyneVec = nil; conf: DblDyneVec = nil; ext: TDoubleRect; xmin, xmax, ymin, ymax: Double; rightLabels: TListChartSource; topLabels: TListChartSource; ser: TChartSeries; i: Integer; begin rightLabels := FChartFrame.Chart.AxisList[2].Marks.Source as TListChartSource; rightLabels.Clear; topLabels := FChartFrame.Chart.AxisList[3].Marks.Source as TListChartSource; topLabels.Clear; FChartFrame.Clear; // Titles FChartFrame.SetTitle('X vs. Y plot using file ' + OS3MainFrm.FileNameEdit.Text); with ARegressionResults do FChartFrame.SetFooter(Format('R(X,Y) = %.3f, Slope = %.3f, Intercept = %.3f', [ R, Slope, Intercept ])); FChartFrame.SetXTitle(XEdit.Text); FChartFrame.SetYTitle(YEdit.Text); // Draw upper confidence band if ConfChk.Checked then begin SetLength(conf, ARegressionResults.Count); for i := 0 to High(conf) do conf[i] := ARegressionResults.ConfidenceLimits(XPoints[i], true); ser := FChartFrame.PlotXY(ptLines, XPoints, conf, nil, nil, 'Upper confidence band', clRed); rightLabels.Add(ser.yValue[ser.Count-1], ser.YValue[ser.Count-1], 'UCL'); end; // Plot data points FChartFrame.PlotXY(ptSymbols, XPoints, YPoints, nil, nil, 'Data values', clNavy); // Draw lower confidence band if ConfChk.Checked then begin SetLength(conf, ARegressionResults.Count); for i := 0 to High(conf) do conf[i] := ARegressionResults.ConfidenceLimits(XPoints[i], false); ser := FChartFrame.PlotXY(ptLines, XPoints, conf, nil, nil, 'Lower confidence band', clRed); rightLabels.Add(ser.yValue[ser.Count-1], ser.YValue[ser.Count-1], 'LCL'); end; // Draw means if MeansChk.Checked then with ARegressionResults do begin FChartFrame.VertLine(XMean, clGreen, psDashDot, 'Mean ' + XEdit.Text); topLabels.Add(XMean, XMean, 'Mean ' + XEdit.Text); FChartFrame.HorLine(YMean, clGreen, psDash, 'Mean ' + YEdit.Text); rightLabels.Add(YMean, YMean, 'Mean ' + YEdit.Text); end; // Draw regression line if LineChk.Checked then begin ext := FChartFrame.Chart.GetFullExtent; SetLength(tmpX, 2); SetLength(tmpY, 2); tmpX[0] := ext.a.x; tmpX[1] := ext.b.x; with ARegressionResults do begin tmpY[1] := tmpX[1] * Slope + Intercept; tmpY[0] := tmpX[0] * Slope + Intercept; end; ser := FChartFrame.PlotXY(ptLines, tmpX, tmpY, nil, nil, 'Predicted', clBlack); rightLabels.Add(tmpY[1], tmpY[1], 'Predicted'); end; FChartFrame.Chart.Legend.Visible := false; end; function TPlotXYForm.PrepareData(ADataGrid: TStringGrid; out xCol, ycol: Integer; out XData, YData: DblDyneVec; out ColNoSelected: IntDyneVec): Boolean; var N: Integer; begin Result := false; ColNoSelected := nil; XData := nil; YData := nil; xCol := GetVariableIndex(ADataGrid, XEdit.Text); yCol := GetVariableIndex(ADataGrid, YEdit.Text); if xCol = -1 then begin ErrorMsg('X variable not found.'); exit; end; if yCol = -1 then begin ErrorMsg('Y variable not found.'); exit; end; SetLength(ColNoSelected, 2); ColNoSelected[0] := xCol; ColNoSelected[1] := yCol; XData := CollectValues(ADataGrid, xCol, colNoSelected); YData := CollectValues(ADataGrid, ycol, colNoSelected); N := Length(XData); if N < 3 then begin ErrorMsg('At least three data points required.'); exit; end; if N <> Length(YData) then begin ErrorMsg('Equal count of cases required for x and y variables.'); exit; end; Result := true; end; procedure TPlotXYForm.Reset; var i: integer; begin inherited; XEdit.Text := ''; YEdit.Text := ''; ConfEdit.Text := FormatFloat('0.0', DEFAULT_CONFIDENCE_LEVEL_PERCENT); LineChk.Checked := false; MeansChk.Checked := false; ConfChk.Checked := false; VarList.Items.Clear; for i := 1 to NoVariables do VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); UpdateBtnStates; end; procedure TPlotXYForm.UpdateBtnStates; begin inherited; XInBtn.Enabled := (VarList.ItemIndex > -1) and (XEdit.Text = ''); XoutBtn.Enabled := (XEdit.Text <> ''); YinBtn.Enabled := (VarList.ItemIndex > -1) and (YEdit.Text = ''); YoutBtn.Enabled := (YEdit.Text <> ''); end; function TPlotXYForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; begin Result := false; if XEdit.Text = '' then begin AMsg := 'No variable selected for X.'; exit; end; if YEdit.Text = '' then begin AControl := YEdit; AMsg := 'No variable selected for Y.'; exit; end; Result := true; end; procedure TPlotXYForm.VarListDblClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if index > -1 then begin if XEdit.Text = '' then XEdit.Text := VarList.Items[index] else YEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TPlotXYForm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; procedure TPlotXYForm.WriteToReport(ARegressionResults: TBivariateRegressionResults); var lReport: TStrings; begin lReport := TStringList.Create; try lReport.Add('X vs. Y PLOT'); lReport.Add(''); lReport.Add('Data file: %s', [OS3MainFrm.FileNameEdit.Text]); lReport.Add(''); lReport.Add('Variables:'); lReport.Add(' X: %s', [XEdit.Text]); lReport.Add(' Y: %s', [YEdit.Text]); lReport.Add(''); lReport.Add('Variable Mean Variance Std.Dev.'); lReport.Add('---------- -------- -------- --------'); with ARegressionResults do begin lReport.Add('%-10s %8.2f %8.2f %8.2f', [XEdit.Text, XMean, XVariance, XStdDev]); lReport.Add('%-10s %8.2f %8.2f %8.2f', [YEdit.Text, 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; FReportFrame.DisplayReport(lReport); finally lReport.Free; end; end; procedure TPlotXYForm.XInBtnClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (XEdit.Text = '') then begin XEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TPlotXYForm.XOutBtnClick(Sender: TObject); begin if XEdit.Text <> '' then begin VarList.Items.Add(XEdit.Text); XEdit.Text := ''; UpdateBtnStates; end; end; procedure TPlotXYForm.YInBtnClick(Sender: TObject); var index: integer; begin index := VarList.ItemIndex; if (index > -1) and (YEdit.Text = '') then begin YEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TPlotXYForm.YOutBtnClick(Sender: TObject); begin if YEdit.Text <> '' then begin VarList.Items.Add(YEdit.Text); YEdit.Text := ''; UpdateBtnStates; end; end; end.