// Use file "cansas.laz" for testing unit PlotXYUnit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Buttons, ComCtrls, MainUnit, Globals, FunctionsLib, BasicStatsReportAndChartFormUnit, ReportFrameUnit, ChartFrameUnit; type { TPlotXYFrm } TPlotXYFrm = 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 { private declarations } procedure PlotXY(XPoints, YPoints, UpConf, LowConf: DblDyneVec; XMean, YMean, R, Slope, Intercept: Double); protected procedure AdjustConstraints; override; procedure Compute; override; procedure UpdateBtnStates; override; function Validate(out AMsg: String; out AControl: TWinControl; Xcol,Ycol: Integer): Boolean; reintroduce; public { public declarations } constructor Create(AOwner: TComponent); override; procedure Reset; override; end; var PlotXYFrm: TPlotXYFrm; implementation {$R *.lfm} uses TAChartUtils, TAChartAxisUtils, TALegend, TASources, TACustomSeries, TASeries, MathUnit, GridProcs, Utils; { TPlotXYFrm } constructor TPlotXYFrm.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; end; with FChartFrame.Chart.AxisList.Add do begin Alignment := calTop; Marks.Source := TListChartSource.Create(self); Marks.Style := smsLabel; Grid.Visible := false; end; PageControl.ActivePage := ChartPage; end; procedure TPlotXYFrm.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 TPlotXYFrm.Compute; var xMean, yMean, xVariance, yVariance, xStddev, yStddev: double; SXX, SXY, SYY, R, slope, intercept, t, confBand: Double; sePred, predicted, sedata: double; i, xCol, yCol, N, DF: integer; xValues: DblDyneVec = nil; yValues: DblDyneVec = nil; UpConf: DblDyneVec = nil; lowConf: DblDyneVec = nil; ColNoSelected: IntDyneVec= nil; C: TWinControl; msg: String; lReport: TStrings; begin xCol := OS3MainFrm.DataGrid.Rows[0].IndexOf(XEdit.Text); yCol := OS3MainFrm.DataGrid.Rows[0].IndexOf(YEdit.Text); // Validation if not Validate(msg, C, Xcol, Ycol) then begin C.SetFocus; ErrorMsg(msg); ModalResult := mrNone; exit; end; SetLength(ColNoSelected, 2); ColNoSelected[0] := Xcol; ColNoSelected[1] := Ycol; xValues := CollectValues(OS3MainFrm.DataGrid, xCol, ColNoSelected); yValues := CollectValues(OS3MainFrm.DataGrid, yCol, ColNoSelected); if (Length(yValues) = 0) then begin ErrorMsg('No y data'); exit; end; if (Length(xValues) <> Length(yValues)) then begin ErrorMsg('Different count of x and y values.'); exit; end; N := Length(yValues); Calc_MeanVarStddevSS(xValues, xMean, xVariance, xStdDev, SXX); Calc_MeanVarStddevSS(yValues, yMean, yVariance, yStdDev, SYY); SXY := 0; for i := 0 to N-1 do SXY := SXY + xValues[i] * yValues[i]; R := (SXY - xMean * yMean * N) / ((N - 1) * xStdDev * yStdDev); sePred := sqrt(1.0 - sqr(R)) * yStdDev * sqrt((N - 1) / (N - 2)); slope := R * yStdDev / xStdDev; intercept := yMean - slope * xMean; // Print the descriptive statistics to the output frame 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('---------- -------- -------- --------'); 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', [sePred]); lReport.Add(' Number of good cases: %8d', [N]); FReportFrame.DisplayReport(lReport); finally lReport.Free; end; // Get upper and lower confidence points for each X value if ConfChk.Checked then begin SortOnX(xValues, yValues); SetLength(UpConf, N); SetLength(lowConf, N); confBand := StrToFloat(ConfEdit.Text) / 100.0; DF := N - 2; t := InverseT(confBand, DF); for i := 0 to N-1 do begin predicted := slope * xValues[i] + intercept; seData := sePred * sqrt(1.0 + 1/N + sqr(xValues[i] - XMean) / SXX); upConf[i] := predicted + t * seData; lowConf[i] := predicted - t * seData; end; end else confBand := 0.0; // Plot the values (and optional line and confidence band if elected) PlotXY(xValues, yValues, upConf, lowConf, xMean, yMean, R, slope, intercept); end; procedure TPlotXYFrm.PlotXY(XPoints, YPoints, UpConf, LowConf: DblDyneVec; XMean, YMean, R, Slope, Intercept: Double); var tmpX: array of Double = nil; tmpY: array of Double = nil; xmin, xmax, ymin, ymax: Double; rightLabels: TListChartSource; topLabels: TListChartSource; ser: TChartSeries; 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); 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 ser := FChartFrame.PlotXY(ptLines, XPoints, UpConf, 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 ser := FChartFrame.PlotXY(ptLines, XPoints, LowConf, nil, nil, 'Lower confidence band', clRed); rightLabels.Add(ser.yValue[ser.Count-1], ser.YValue[ser.Count-1], 'LCL'); end; FChartFrame.Chart.Prepare; FChartFrame.GetXRange(xmin, xmax, false); FChartFrame.GetYRange(ymin, ymax, false); // Draw means if MeansChk.Checked then 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 SetLength(tmpX, 2); SetLength(tmpY, 2); tmpX[0] := xmin; tmpY[0] := tmpX[0] * slope + intercept; tmpX[1] := xmax; tmpY[1] := tmpX[1] * slope + intercept; ser := FChartFrame.PlotXY(ptLines, tmpX, tmpY, nil, nil, 'Predicted', clBlack); rightLabels.Add(tmpY[1], tmpY[1], 'Predicted'); end; FChartFrame.Chart.Legend.Visible := false; end; procedure TPlotXYfrm.Reset; var i: integer; begin 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]); if Assigned(FChartFrame) then FChartFrame.Clear; if Assigned(FReportFrame) then FReportFrame.Clear; UpdateBtnStates; end; procedure TPlotXYFrm.UpdateBtnStates; begin XInBtn.Enabled := (VarList.ItemIndex > -1) and (XEdit.Text = ''); XoutBtn.Enabled := (XEdit.Text <> ''); YinBtn.Enabled := (VarList.ItemIndex > -1) and (YEdit.Text = ''); YoutBtn.Enabled := (YEdit.Text <> ''); if Assigned(FReportFrame) then FReportFrame.UpdateBtnStates; if Assigned(FChartFrame) then FChartFrame.UpdateBtnStates; end; function TPlotXYFrm.Validate(out AMsg: String; out AControl: TWinControl; Xcol, Ycol: Integer): Boolean; begin Result := false; if (Xcol < 0) then begin AControl := XEdit; AMsg := 'No case selected for X.'; exit; end; if (Ycol < 0) then begin AControl := YEdit; AMsg := 'No case selected for Y.'; exit; end; Result := true; end; procedure TPlotXYFrm.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 TPlotXYFrm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; procedure TPlotXYFrm.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 TPlotXYFrm.XOutBtnClick(Sender: TObject); begin if XEdit.Text <> '' then begin VarList.Items.Add(XEdit.Text); XEdit.Text := ''; UpdateBtnStates; end; end; procedure TPlotXYFrm.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 TPlotXYFrm.YOutBtnClick(Sender: TObject); begin if YEdit.Text <> '' then begin VarList.Items.Add(YEdit.Text); YEdit.Text := ''; UpdateBtnStates; end; end; end.