2020-03-30 18:01:44 +00:00
|
|
|
// Use file "Sickness.laz" for testing
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
// to do Not sure about the "red" slope line...
|
2020-10-10 11:02:59 +00:00
|
|
|
// to do: There are also two code positions where a negative intercept is made positive. Why? Correct?
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
unit ResistanceLineUnit;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2020-10-09 21:35:35 +00:00
|
|
|
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
|
2020-10-10 11:28:34 +00:00
|
|
|
StdCtrls, ExtCtrls, Buttons, Printers, ComCtrls, Grids,
|
2020-10-10 11:02:59 +00:00
|
|
|
MainUnit, Globals, DataProcs, DictionaryUnit,
|
2020-10-14 14:28:59 +00:00
|
|
|
MatrixUnit, RegressionUnit, BasicStatsReportAndChartFormUnit;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
{ TResistanceLineForm }
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
TResistanceLineForm = class(TBasicStatsReportAndChartForm)
|
|
|
|
OptionsBevel: TBevel;
|
2020-03-30 18:01:44 +00:00
|
|
|
GridChk: TCheckBox;
|
2020-10-09 21:35:35 +00:00
|
|
|
PlotMediansChk: TRadioButton;
|
2020-03-30 18:01:44 +00:00
|
|
|
ConfChk: TCheckBox;
|
|
|
|
ConfEdit: TEdit;
|
|
|
|
DescChk: TCheckBox;
|
2020-10-09 21:35:35 +00:00
|
|
|
OptionsGroup: TGroupBox;
|
|
|
|
ConfLabel: TLabel;
|
2020-03-30 18:01:44 +00:00
|
|
|
LineChk: TCheckBox;
|
2020-10-09 21:35:35 +00:00
|
|
|
PlotPointsChk: TRadioButton;
|
2020-03-30 18:01:44 +00:00
|
|
|
VarList: TListBox;
|
|
|
|
MeansChk: TCheckBox;
|
|
|
|
XInBtn: TBitBtn;
|
|
|
|
YInBtn: TBitBtn;
|
|
|
|
XOutBtn: TBitBtn;
|
|
|
|
YOutBtn: TBitBtn;
|
|
|
|
XEdit: TEdit;
|
|
|
|
YEdit: TEdit;
|
|
|
|
Label1: TLabel;
|
|
|
|
Label2: TLabel;
|
|
|
|
Label3: TLabel;
|
|
|
|
Label4: TLabel;
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure PlotMediansChkChange(Sender: TObject);
|
2020-03-30 18:01:44 +00:00
|
|
|
procedure StdCorChkChange(Sender: TObject);
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure VarListDblClick(Sender: TObject);
|
|
|
|
procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean);
|
2020-03-30 18:01:44 +00:00
|
|
|
procedure XInBtnClick(Sender: TObject);
|
|
|
|
procedure XOutBtnClick(Sender: TObject);
|
|
|
|
procedure YInBtnClick(Sender: TObject);
|
|
|
|
procedure YOutBtnClick(Sender: TObject);
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
private
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure PlotMedians(const XMedians, YMedians: DblDyneVec;
|
|
|
|
ASlope, AIntercept: Double);
|
2020-10-10 11:02:59 +00:00
|
|
|
procedure PlotXY(const XPoints, YPoints: DblDyneVec;
|
|
|
|
ARegressionResults: TBivariateRegressionResults);
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-10-10 11:28:34 +00:00
|
|
|
function PrepareData(ADataGrid: TStringGrid;
|
|
|
|
out xCol, ycol: Integer; out XData, YData: DblDyneVec;
|
|
|
|
out ColNoSelected: IntDyneVec): Boolean;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure ResidualsToGrid(xCol, yCol: Integer;
|
|
|
|
const AColNoSelected: IntDyneVec; Slope, Intercept: Double);
|
|
|
|
|
2020-10-10 11:28:34 +00:00
|
|
|
procedure ResistantLineAnalysis(const XData, YData: DblDyneVec;
|
2020-10-09 21:35:35 +00:00
|
|
|
out XMedians, yMedians: DblDyneVec; out GrpSize: IntDyneVec);
|
|
|
|
|
|
|
|
procedure WriteMedianReport(AReport: TStrings;
|
|
|
|
const XMedians, YMedians: DblDyneVec; const GrpSize: IntDyneVec;
|
|
|
|
Slope1, Slope2, Slope, Intercept: Double);
|
|
|
|
procedure WriteXYReport(AReport: TStrings; AFileName: String;
|
2020-10-10 11:02:59 +00:00
|
|
|
const ARegressionResults: TBivariateRegressionResults);
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
protected
|
|
|
|
procedure AdjustConstraints; override;
|
|
|
|
procedure Compute; override;
|
|
|
|
procedure UpdateBtnStates; override;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
public
|
2020-10-09 21:35:35 +00:00
|
|
|
constructor Create(AOwner: TComponent); override;
|
|
|
|
procedure Reset; override;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
ResistanceLineForm: TResistanceLineForm;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
{$R *.lfm}
|
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
uses
|
2020-10-10 11:28:34 +00:00
|
|
|
Math,
|
2020-10-09 21:35:35 +00:00
|
|
|
TATypes, TAChartUtils, TAChartAxisUtils, TALegend, TASources, TACustomSeries,
|
2020-10-10 11:02:59 +00:00
|
|
|
ChartFrameUnit, Utils, GridProcs;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
{ TResistanceLineForm }
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
constructor TResistanceLineForm.Create(AOwner: TComponent);
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-10-09 21:35:35 +00:00
|
|
|
inherited;
|
|
|
|
if DictionaryFrm = nil then Application.CreateForm(TDictionaryFrm, DictionaryFrm);
|
|
|
|
|
|
|
|
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;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
procedure TResistanceLineForm.AdjustConstraints;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-10-09 21:35:35 +00:00
|
|
|
ParamsPanel.Constraints.MinHeight := YOutBtn.Top + YOutBtn.Height +
|
|
|
|
VarList.BorderSpacing.Bottom + OptionsGroup.Height + ButtonBevel.Height +
|
|
|
|
CloseBtn.Height;
|
|
|
|
|
|
|
|
ParamsPanel.Constraints.MinWidth := Max(
|
|
|
|
4*CloseBtn.Width + 3*CloseBtn.BorderSpacing.Left,
|
|
|
|
OptionsGroup.Width);
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
procedure TResistanceLineForm.Compute;
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-10-10 11:28:34 +00:00
|
|
|
xValues: DblDyneVec = nil;
|
|
|
|
yValues: DblDyneVec = nil;
|
2020-10-09 21:35:35 +00:00
|
|
|
xMedians: DblDyneVec = nil;
|
|
|
|
yMedians: DblDyneVec = nil;
|
|
|
|
colNoSelected : IntDyneVec = nil;
|
|
|
|
grpSize: IntDyneVec = nil;
|
|
|
|
xCol, yCol: Integer;
|
2020-10-10 11:02:59 +00:00
|
|
|
regressionRes: TBivariateRegressionResults;
|
|
|
|
confBand: Double;
|
|
|
|
slope1, slope2, slopeRL: Double;
|
|
|
|
c1, c2, c3, c: Double;
|
2020-03-30 18:01:44 +00:00
|
|
|
lReport: TStrings;
|
2020-10-09 21:35:35 +00:00
|
|
|
dataGrid: TStringGrid;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-10-09 21:35:35 +00:00
|
|
|
dataGrid := OS3MainFrm.DataGrid;
|
|
|
|
|
|
|
|
// Get values from the grid
|
2020-10-10 11:28:34 +00:00
|
|
|
if not PrepareData(dataGrid, xCol, yCol, xValues, yvalues, colNoSelected) then
|
2020-10-09 21:35:35 +00:00
|
|
|
exit;
|
|
|
|
|
|
|
|
// Sort on the x values
|
2020-10-10 11:28:34 +00:00
|
|
|
SortOnX(xValues, yValues);
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-10-10 11:02:59 +00:00
|
|
|
// Calculate bivariate regression
|
|
|
|
confBand := StrToFloat(ConfEdit.Text) / 100;
|
2020-10-14 14:28:59 +00:00
|
|
|
BivariateRegression(xValues, yValues, confBand, regressionRes);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
// Do the resistant line analysis
|
2020-10-10 11:28:34 +00:00
|
|
|
ResistantLineAnalysis(xValues, yValues, xMedians, yMedians, grpSize);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
slope1 := (yMedians[1] - yMedians[0]) / (xMedians[1] - xMedians[0]);
|
|
|
|
slope2 := (yMedians[2] - yMedians[1]) / (xMedians[2] - xMedians[1]);
|
|
|
|
slopeRL := (yMedians[2] - yMedians[0]) / (xMedians[2] - xMedians[0]);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
// obtain estimate of the constant for the prediction equation
|
|
|
|
c1 := slopeRL * xMedians[0] - yMedians[0];
|
|
|
|
c2 := slopeRL * xMedians[1] - yMedians[1];
|
|
|
|
c3 := slopeRL * xMedians[2] - yMedians[2];
|
|
|
|
c := (c1 + c2 + c3) / 3.0;
|
|
|
|
if c < 0 then c := -c; // wp: why is this necessary?
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
if GridChk.Checked then
|
|
|
|
ResidualsToGrid(xCol, yCol, colNoSelected, slopeRL, c);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-10 11:02:59 +00:00
|
|
|
// Write results to report in ReportFrame
|
2020-03-30 18:01:44 +00:00
|
|
|
lReport := TStringList.Create;
|
|
|
|
try
|
2020-10-09 21:35:35 +00:00
|
|
|
WriteMedianReport(lReport,
|
|
|
|
xMedians, yMedians, grpSize,
|
|
|
|
slope1, slope2, slopeRL, c
|
|
|
|
);
|
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
if DescChk.Checked then
|
2020-10-09 21:35:35 +00:00
|
|
|
WriteXYReport(lReport,
|
|
|
|
OS3MainFrm.FileNameEdit.Text,
|
2020-10-10 11:02:59 +00:00
|
|
|
regressionRes
|
2020-10-09 21:35:35 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
FReportFrame.DisplayReport(lReport);
|
|
|
|
finally
|
|
|
|
lReport.Free;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-10 11:02:59 +00:00
|
|
|
// Plot results in ChartFrame
|
2020-10-09 21:35:35 +00:00
|
|
|
if PlotPointsChk.Checked then
|
2020-10-10 11:28:34 +00:00
|
|
|
PlotXY(xValues, yValues, regressionRes);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
if PlotMediansChk.Checked then
|
2020-10-10 11:28:34 +00:00
|
|
|
PlotMedians(xMedians, yMedians, slopeRL, c);
|
2020-10-09 21:35:35 +00:00
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure TResistanceLineForm.PlotMedians(const XMedians, YMedians: DblDyneVec;
|
|
|
|
Aslope, AIntercept: Double);
|
|
|
|
var
|
|
|
|
ser: TChartSeries;
|
|
|
|
rightLabels: TListChartSource;
|
|
|
|
topLabels: TListChartSource;
|
|
|
|
labels: StrDyneVec = nil;
|
|
|
|
xpts: DblDyneVec = nil;
|
|
|
|
ypts: DblDyneVec = nil;
|
|
|
|
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;
|
|
|
|
FChartFrame.Clear;
|
|
|
|
|
|
|
|
FChartFrame.Chart.Margins.Left := 20;
|
|
|
|
FChartFrame.Chart.Margins.Right := 20;
|
|
|
|
|
|
|
|
FChartFrame.SetTitle('Median plot for three groups');
|
|
|
|
FChartFrame.SetFooter(Format('Slope = %.3f', [ASlope]));
|
|
|
|
FChartFrame.SetXTitle(XEdit.Text);
|
|
|
|
FChartFrame.SetYTitle(YEdit.Text);
|
|
|
|
|
|
|
|
SetLength(labels, Length(XMedians));
|
|
|
|
for i := 0 to High(XMedians) do
|
|
|
|
labels[i] := Format('M%d', [i+1]);
|
|
|
|
ser := FChartFrame.PlotXY(ptLinesAndSymbols, XMedians, YMedians, labels, nil, 'Medians', clNavy, psCircle);
|
|
|
|
ser.Marks.Style := smsLabel;
|
|
|
|
labels := nil;
|
|
|
|
|
|
|
|
// Draw slope line
|
|
|
|
SetLength(xpts, 2);
|
|
|
|
SetLength(ypts, 2);
|
|
|
|
xpts[0] := XMedians[0];
|
|
|
|
ypts[0] := AIntercept + ASlope * xpts[0];
|
|
|
|
xpts[1] := XMedians[2];
|
|
|
|
ypts[1] := AIntercept + ASlope * xpts[1];
|
|
|
|
FChartFrame.PlotXY(ptLines, xpts, ypts, nil, nil, 'Slope line', clRed);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-10-10 11:02:59 +00:00
|
|
|
procedure TResistanceLineForm.PlotXY(const XPoints, YPoints: DblDyneVec;
|
|
|
|
ARegressionResults: TBivariateRegressionResults);
|
2020-10-09 21:35:35 +00:00
|
|
|
var
|
|
|
|
ser: TChartSeries;
|
|
|
|
xPts: DblDyneVec = nil;
|
|
|
|
yPts: DblDyneVec = nil;
|
2020-10-10 11:02:59 +00:00
|
|
|
conf: DblDyneVec = nil;
|
2020-10-09 21:35:35 +00:00
|
|
|
rightLabels: TListChartSource;
|
|
|
|
topLabels: TListChartSource;
|
2020-10-10 11:02:59 +00:00
|
|
|
i: Integer;
|
2020-10-09 21:35:35 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
FChartFrame.Chart.Margins.Left := 4;
|
|
|
|
FChartFrame.Chart.Margins.Right := 4;
|
|
|
|
|
|
|
|
FChartFrame.SetTitle('X vs Y plot using file '+ OS3MainFrm.FileNameEdit.Text);
|
2020-10-10 11:02:59 +00:00
|
|
|
with ARegressionResults do
|
|
|
|
FChartFrame.SetFooter(Format('R(X,Y) = %.3f, Slope = %.2f, Intercept = %.2f', [R, Slope, Intercept]));
|
2020-10-09 21:35:35 +00:00
|
|
|
FChartFrame.SetXTitle(XEdit.Text);
|
|
|
|
FChartFrame.SetYTitle(YEdit.Text);
|
|
|
|
|
|
|
|
// Draw means
|
|
|
|
if MeansChk.Checked then
|
2020-10-10 11:02:59 +00:00
|
|
|
with ARegressionResults do
|
|
|
|
begin
|
|
|
|
FChartFrame.VertLine(XMean, clGreen, psDashDot, 'Mean ' + XEdit.Text);
|
|
|
|
topLabels.Add(XMean, XMean, 'Mean ' + XEdit.Text);
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-10-10 11:02:59 +00:00
|
|
|
FChartFrame.HorLine(YMean, clGreen, psDash, 'Mean ' + YEdit.Text);
|
|
|
|
rightLabels.Add(YMean, YMean, 'Mean ' + YEdit.Text);
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
// Draw slope line
|
|
|
|
if LineChk.Checked then
|
|
|
|
begin
|
|
|
|
SetLength(xPts, 2);
|
|
|
|
SetLength(yPts, 2);
|
|
|
|
xPts[0] := XPoints[0];
|
|
|
|
xPts[1] := XPoints[High(XPoints)];
|
2020-10-10 11:02:59 +00:00
|
|
|
with ARegressionResults do
|
|
|
|
begin
|
|
|
|
yPts[0] := Intercept + XPoints[0] * Slope;
|
|
|
|
yPts[1] := Intercept + XPoints[High(XPoints)] * Slope;
|
|
|
|
end;
|
2020-10-09 21:35:35 +00:00
|
|
|
ser := FChartFrame.PlotXY(ptLines, xpts, ypts, nil, nil, 'Regression', clBlack);
|
|
|
|
rightLabels.Add(ser.XValue[ser.Count-1], ser.YValue[ser.Count-1], 'Regression');
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
if ConfChk.Checked then
|
|
|
|
begin
|
2020-10-10 11:02:59 +00:00
|
|
|
// Draw upper confidence band
|
|
|
|
SetLength(conf, ARegressionResults.Count);
|
|
|
|
for i := 0 to ARegressionResults.Count-1 do
|
|
|
|
conf[i] := ARegressionResults.ConfidenceLimits(XPoints[i], true);
|
|
|
|
ser := FChartFrame.PlotXY(ptLines, XPoints, conf, nil, nil, 'Upper confidence band', clRed);
|
2020-10-09 21:35:35 +00:00
|
|
|
rightLabels.Add(ser.yValue[ser.Count-1], ser.YValue[ser.Count-1], 'UCL');
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-10 11:02:59 +00:00
|
|
|
// Draw lower confidence band
|
|
|
|
for i := 0 to ARegressionResults.Count-1 do
|
|
|
|
conf[i] := ARegressionResults.ConfidenceLimits(XPoints[i], false);
|
|
|
|
ser := FChartFrame.PlotXY(ptLines, XPoints, conf, nil, nil, 'Lower confidence band', clRed);
|
2020-10-09 21:35:35 +00:00
|
|
|
rightLabels.Add(ser.yValue[ser.Count-1], ser.YValue[ser.Count-1], 'LCL');
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
// Draw points for x and y pairs
|
|
|
|
ser := FChartFrame.PlotXY(ptSymbols, XPoints, YPoints, nil, nil, 'Data', clYellow, psCircle);
|
|
|
|
ser.Marks.Style := smsNone;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
|
2020-10-10 11:28:34 +00:00
|
|
|
function TResistanceLineForm.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 specified or not found.');
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if yCol = -1 then
|
|
|
|
begin
|
|
|
|
ErrorMsg('Y variable not specified or not found.');
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
SetLength(ColNoSelected, 2);
|
|
|
|
ColNoSelected[0] := xCol;
|
|
|
|
ColNoSelected[1] := yCol;
|
|
|
|
|
2020-10-17 22:42:41 +00:00
|
|
|
XData := CollectVecValues(ADataGrid, xCol, colNoSelected);
|
|
|
|
YData := CollectVecValues(ADataGrid, ycol, colNoSelected);
|
2020-10-10 11:28:34 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure TResistanceLineForm.Reset;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
inherited;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
PlotMediansChk.Checked := true;
|
|
|
|
PlotMediansChkChange(PlotMediansChk);
|
|
|
|
GridChk.Checked := false;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
XEdit.Text := '';
|
|
|
|
YEdit.Text := '';
|
|
|
|
VarList.Clear;
|
|
|
|
ConfEdit.Text := FormatFloat('0.0', DEFAULT_CONFIDENCE_LEVEL_PERCENT);
|
|
|
|
for i := 1 to NoVariables do
|
|
|
|
VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]);
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ Get the residuals (Y - predicted Y) for each X value and place them in the grid }
|
|
|
|
procedure TResistanceLineForm.ResidualsToGrid(xCol, yCol: Integer;
|
|
|
|
const AColNoSelected: IntDyneVec; Slope, Intercept: Double);
|
|
|
|
var
|
|
|
|
s: String;
|
|
|
|
i: Integer;
|
|
|
|
x, y, yPred, yRes: Double;
|
|
|
|
begin
|
|
|
|
// New column for predicted value
|
|
|
|
s := 'Pred.' + OS3MainFrm.DataGrid.Cells[yCol, 0];
|
|
|
|
DictionaryFrm.NewVar(NoVariables+1);
|
|
|
|
DictionaryFrm.DictGrid.Cells[1, NoVariables] := s;
|
|
|
|
OS3MainFrm.DataGrid.Cells[NoVariables, 0] := s;
|
|
|
|
|
|
|
|
// New column for residual value
|
|
|
|
s := 'Residual';
|
|
|
|
DictionaryFrm.NewVar(NoVariables+1);
|
|
|
|
DictionaryFrm.DictGrid.Cells[1, NoVariables] := s;
|
|
|
|
OS3MainFrm.DataGrid.Cells[NoVariables, 0] := s;
|
|
|
|
|
|
|
|
for i := 1 to NoCases do
|
|
|
|
begin
|
|
|
|
if not DataProcs.GoodRecord(i, Length(AColNoSelected), AColNoSelected) then continue;
|
|
|
|
x := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[xCol, i]));
|
|
|
|
y := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[yCol, i]));
|
|
|
|
|
|
|
|
// Calculate predicted value
|
|
|
|
if Intercept >= 0 then
|
|
|
|
yPred := Slope * x + Intercept // wp: why two equations?
|
|
|
|
else
|
|
|
|
yPred := Slope * x - Intercept;
|
|
|
|
|
|
|
|
// Calculate residual
|
|
|
|
yRes := y - yPred;
|
|
|
|
|
|
|
|
// Write to grid
|
|
|
|
OS3MainFrm.DataGrid.Cells[NoVariables-1, i] := Format('%.3f', [yPred]);
|
|
|
|
OS3MainFrm.DataGrid.Cells[NoVariables, i] := Format('%.3f', [yRes]);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Do the resistant line analysis }
|
2020-10-10 11:28:34 +00:00
|
|
|
procedure TResistanceLineForm.ResistantLineAnalysis(const XData, YData: DblDyneVec;
|
2020-10-09 21:35:35 +00:00
|
|
|
out XMedians, yMedians: DblDyneVec; out GrpSize: IntDyneVec);
|
|
|
|
var
|
|
|
|
i, N, offs: Integer;
|
|
|
|
xVector: DblDyneVec = nil;
|
|
|
|
yVector: DblDyneVec = nil;
|
|
|
|
begin
|
2020-10-10 11:28:34 +00:00
|
|
|
N := Length(XData);
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
GrpSize := nil;
|
|
|
|
XMedians := nil;
|
|
|
|
YMedians := nil;
|
|
|
|
|
|
|
|
SetLength(GrpSize, 3);
|
|
|
|
SetLength(XMedians, 3);
|
|
|
|
SetLength(YMedians, 3);
|
|
|
|
|
|
|
|
GrpSize[0] := N div 3;
|
|
|
|
GrpSize[2] := GrpSize[0];
|
|
|
|
GrpSize[1] := N - GrpSize[0] - GrpSize[2];
|
|
|
|
|
|
|
|
// Sort the y values. The x values already are sorted.
|
|
|
|
//SortOnX(xyPoints[1]);
|
|
|
|
|
|
|
|
// Get median for each group of x and y values
|
|
|
|
|
|
|
|
// First group
|
|
|
|
SetLength(xVector, GrpSize[0]);
|
|
|
|
SetLength(yVector, GrpSize[0]);
|
|
|
|
offs := 0;
|
|
|
|
for i := 0 to GrpSize[1] - 1 do
|
|
|
|
begin
|
2020-10-10 11:28:34 +00:00
|
|
|
xVector[i] := XData[i + offs];
|
|
|
|
yVector[i] := YData[i + offs];
|
2020-10-09 21:35:35 +00:00
|
|
|
end;
|
2020-10-14 14:28:59 +00:00
|
|
|
xMedians[0] := VecMedian(xVector);
|
|
|
|
yMedians[0] := VecMedian(yVector);
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
// Second group
|
|
|
|
SetLength(xVector, GrpSize[1]);
|
|
|
|
SetLength(yVector, GrpSize[1]);
|
|
|
|
offs := GrpSize[0];
|
|
|
|
for i := 0 to GrpSize[1] - 1 do
|
|
|
|
begin
|
2020-10-10 11:28:34 +00:00
|
|
|
xVector[i] := XData[i + offs];
|
|
|
|
yVector[i] := YData[i + offs];
|
2020-10-09 21:35:35 +00:00
|
|
|
end;
|
2020-10-14 14:28:59 +00:00
|
|
|
xMedians[1] := VecMedian(xVector);
|
|
|
|
yMedians[1] := VecMedian(yVector);
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
// Third group
|
|
|
|
SetLength(xVector, GrpSize[2]);
|
|
|
|
SetLength(yVector, GrpSize[2]);
|
|
|
|
offs := GrpSize[0] + GrpSize[1];
|
|
|
|
for i := 0 to GrpSize[2] - 1 do
|
|
|
|
begin
|
2020-10-10 11:28:34 +00:00
|
|
|
xVector[i] := XData[i + offs];
|
|
|
|
yVector[i] := YData[i + offs];
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
2020-10-14 14:28:59 +00:00
|
|
|
xMedians[2] := VecMedian(xVector);
|
|
|
|
yMedians[2] := VecMedian(yVector);
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
procedure TResistanceLineForm.StdCorChkChange(Sender: TObject);
|
2020-09-29 10:36:56 +00:00
|
|
|
begin
|
2020-10-09 21:35:35 +00:00
|
|
|
//if StdCorChk.Checked then OptionsGroup.Enabled := true else OptionsGroup.Enabled := false;
|
2020-09-29 10:36:56 +00:00
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure TResistanceLineForm.PlotMediansChkChange(Sender: TObject);
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-10-09 21:35:35 +00:00
|
|
|
enable: Boolean;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-10-09 21:35:35 +00:00
|
|
|
enable := ((Sender = PlotPointsChk) and PlotPointsChk.Checked) or
|
|
|
|
((Sender = PlotMediansChk) and (not PlotMediansChk.Checked));
|
|
|
|
LineChk.Enabled := enable;
|
|
|
|
MeansChk.Enabled := enable;
|
|
|
|
ConfChk.Enabled := enable;
|
|
|
|
ConfLabel.Enabled := enable;
|
|
|
|
ConfEdit.Enabled := enable;
|
|
|
|
GridChk.Enabled := enable;
|
|
|
|
DescChk.Enabled := enable;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure TResistanceLineForm.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 if YEdit.Text = '' then
|
|
|
|
YEdit.Text := VarList.Items[index];
|
|
|
|
VarList.Items.Delete(index);
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TResistanceLineForm.UpdateBtnStates;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
|
|
|
|
XInBtn.Enabled := (VarList.ItemIndex > -1) and (XEdit.Text = '');
|
|
|
|
YInBtn.Enabled := (VarList.ItemIndex > -1) and (YEdit.Text = '');
|
|
|
|
XOutBtn.Enabled := (XEdit.Text <> '');
|
|
|
|
YOutBtn.Enabled := (YEdit.Text <> '');
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
procedure TResistanceLineForm.VarListSelectionChange(Sender: TObject;
|
|
|
|
User: boolean);
|
|
|
|
begin
|
|
|
|
UpdateBtnStates;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
procedure TResistanceLineForm.WriteMedianReport(AReport: TStrings;
|
|
|
|
const XMedians, YMedians: DblDyneVec; const GrpSize: IntDyneVec;
|
|
|
|
Slope1, Slope2, Slope, Intercept: Double);
|
|
|
|
var
|
|
|
|
i: Integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-10-09 21:35:35 +00:00
|
|
|
AReport.Add('RESISTANT LINE ANALYSIS');
|
|
|
|
AReport.Add('');
|
|
|
|
AReport.Add('Group X Median Y Median Size');
|
|
|
|
AReport.Add('----- -------- -------- -----');
|
|
|
|
for i := 0 to 2 do
|
|
|
|
AReport.Add('%3d %8.3f %8.3f %3d', [i+1, XMedians[i], YMedians[i], GrpSize[i]]);
|
|
|
|
AReport.Add('');
|
|
|
|
|
|
|
|
AReport.Add('Half Slopes: %10.3f and %.3f', [Slope1, Slope2]);
|
|
|
|
AReport.Add('Slope: %10.3f', [Slope]);
|
|
|
|
aReport.Add('Ratio of half slopes: %10.3f', [Slope2 / Slope1]);
|
|
|
|
|
|
|
|
// Prediction equation
|
|
|
|
AReport.Add('Equation: y = %.3f * X + %.3f', [Slope, Intercept]);
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
|
|
|
procedure TResistanceLineForm.WriteXYReport(AReport: TStrings; AFileName: String;
|
2020-10-10 11:02:59 +00:00
|
|
|
const ARegressionResults: TBivariateRegressionResults);
|
|
|
|
{
|
2020-10-09 21:35:35 +00:00
|
|
|
xMean, yMean, xVariance, yVariance, xStdDev, yStdDev, R, slope, intercept, SEPred: double;
|
|
|
|
N: Integer);
|
2020-10-10 11:02:59 +00:00
|
|
|
}
|
2020-10-09 21:35:35 +00:00
|
|
|
begin
|
|
|
|
AReport.Add('');
|
|
|
|
AReport.Add(DIVIDER);
|
|
|
|
AReport.Add('');
|
|
|
|
AReport.Add('ORIGINAL X vs. Y PLOT');
|
|
|
|
AReport.Add('');
|
|
|
|
AReport.Add('Data file: ' + AFileName);
|
|
|
|
AReport.Add('Variables:');
|
|
|
|
AReport.Add(' X: ' + XEdit.Text);
|
|
|
|
AReport.Add(' Y: ' + YEdit.Text);
|
|
|
|
AReport.Add('');
|
|
|
|
AReport.Add('Variable Mean Variance Std.Dev.');
|
|
|
|
AReport.Add('---------- -------- -------- --------');
|
2020-10-10 11:02:59 +00:00
|
|
|
with ARegressionResults do
|
|
|
|
begin
|
|
|
|
AReport.Add('%-10s %8.2f %8.2f %8.2f', [XEdit.Text, XMean, XVariance, XStdDev]);
|
|
|
|
AReport.Add('%-10s %8.2f %8.2f %8.2f', [YEdit.Text, YMean, YVariance, YStdDev]);
|
|
|
|
AReport.Add('');
|
|
|
|
AReport.Add('Correlation: %8.4f', [R]);
|
|
|
|
AReport.Add('Slope: %8.3f', [Slope]);
|
|
|
|
AReport.Add('Intercept: %8.3f', [Intercept]);
|
|
|
|
AReport.Add('Standard Error of Estimate: %8.3f', [StdErrorPredicted]);
|
|
|
|
AReport.Add('Number of good cases: %8d', [Count]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{
|
2020-10-09 21:35:35 +00:00
|
|
|
AReport.Add('%-10s %8.2f %8.2f %8.2f', [XEdit.Text, xMean, xVariance, xStdDev]);
|
|
|
|
AReport.Add('%-10s %8.2f %8.2f %8.2f', [YEdit.Text, yMean, yVariance, yStdDev]);
|
|
|
|
AReport.Add('');
|
|
|
|
AReport.Add('Correlation: %8.4f', [R]);
|
|
|
|
AReport.Add('Slope: %8.3f', [Slope]);
|
|
|
|
AReport.Add('Intercept: %8.3f', [Intercept]);
|
|
|
|
AReport.Add('Standard Error of Estimate: %8.3f', [SEPred]);
|
|
|
|
AReport.Add('Number of good cases: %8d', [N]);
|
2020-10-10 11:02:59 +00:00
|
|
|
}
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
procedure TResistanceLineForm.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);
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
procedure TResistanceLineForm.XOutBtnClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if XEdit.Text <> '' then
|
|
|
|
begin
|
|
|
|
VarList.Items.Add(XEdit.Text);
|
|
|
|
XEdit.Text := '';
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
procedure TResistanceLineForm.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);
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
2020-10-09 21:35:35 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
procedure TResistanceLineForm.YOutBtnClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if YEdit.Text <> '' then
|
|
|
|
begin
|
|
|
|
VarList.Items.Add(YEdit.Text);
|
|
|
|
YEdit.Text := '';
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
end.
|
|
|
|
|