LazStats: Inherit form in CorSimUnit from TBasicStatsReportAndChartForm. Use TAChart.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7784 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-10-19 22:03:10 +00:00
parent ad2b13edcf
commit 272f8ced87
5 changed files with 644 additions and 550 deletions

View File

@ -809,7 +809,7 @@
<Unit88>
<Filename Value="forms\simulations\corsimunit.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="CorSimFrm"/>
<ComponentName Value="CorSimForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="CorSimUnit"/>

View File

@ -1373,9 +1373,9 @@ end;
// Menu "Simulations" > "Bivariate Scatter Plot"
procedure TOS3MainFrm.mnuSimBivarScatterPlotClick(Sender: TObject);
begin
if CorSimFrm = nil then
Application.CreateForm(TCorSimFrm, CorSimFrm);
CorSimFrm.ShowModal;
if CorSimForm = nil then
Application.CreateForm(TCorSimForm, CorSimForm);
CorSimForm.Show;
end;
// Menu "Simulations" > "Chisquare Probability"

View File

@ -1,4 +1,4 @@
object CorSimFrm: TCorSimFrm
inherited CorSimForm: TCorSimForm
Left = 542
Height = 447
Top = 126
@ -8,223 +8,263 @@ object CorSimFrm: TCorSimFrm
Caption = 'Correlation Simulation'
ClientHeight = 447
ClientWidth = 857
OnShow = FormShow
Position = poMainFormCenter
LCLVersion = '2.1.0.0'
object Image1: TImage
Left = 8
Height = 389
Top = 8
Width = 841
Align = alClient
BorderSpacing.Left = 8
BorderSpacing.Top = 8
BorderSpacing.Right = 8
end
object Panel1: TPanel
AnchorSideTop.Side = asrCenter
Left = 8
Height = 26
Top = 413
Width = 841
Align = alBottom
AutoSize = True
BorderSpacing.Around = 8
BevelOuter = bvNone
ClientHeight = 26
ClientWidth = 841
TabOrder = 0
object Label1: TLabel
AnchorSideLeft.Control = Panel1
AnchorSideTop.Control = MeanX
inherited ParamsPanel: TPanel
Height = 431
Width = 267
ClientHeight = 431
ClientWidth = 267
inherited CloseBtn: TButton
Left = 212
Top = 406
TabOrder = 10
end
inherited ComputeBtn: TButton
Left = 128
Top = 406
TabOrder = 9
end
inherited ResetBtn: TButton
Left = 66
Top = 406
TabOrder = 8
end
inherited HelpBtn: TButton
Left = 7
Top = 406
TabOrder = 7
Visible = False
end
inherited ButtonBevel: TBevel
Top = 390
Width = 267
end
object Label1: TLabel[5]
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = MeanXEdit
AnchorSideTop.Side = asrCenter
Left = 0
Height = 15
Top = 6
Top = 4
Width = 43
Caption = 'Mean X:'
ParentColor = False
end
object Label2: TLabel
AnchorSideLeft.Control = MeanX
object MeanXEdit: TEdit[6]
AnchorSideLeft.Control = Label1
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Label1
Left = 108
AnchorSideTop.Control = ParamsPanel
Left = 51
Height = 23
Top = 0
Width = 60
Alignment = taRightJustify
BorderSpacing.Left = 8
TabOrder = 0
Text = 'MeanXEdit'
end
object Label2: TLabel[7]
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = MeanYEdit
AnchorSideTop.Side = asrCenter
Left = 0
Height = 15
Top = 6
Top = 35
Width = 43
BorderSpacing.Left = 16
Caption = 'Mean Y:'
ParentColor = False
end
object Label3: TLabel
AnchorSideLeft.Control = MeanY
object MeanYEdit: TEdit[8]
AnchorSideLeft.Control = MeanXEdit
AnchorSideTop.Control = MeanXEdit
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = MeanXEdit
AnchorSideRight.Side = asrBottom
Left = 51
Height = 23
Top = 31
Width = 60
Alignment = taRightJustify
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Top = 8
TabOrder = 2
Text = 'MeanYEdit'
end
object Label3: TLabel[9]
AnchorSideLeft.Control = MeanXEdit
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Label2
Left = 221
AnchorSideTop.Control = StdDevXEdit
AnchorSideTop.Side = asrCenter
Left = 135
Height = 15
Top = 6
Top = 4
Width = 50
BorderSpacing.Left = 16
BorderSpacing.Left = 24
Caption = 'Std.Dev.X'
ParentColor = False
end
object Label4: TLabel
AnchorSideLeft.Control = SDX
object StdDevXEdit: TEdit[10]
AnchorSideLeft.Control = Label3
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Label3
Left = 345
AnchorSideTop.Control = ParamsPanel
Left = 193
Height = 23
Top = 0
Width = 60
Alignment = taRightJustify
BorderSpacing.Left = 8
TabOrder = 1
Text = 'StdDevXEdit'
end
object Label4: TLabel[11]
AnchorSideLeft.Control = Label3
AnchorSideTop.Control = StdDevYEdit
AnchorSideTop.Side = asrCenter
Left = 135
Height = 15
Top = 6
Top = 35
Width = 50
BorderSpacing.Left = 16
Caption = 'Std.Dev.Y'
ParentColor = False
end
object Label5: TLabel
AnchorSideLeft.Control = SDY
object StdDevYEdit: TEdit[12]
AnchorSideLeft.Control = StdDevXEdit
AnchorSideTop.Control = MeanYEdit
AnchorSideRight.Control = StdDevXEdit
AnchorSideRight.Side = asrBottom
Left = 193
Height = 23
Top = 31
Width = 60
Alignment = taRightJustify
Anchors = [akTop, akLeft, akRight]
TabOrder = 3
Text = 'StdDevYEdit'
end
object Label5: TLabel[13]
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Label4
Left = 450
AnchorSideTop.Control = CorrEdit
AnchorSideTop.Side = asrCenter
AnchorSideRight.Control = CorrEdit
Left = 51
Height = 15
Top = 6
Width = 36
Top = 82
Width = 76
Anchors = [akTop, akRight]
BorderSpacing.Left = 8
Caption = 'Cor.XY'
BorderSpacing.Right = 8
Caption = 'Correlation XY'
ParentColor = False
end
object Label6: TLabel
AnchorSideLeft.Control = Corr
object CorrEdit: TEdit[14]
AnchorSideLeft.Control = Label3
AnchorSideTop.Control = MeanYEdit
AnchorSideTop.Side = asrBottom
Left = 135
Height = 23
Top = 78
Width = 60
Alignment = taRightJustify
BorderSpacing.Top = 24
TabOrder = 4
Text = 'CorrEdit'
end
object Label6: TLabel[15]
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Label5
AnchorSideTop.Control = NumObsEdit
AnchorSideTop.Side = asrCenter
Left = 554
AnchorSideRight.Control = NumObsEdit
Left = 92
Height = 15
Top = 6
Top = 113
Width = 35
Anchors = [akTop, akRight]
BorderSpacing.Left = 16
BorderSpacing.Right = 8
Caption = 'N Size:'
ParentColor = False
end
object MeanX: TEdit
AnchorSideLeft.Control = Label1
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Panel1
AnchorSideTop.Side = asrCenter
Left = 49
Height = 23
Top = 2
Width = 43
Alignment = taRightJustify
BorderSpacing.Left = 6
OnKeyPress = MeanXKeyPress
TabOrder = 0
Text = 'MeanX'
end
object MeanY: TEdit
AnchorSideLeft.Control = Label2
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = MeanX
Left = 159
Height = 23
Top = 2
Width = 46
Alignment = taRightJustify
BorderSpacing.Left = 8
OnKeyPress = MeanYKeyPress
TabOrder = 1
Text = 'MeanY'
end
object SDX: TEdit
AnchorSideLeft.Control = Label3
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = MeanY
Left = 279
Height = 23
Top = 2
Width = 50
Alignment = taRightJustify
BorderSpacing.Left = 8
OnKeyPress = SDXKeyPress
TabOrder = 2
Text = 'SDX'
end
object SDY: TEdit
AnchorSideLeft.Control = Label4
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = SDX
Left = 403
Height = 23
Top = 2
Width = 39
Alignment = taRightJustify
BorderSpacing.Left = 8
OnKeyPress = SDYKeyPress
TabOrder = 3
Text = 'SDY'
end
object Corr: TEdit
AnchorSideLeft.Control = Label5
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = SDY
Left = 494
Height = 23
Top = 2
Width = 44
Alignment = taRightJustify
BorderSpacing.Left = 8
OnKeyPress = CorrKeyPress
TabOrder = 4
Text = 'Corr'
end
object ComputeBtn: TButton
AnchorSideTop.Control = CloseBtn
AnchorSideRight.Control = CloseBtn
Left = 702
Height = 26
Top = 0
Width = 65
Anchors = [akTop, akRight]
BorderSpacing.Right = 8
Caption = 'Compute'
OnClick = ComputeBtnClick
TabOrder = 6
end
object CloseBtn: TButton
AnchorSideTop.Control = Panel1
AnchorSideTop.Side = asrCenter
AnchorSideRight.Control = Panel1
object NumObsEdit: TEdit[16]
AnchorSideLeft.Control = CorrEdit
AnchorSideTop.Control = CorrEdit
AnchorSideTop.Side = asrBottom
AnchorSideRight.Control = CorrEdit
AnchorSideRight.Side = asrBottom
Left = 775
Height = 26
Top = 0
Width = 66
Anchors = [akTop, akRight]
Caption = 'Close'
ModalResult = 11
TabOrder = 7
end
object Nobs: TEdit
AnchorSideLeft.Control = Label6
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Corr
Left = 597
Left = 135
Height = 23
Top = 2
Width = 40
Top = 109
Width = 60
Alignment = taRightJustify
BorderSpacing.Left = 8
OnKeyPress = NobsKeyPress
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Top = 8
TabOrder = 5
Text = 'Nobs'
Text = 'NumObsEdit'
end
object PlotOptionsGroup: TGroupBox[17]
AnchorSideLeft.Control = ParamsPanel
AnchorSideTop.Control = NumObsEdit
AnchorSideTop.Side = asrBottom
Left = 0
Height = 90
Top = 148
Width = 178
AutoSize = True
BorderSpacing.Top = 16
Caption = 'Plot options'
ClientHeight = 70
ClientWidth = 174
TabOrder = 6
object NumBinsEdit: TSpinEdit
AnchorSideLeft.Control = Label7
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = PlotOptionsGroup
Left = 107
Height = 23
Top = 8
Width = 55
BorderSpacing.Left = 8
BorderSpacing.Top = 8
BorderSpacing.Right = 12
MinValue = 2
OnChange = NumBinsEditChange
TabOrder = 0
Value = 2
end
object Label7: TLabel
AnchorSideLeft.Control = PlotOptionsGroup
AnchorSideTop.Control = NumBinsEdit
AnchorSideTop.Side = asrCenter
Left = 16
Height = 15
Top = 12
Width = 83
BorderSpacing.Left = 16
Caption = 'Number of bins'
ParentColor = False
end
object BinCountChk: TCheckBox
AnchorSideLeft.Control = Label7
AnchorSideTop.Control = NumBinsEdit
AnchorSideTop.Side = asrBottom
Left = 16
Height = 19
Top = 39
Width = 113
BorderSpacing.Top = 8
BorderSpacing.Bottom = 12
Caption = 'Show frequencies'
OnChange = BinCountChkChange
TabOrder = 1
end
end
end
object Bevel1: TBevel
Left = 0
Height = 8
Top = 397
Width = 857
Align = alBottom
Shape = bsBottomLine
inherited ParamsSplitter: TSplitter
Left = 279
Height = 447
end
inherited PageControl: TPageControl
Left = 288
Height = 431
Width = 561
TabOrder = 1
end
end

View File

@ -5,462 +5,491 @@ unit CorSimUnit;
interface
uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
ExtCtrls, StdCtrls, Math,
Globals, OutputUnit;
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
ExtCtrls, StdCtrls, ComCtrls, Spin,
TAGraph, TACustomSeries,
Globals, BasicStatsReportAndChartFormUnit;
type
{ TCorSimFrm }
TCorSimResult = record
a, b: Double;
xMean, xStdDev: Double;
yMean, yStdDev: Double;
CorrXY: Double;
end;
TCorSimFrm = class(TForm)
Bevel1: TBevel;
Nobs: TEdit;
Image1: TImage;
{ TCorSimForm }
TCorSimForm = class(TBasicStatsReportAndChartForm)
BinCountChk: TCheckBox;
Label7: TLabel;
PlotOptionsGroup: TGroupBox;
NumObsEdit: TEdit;
Label6: TLabel;
CloseBtn: TButton;
ComputeBtn: TButton;
Corr: TEdit;
CorrEdit: TEdit;
Label5: TLabel;
SDY: TEdit;
NumBinsEdit: TSpinEdit;
StdDevYEdit: TEdit;
Label4: TLabel;
SDX: TEdit;
StdDevXEdit: TEdit;
Label3: TLabel;
MeanY: TEdit;
MeanYEdit: TEdit;
Label2: TLabel;
MeanX: TEdit;
MeanXEdit: TEdit;
Label1: TLabel;
Panel1: TPanel;
procedure ComputeBtnClick(Sender: TObject);
procedure CorrKeyPress(Sender: TObject; var Key: char);
procedure FormShow(Sender: TObject);
procedure MeanXKeyPress(Sender: TObject; var Key: char);
procedure MeanYKeyPress(Sender: TObject; var Key: char);
procedure NobsKeyPress(Sender: TObject; var Key: char);
procedure SDXKeyPress(Sender: TObject; var Key: char);
procedure SDYKeyPress(Sender: TObject; var Key: char);
procedure BinCountChkChange(Sender: TObject);
procedure NumBinsEditChange(Sender: TObject);
private
{ private declarations }
xmean, ymean, xsd, ysd, corxy, corsqr, yvariance, predvar : double;
errvariance, stderror, b, constant, newxmean, newymean : double;
newxsd, newysd, newcorr, randomerror, newb, newconstant : double;
x, y: DblDyneVec;
freqx, freqy: IntDyneVec;
N: integer;
XHistogramChart: TChart;
YHistogramChart: TChart;
XHistogramSeries: TChartSeries;
YHistogramSeries: TChartSeries;
xValues, yValues: DblDyneVec;
simResult: TCorSimResult;
procedure ChartAfterPaint(Sender: TChart);
procedure ChartFrameResize(Sender: TObject);
procedure GenerateData(ACount: Integer;
const XMean, YMean, XStdDev, YStdDev, CorrXY: Double; out A, B: Double);
procedure Plot;
function Validate(out AMsg: String; out AControl: TWinControl): Boolean;
procedure WriteReport(A, B, XMean, YMean, XStdDev, YStdDev, CorrXY: Double);
protected
procedure AdjustConstraints; override;
procedure Compute; override;
function Validate(out AMsg: String; out AControl: TWinControl): Boolean; override;
public
{ public declarations }
constructor Create(AOwner: TComponent); override;
procedure Reset; override;
end;
var
CorSimFrm: TCorSimFrm;
CorSimForm: TCorSimForm;
implementation
{ TCorSimFrm }
{$R *.lfm}
procedure TCorSimFrm.MeanXKeyPress(Sender: TObject; var Key: char);
begin
if Key = #13 then MeanY.SetFocus;
end;
uses
Math,
TAGeometry, TAChartUtils, TALegend, TASeries,
MatrixUnit, ChartFrameUnit;
procedure TCorSimFrm.CorrKeyPress(Sender: TObject; var Key: char);
begin
if Key = #13 then Nobs.SetFocus;
end;
procedure TCorSimFrm.ComputeBtnClick(Sender: TObject);
var
i: integer;
msg: String;
C: TWinControl;
lReport: TStrings;
{ TCorSimForm }
constructor TCorSimForm.Create(AOwner: TComponent);
begin
if not Validate(msg, C) then begin
C.SetFocus;
MessageDlg(msg, mtError, [mbOk], 0);
exit;
inherited;
FChartFrame.OnResize := @ChartFrameResize;
FChartFrame.Chart.OnAfterPaint := @ChartAfterPaint;
FChartFrame.Chart.Foot.Alignment := taLeftJustify;
XHistogramChart := TChart.Create(FChartFrame);
with XHistogramChart do
begin
Parent := FChartFrame;
AnchorSideTop.Control := FChartFrame.ChartToolBar;
AnchorSideTop.Side := asrBottom;
AnchorSideLeft.Control := FChartFrame.Chart;
AnchorSideRight.Control := FChartFrame.Chart;
AnchorSideRight.Side := asrBottom;
Anchors := [akLeft, akTop, akRight];
Height := 200;
Color := FChartFrame.Chart.Color;
BackColor := FChartFrame.Chart.Color;
Margins.Bottom := 0;
Frame.Visible := false;
LeftAxis.Visible := false;
BottomAxis.Grid.Visible := false;
BottomAxis.Marks.Visible := false;
BottomAxis.AxisPen.Visible := true;
end;
N := StrToInt(NObs.Text);
xmean := StrToFloat(MeanX.Text);
ymean := StrToFloat(MeanY.Text);
xsd := StrToFloat(SDX.Text);
ysd := StrToFloat(SDY.Text);
corxy := StrToFloat(Corr.Text);
XHistogramSeries := TBarSeries.Create(FChartFrame);
with TBarSeries(XHistogramSeries) do
begin
BarBrush.Color := DATA_COLORS[1];
MarkPositions := lmpPositive;
end;
XHistogramChart.AddSeries(XHistogramSeries);
YHistogramChart := TChart.Create(FChartFrame);
with YHistogramChart do
begin
Parent := FChartFrame;
AnchorSideTop.Control := FChartFrame.Chart;
AnchorSideRight.Control := FChartFrame;
AnchorSideRight.Side := asrBottom;
AnchorSideBottom.Control := FChartFrame.Chart;
AnchorSideBottom.Side := asrBottom;
Anchors := [akTop, akRight, akBottom];
Width := 200;
Color := FChartFrame.Chart.Color;
BackColor := FChartFrame.Chart.Color;
Margins.Left := 0;
Frame.Visible := false;
BottomAxis.Visible := false;
LeftAxis.Grid.Visible := false;
LeftAxis.Marks.Visible := false;
LeftAxis.AxisPen.Visible := true;
end;
YHistogramSeries := TBarSeries.Create(FChartFrame);
with TBarSeries(YHistogramSeries) do
begin
AxisIndexX := 0;
AxisIndexY := 1;
BarBrush.Color := DATA_COLORS[2];
MarkPositions := lmpPositive;
end;
YHistogramChart.AddSeries(YHistogramSeries);
with FChartFrame.Chart do
begin
Align := alNone;
AnchorSideLeft.Control := FChartFrame;
AnchorSideBottom.Control := FChartFrame;
AnchorSideBottom.Side := asrBottom;
AnchorSideTop.Control := XHistogramChart;
AnchorSideTop.Side := asrBottom;
AnchorSideRight.Control := YHistogramChart;
Anchors := [akLeft, akTop, akRight, akBottom];
end;
PageControl.ActivePageIndex := 0;
end;
procedure TCorSimForm.AdjustConstraints;
begin
inherited;
ParamsPanel.Constraints.MinWidth := Max(
3*CloseBtn.Width + 2*CloseBtn.BorderSpacing.Left,
StdDevXEdit.Left + StdDevXEdit.Width
);
ParamsPanel.Constraints.MinHeight :=
PlotOptionsGroup.Top + PlotOptionsGroup.Height +
ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height;
end;
procedure TCorSimForm.BinCountChkChange(Sender: TObject);
begin
if BinCountChk.Checked then
begin
XHistogramSeries.Marks.Style := smsValue;
YHistogramSeries.Marks.Style := smsValue;
end else
begin
XHistogramSeries.Marks.Style := smsNone;
YHistogramSeries.Marks.Style := smsNone;
end;
end;
procedure TCorSimForm.ChartAfterPaint(Sender: TChart);
var
ext: TDoubleRect;
R: Trect;
begin
ext := FChartFrame.Chart.CurrentExtent;
R.TopLeft := FChartFrame.Chart.GraphToImage(DoublePoint(ext.a.x, ext.b.y));
R.BottomRight := FChartFrame.Chart.GraphToImage(DoublePoint(ext.b.x, ext.a.y));
XHistogramChart.BorderSpacing.Left := R.Left;
XHistogramChart.BorderSpacing.Right := FChartFrame.Chart.Width - R.Right;
YHistogramChart.BorderSpacing.Top := R.Top;
YHistogramChart.BorderSpacing.Bottom := FChartFrame.Chart.Height - R.Bottom;
end;
procedure TCorSimForm.ChartFrameResize(Sender: TObject);
begin
XHistogramChart.Height := Min(FChartFrame.Width div 5, FChartFrame.Height div 5);
YHistogramChart.Width := XHistogramChart.Height;
end;
procedure TCorSimForm.Compute;
var
N: Integer;
a, b: Double;
xMean, yMean, xStdDev, yStdDev, corrXY: Double;
begin
N := StrToInt(NumObsEdit.Text);
xMean := StrToFloat(MeanXEdit.Text);
yMean := StrToFloat(MeanYEdit.Text);
xStdDev := StrToFloat(StdDevXEdit.Text);
yStdDev := StrToFloat(StdDevYEdit.Text);
corrXY := StrToFloat(CorrEdit.Text);
GenerateData(N, xMean, yMean, xStdDev, yStdDev, corrXY, a, b);
with SimResult do
begin
VecMeanStdDev(xValues, XMean, XStdDev);
VecMeanStdDev(yValues, YMean, YStdDev);
SimResult.CorrXY := (xValues * yValues - XMean * YMean * N) / ((N-1) * XStdDev * YStdDev);
B := CorrXY * (YStdDev / XStdDev);
A := YMean - B * XMean;
end;
WriteReport(a, b, xMean, yMean, xStdDev, yStdDev, corrXY);
Plot;
end;
procedure TCorSimForm.GenerateData(ACount: Integer;
const XMean, YMean, XStdDev, YStdDev, CorrXY: Double; out A, B: Double);
var
corrSqr, yVariance, predVariance, errVariance, stdError: Double;
i: Integer;
begin
Randomize;
SetLength(freqx, N + 1);
SetLength(freqy, N + 1);
SetLength(x, N + 1);
SetLength(y, N + 1);
// Calculate the fitted line parameters
B := CorrXY * (YStdDev / XStdDev);
A := YMean - B * XMean;
// generate x and y data observations
corsqr := corxy * corxy;
yvariance := ysd * ysd;
predvar := corsqr * yvariance;
errvariance := yvariance - predvar;
stderror := sqrt(errvariance);
b := corxy * (ysd / xsd);
constant := ymean - b * xmean;
// Calculate the "scatter" parameters
corrSqr := sqr(CorrXY);
yVariance := YStdDev * YStdDev;
predVariance := corrSqr * yVariance;
errVariance := yVariance - predVariance;
stdError := sqrt(errVariance);
newxmean := 0.0;
newymean := 0.0;
newxsd := 0.0;
newysd := 0.0;
newcorr := 0.0;
for i := 1 to N do
// Calculate x and y values
xValues := nil;
yValues := nil;
SetLength(xValues, ACount);
SetLength(yValues, ACount);
for i := 0 to ACount-1 do
begin
x[i] := RandG(xmean, xsd);
randomerror := RandG(0.0, stderror);
y[i] := b * x[i] + constant + randomerror;
newxmean := newxmean + x[i];
newymean := newymean + y[i];
newxsd := newxsd + sqr(x[i]);
newysd := newysd + sqr(y[i]);
newcorr := newcorr + x[i] * y[i];
end;
newxsd := newxsd - sqr(newxmean) / N;
newxsd := newxsd / (N - 1);
newxsd := sqrt(newxsd);
newysd := newysd - sqr(newymean) / N;
newysd := newysd / (N - 1);
newysd := sqrt(newysd);
newcorr := newcorr - newxmean * newymean / N;
newcorr := newcorr / (N - 1);
newcorr := newcorr / (newxsd * newysd);
newxmean := newxmean / N;
newymean := newymean / N;
newb := newcorr * (newysd / newxsd);
newconstant := newymean - newb * newxmean;
lReport := TStringList.Create;
try
lReport.Add('POPULATION PARAMETERS FOR THE SIMULATION');
lReport.Add('');
lReport.Add('Mean X: %8.3f', [xmean]);
lReport.Add('Std. Dev. X: %8.3f', [xsd]);
lReport.Add('');
lReport.Add('Mean Y: %8.3f', [ymean]);
lReport.Add('Std. Dev. Y: %8.3f', [ysd]);
lReport.Add('');
lReport.Add('Product-Moment Correlation: %8.3f', [corxy]);
lReport.Add('Regression line slope: %8.3f', [b]);
lReport.Add(' constant: %8.3f', [constant]);
lReport.Add('');
lReport.Add(DIVIDER);
lReport.Add('');
lReport.Add('SAMPLE STATISTICS FOR %d OBSERVATIONS FROM THE POPULATION', [N]);
lReport.Add('');
lReport.Add('Mean X: %8.3f', [newxmean]);
lReport.Add('Std. Dev. X: %8.3f', [newxsd]);
lReport.Add('');
lReport.Add('Mean Y: %8.3f', [newymean]);
lReport.Add('Std. Dev. Y: %8.3f', [newysd]);
lReport.Add('');
lReport.Add('Product-Moment Correlation: %8.3f', [newcorr]);
lReport.Add('Regression line slope: %8.3f', [newb]);
lReport.Add(' constant: %8.3f', [newconstant]);
lReport.Add('');
lReport.Add(DIVIDER);
lReport.Add('');
lReport.Add('Pair No. X Y ');
lReport.Add('-------- --------- ---------');
for i := 1 to N do
lReport.Add(' %4d %8.3f %8.3f', [i, x[i], y[i]]);
DisplayReport(lReport);
Plot();
finally
lReport.Free;
freqx := nil;
freqy := nil;
x := nil;
y := nil;
xValues[i] := RandG(xMean, xStdDev);
yValues[i] := A + B * xValues[i] + RandG(0.0, stdError);
end;
end;
procedure TCorSimFrm.FormShow(Sender: TObject);
begin
Image1.Canvas.Pen.Color := clBlack;
Image1.Canvas.Brush.Color := clWhite;
Image1.Canvas.Rectangle(0, 0, Image1.Width, Image1.Height);
MeanX.Text := '100';
MeanY.Text := '100';
SDX.Text := '15';
SDY.Text := '15';
Corr.Text := '.8';
Nobs.Text := '100';
procedure TCorSimForm.NumBinsEditChange(Sender: TObject);
begin
if Assigned(FChartFrame) and Assigned(xValues) and Assigned(yValues) then
Plot;
end;
procedure TCorSimFrm.MeanYKeyPress(Sender: TObject; var Key: char);
begin
if Key = #13 then SDX.SetFocus;
end;
procedure TCorSimFrm.NobsKeyPress(Sender: TObject; var Key: char);
begin
if Key = #13 then ComputeBtn.SetFocus;
end;
procedure TCorSimFrm.SDXKeyPress(Sender: TObject; var Key: char);
begin
if Key = #13 then SDY.SetFocus;
end;
procedure TCorSimFrm.SDYKeyPress(Sender: TObject; var Key: char);
begin
if Key = #13 then Corr.SetFocus;
end;
procedure TCorSimFrm.Plot;
procedure TCorSimForm.Plot;
var
minx, maxx, miny, maxy, xincrement, yincrement: double;
predy1, predy2, lowerx, upperx, frange, prop: double;
charlabel: string;
xpos, ypos, xpos1, ypos1, xpos2, ypos2: integer;
i, winwidth, winheight, xoffset, yoffset, xaxislong, yaxislong: integer;
j, xspacing, yspacing, labelwidth, minfreq, maxfreq: integer;
flength, theight, lowery, uppery: integer;
freqData: DblDyneVec;
x, mn, mx: Double;
i, n: Integer;
xpts, ypts: DblDyneVec;
begin
// get min and max of x and y points
minx := x[1];
maxx := minx;
miny := y[1];
maxy := miny;
for i := 1 to N do
FChartFrame.Clear;
XHistogramSeries.Clear;
YHistogramSeries.Clear;
// Chart labelling
FChartFrame.SetXTitle('x distribution');
FChartFrame.SetYTitle('y distribution');
FChartFrame.SetFooter(Format(
'Correlation: %.3f' + LineEnding +
'X Mean: %.3f, StdDev: %.3f' + LineEnding +
'Y Mean: %.3f, StdDev: %.3f', [
SimResult.CorrXY, SimResult.XMean, SimResult.XStdDev, SimResult.YMean, SimResult.YStdDev
]));
FChartFrame.Chart.Legend.Alignment := laBottomCenter;
FChartFrame.Chart.Legend.ColumnCount := 2;
// Draw data points
FChartFrame.PlotXY(ptSymbols, xValues, yValues, nil, nil, 'Data', DATA_COLORS[0]);
// Draw top histogram
VecMaxMin(xValues, mx, mn);
n := NumBinsEdit.Value;
freqData := VecHistogram(xValues, mn, mx, n);
for i:= 0 to n-1 do
begin
if (minx > x[i]) then minx := x[i];
if (maxx < x[i]) then maxx := x[i];
if (miny > y[i]) then miny := y[i];
if (maxy < y[i]) then maxy := y[i];
end;
xincrement := (maxx - minx) / 10;
yincrement := (maxy - miny) / 10;
winwidth := Image1.Width;
winheight := Image1.Height;
xoffset := winwidth div 5;
yoffset := winheight div 5;
xaxislong := winwidth - xoffset- winwidth div 10;
yaxislong := winheight - yoffset - winheight div 10;
xspacing := xaxislong div 10;
yspacing := yaxislong div 10;
Image1.Canvas.Pen.Color := clBlack;
Image1.Canvas.Line(xoffset, yaxislong, winwidth, yaxislong);
Image1.canvas.Line(xoffset, yaxislong, xoffset, 0);
// do xaxis
for i := 0 to 11 do
begin
Image1.Canvas.Line(xoffset + i * xspacing, yaxislong, xoffset + i * xspacing, yaxislong + 10);
charlabel := Format('%.3f', [minx + i * xincrement]);
labelwidth := Image1.Canvas.TextWidth(charlabel);
xpos := xoffset + i * xspacing - labelwidth div 2;
ypos := yaxislong + 12;
Image1.Canvas.TextOut(xpos, ypos, charlabel);
x := i / (n-1) * (mx - mn) + mn;
XHistogramSeries.AddXY(x, freqData[i]);
end;
// do yaxis
for i := 0 to 11 do
begin
Image1.Canvas.Line(xoffset, yaxislong - i * yspacing, xoffset-10, yaxislong - i * yspacing);
charlabel := Format('%.3f', [miny + i * yincrement]);
labelwidth := Image1.Canvas.TextWidth(charlabel);
xpos := xoffset - 10 - labelwidth;
ypos := yaxislong - i * yspacing;
Image1.Canvas.TextOut(xpos, ypos, charlabel);
end;
// Draw regression line
// Regression line
SetLength(xpts, 2);
SetLength(yPts, 2);
xpts[0] := mn; ypts[0] := SimResult.a + SimResult.b * xpts[0];
xpts[1] := mx; ypts[1] := SimResult.a + SimResult.b * xpts[1];
FChartFrame.PlotXY(ptLines, xpts, ypts, nil, nil, 'Regression line', clBlack);
// plot points
Image1.Canvas.Pen.Color := clRed;
for i := 1 to N do
// Draw right histogram
VecMaxMin(yValues, mx, mn);
n := NumBinsEdit.Value;
freqData := VecHistogram(yValues, mn, mx, n);
for i := 0 to n-1 do
begin
xpos := round(xoffset + ((x[i] - minx) / (maxx - minx) * xaxislong));
ypos := round(yaxislong - ((y[i] - miny) / (maxy - miny) * yaxislong));
Image1.Canvas.Ellipse(xpos, ypos, xpos+5, ypos+5);
x := i / (n-1) * (mx - mn) + mn;
YHistogramSeries.AddXY(x, freqData[i]);
end;
// draw regression line
Image1.Canvas.Pen.Color := clBlack;
predy1 := newb * minx + newconstant;
predy2 := newb * maxx + newconstant;
xpos1 := xoffset;
xpos2 := xoffset + xaxislong;
ypos1 := round(yaxislong - ((predy1 - miny) / (maxy - miny) * yaxislong));
ypos2 := round(yaxislong - ((predy2 - miny) / (maxy - miny) * yaxislong));
Image1.Canvas.Line(xpos1, ypos1, xpos2, ypos2);
// do x frequency distribution
xincrement := (maxx - minx) / 50.0;
xspacing := xaxislong div 50;
for j := 1 to 51 do
freqx[j] := 0;
for i := 1 to N do
begin
for j := 1 to 51 do
begin
lowerx := minx + j * xincrement;
upperx := minx + (j+1) * xincrement;
if (x[i] >= lowerx) and (x[i] < upperx) then
freqx[j] := freqx[j] + 1;
end;
end;
// plot the x frequencies
minfreq := N;
maxfreq := 0;
for j := 1 to 51 do
begin
if (freqx[j] > maxfreq) then
maxfreq := freqx[j];
if (freqx[j] < minfreq) then
minfreq := freqx[j];
end;
flength := winheight - (yaxislong + 25) - Panel1.Height;
for j := 1 to 51 do
begin
xpos := xoffset + j * xspacing;
ypos1 := round(yaxislong + 25 + (freqx[j] - minfreq)/ (maxfreq-minfreq) * flength);
ypos2 := yaxislong + 25;
Image1.Canvas.Line(xpos, ypos1, xpos, ypos2);
end;
Image1.Canvas.Line(xoffset, yaxislong+25, winwidth, yaxislong+25);
xpos := 20;
ypos := yaxislong+30;
Image1.Canvas.TextOut(xpos, ypos, 'X DISTRIBUTION');
theight := Image1.Canvas.TextHeight('X');
ypos := ypos + theight;
charlabel := Format('Correlation: %.3f', [newcorr]);
Image1.Canvas.TextOut(xpos, ypos, charlabel);
ypos := ypos + theight;
charlabel := Format('Mean X: %.3f; Mean Y: %.3f', [newxmean, newymean]);
Image1.Canvas.TextOut(xpos, ypos, charlabel);
charlabel := Format('SD X: %.3f; SD Y: %.3f', [newxsd, newysd]);
ypos := ypos + theight;
Image1.Canvas.TextOut(xpos,ypos,charlabel);
// do y frequency distribution
yincrement := (maxy-miny) / 50.0;
yspacing := yaxislong div 50;
for j := 1 to 51 do
freqy[j] := 0;
for i := 1 to N do
begin
for j := 1 to 51 do
begin
lowery := round(miny + j * yincrement);
uppery := round(miny + ((j+1) * yincrement));
if (y[i] >= lowery) and (y[i] < uppery) then
freqy[j] := freqy[j] + 1;
end;
end;
// plot the y frequencies
minfreq := N;
maxfreq := 0;
for j := 1 to 51 do
begin
if (freqy[j] > maxfreq) then maxfreq := freqy[j];
if (freqy[j] < minfreq) then minfreq := freqy[j];
end;
flength := winwidth - (xaxislong + 150);
for j := 1 to 51 do
begin
ypos := yaxislong - j * yspacing;
frange := maxfreq - minfreq;
prop := (freqy[j] - minfreq) / frange;
xpos1 := round(xoffset - 50 - prop * flength);
xpos2 := xoffset - 50;
Image1.Canvas.Line(xpos1, ypos, xpos2, ypos);
end;
Image1.Canvas.Line(xoffset - 50, yaxislong, xoffset - 50, 0);
Image1.Canvas.TextOut(0,0,'Y DISTRIBUTION');
end;
function TCorSimFrm.Validate(out AMsg: String; out AControl: TWinControl): Boolean;
procedure TCorSimForm.Reset;
begin
inherited;
MeanXEdit.Text := '100';
MeanYEdit.Text := '100';
StdDevXEdit.Text := '15';
StdDevYEdit.Text := '15';
CorrEdit.Text := '0.8';
NumObsEdit.Text := '100';
NumBinsEdit.Value := 12;
end;
function TCorSimForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean;
var
x: Double;
n: Integer;
begin
Result := false;
if (MeanX.Text = '') or (MeanY.Text = '') or
(SDX.Text = '') or (SDY.Text = '') or
(Corr.Text = '') or (NObs.Text = '') then
if (MeanXEdit.Text = '') or (MeanYEdit.Text = '') or
(StdDevXEdit.Text = '') or (StdDevYEdit.Text = '') or
(CorrEdit.Text = '') or (NumObsEdit.Text = '') then
begin
if MeanX.Text = '' then
AControl := MeanX
else if MeanY.Text = '' then
AControl := MeanY
else if SDX.Text = '' then
AControl := SDX
else if SDY.Text = '' then
AControl := SDY
else if Corr.Text = '' then
AControl := Corr
else if NObs.Text = '' then
AControl := NObs;
if MeanXEdit.Text = '' then
AControl := MeanXEdit
else if MeanYEdit.Text = '' then
AControl := MeanYEdit
else if StdDevXEdit.Text = '' then
AControl := StdDevXEdit
else if StdDevYEdit.Text = '' then
AControl := StdDevYEdit
else if CorrEdit.Text = '' then
AControl := CorrEdit
else if NumObsEdit.Text = '' then
AControl := NumObsEdit;
AMsg := 'Input cannot be empty.';
exit;
end;
if not TryStrToFloat(MeanX.Text, xMean) then
if not TryStrToFloat(MeanXEdit.Text, x) then
begin
AControl := MeanX;
AControl := MeanXEdit;
AMsg := 'Mean X must be a valid number.';
exit;
end;
if not TryStrToFloat(MeanY.Text, yMean) then
if not TryStrToFloat(MeanYEdit.Text, x) then
begin
AControl := MeanY;
AControl := MeanYEdit;
AMsg := 'Mean Y must be a valid number.';
exit;
end;
if not TryStrToFloat(SDX.Text, xSD) or (xSD <= 0) then
if not TryStrToFloat(StdDevXEdit.Text, x) or (x <= 0) then
begin
AControl := SDX;
AControl := StdDevXEdit;
AMsg := 'Std.Dev X must be a valid positive number.';
exit;
end;
if not TryStrToFloat(SDY.Text, ySD) or (ySD <= 0) then
if not TryStrToFloat(StdDevYEdit.Text, x) or (x <= 0) then
begin
AControl := SDY;
AControl := StdDevYEdit;
AMsg := 'Std.Dev Y must be a valid positive number.';
exit;
end;
if not TryStrToFloat(Corr.Text, corXY) then
if not TryStrToFloat(CorrEdit.Text, x) then
begin
AControl := Corr;
AControl := CorrEdit;
AMsg := 'Correlation XY must be a valid number.';
exit;
end;
if not TryStrToInt(NObs.Text, N) or (N <= 0) then
if not TryStrToInt(NumObsEdit.Text, n) or (n <= 0) then
begin
AControl := NObs;
AControl := NumObsEdit;
AMsg := 'Number of observations must be a valid positive integer.';
exit;
end;
if NumBinsEdit.Text = '' then
begin
AControl := NumBinsEdit;
AMsg := 'Empty input not allowed here.';
exit;
end;
if not TryStrToInt(NumBinsEdit.Text, n) or (n < 2) then
begin
AControl := NumBinsEdit;
AMsg := 'There must be at least 2 bins.';
exit;
end;
Result := true;
end;
initialization
{$I corsimunit.lrs}
procedure TCorSimForm.WriteReport(
A, B, XMean, YMean, XStdDev, YStdDev, CorrXY: Double);
var
lReport: TStrings;
i: Integer;
begin
lReport := TStringList.Create;
try
lReport.Add('POPULATION PARAMETERS FOR THE SIMULATION');
lReport.Add('');
lReport.Add('Mean X: %8.3f', [XMean]);
lReport.Add('Std. Dev. X: %8.3f', [XStdDev]);
lReport.Add('');
lReport.Add('Mean Y: %8.3f', [YMean]);
lReport.Add('Std. Dev. Y: %8.3f', [YStdDev]);
lReport.Add('');
lReport.Add('Product-Moment Correlation: %8.3f', [CorrXY]);
lReport.Add('Regression line slope: %8.3f', [B]);
lReport.Add(' constant: %8.3f', [A]);
lReport.Add('');
lReport.Add(DIVIDER);
lReport.Add('');
lReport.Add('SAMPLE STATISTICS FOR %d OBSERVATIONS FROM THE POPULATION', [Length(xValues)]);
lReport.Add('');
lReport.Add('Mean X: %8.3f', [SimResult.XMean]);
lReport.Add('Std. Dev. X: %8.3f', [SimResult.XStdDev]);
lReport.Add('');
lReport.Add('Mean Y: %8.3f', [SimResult.YMean]);
lReport.Add('Std. Dev. Y: %8.3f', [SimResult.YStdDev]);
lReport.Add('');
lReport.Add('Product-Moment Correlation: %8.3f', [SimResult.CorrXY]);
lReport.Add('Regression line slope: %8.3f', [SimResult.B]);
lReport.Add(' constant: %8.3f', [SimResult.A]);
lReport.Add('');
lReport.Add(DIVIDER);
lReport.Add('');
lReport.Add('Pair No. X Y ');
lReport.Add('-------- --------- ---------');
for i := 0 to High(XValues) do
lReport.Add(' %4d %8.3f %8.3f', [i, xValues[i], yValues[i]]);
FReportFrame.DisplayReport(lReport);
finally
lReport.Free;
end;
end;
end.

View File

@ -42,19 +42,24 @@ procedure VecMeanVarStdDevSS(const AData: TDblVector;
procedure VecSumSS(const AData: TDblVector;
out Sum, SS: Double);
function VecHistogram(const AData: TDblVector; AMin, AMax: Double;
N: Integer): TDblVector;
function VecMedian(const AData: TDblVector): Double;
// Matrices
{ NOTE: Indices follow math convention:
- 1st index is the row index, i.e. runs vertically
- 2nd index is the col index, i.e. runs horizontally
All indices are 0-based. }
operator + (A, B: TDblMatrix): TDblMatrix;
operator - (A, B: TDblMatrix): TDblMatrix;
operator * (A, B: TDblMatrix): TDblMatrix;
operator * (A: TDblMatrix; v: TDblVector): TDblVector;
{ NOTE: Indices follow math convention:
- 1st index is the row index, i.e. runs vertically
- 2nd index is the col index, i.e. runs horizontally
All indices are 0-based. }
function MatAppendColVector(A: TDblMatrix; v: TDblVector): TDblMatrix;
procedure MatCheck(A: TDblMatrix);
procedure MatCheckSquare(A: TDblMatrix; out n: Integer);
@ -319,6 +324,26 @@ begin
end;
function VecHistogram(const AData: TDblVector; AMin, AMax: Double;
N: Integer): TDblVector;
var
i, j: Integer;
factor: Double;
begin
SetLength(Result, N);
for j := 0 to N-1 do Result[j] := 0;
factor := N / (AMax - AMin);
for i := 0 to High(AData) do
begin
j := trunc((AData[i] - AMin) * factor);
if j <= 0 then j := 0;
if j >= N then j := N-1;
Result[j] := Result[j] + 1;
end;
end;
function VecMedian(const AData: TDblVector): Double;
var
N, midPt: integer;