// Use file "BubblePlot2.laz" for testing unit MultXvsYUnit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Buttons, Clipbrd, ComCtrls, MainUnit, Globals, DataProcs, DictionaryUnit, ContextHelpUnit, BasicStatsFormUnit, ReportFrameUnit, ChartFrameUnit; type { TMultXvsYFrm } TMultXvsYFrm = class(TBasicStatsForm) ButtonBevel: TBevel; HelpBtn: TButton; PageControl: TPageControl; ParamsPanel: TPanel; ParamsSplitter: TSplitter; ReportPage: TTabSheet; ChartPage: TTabSheet; XInBtn: TBitBtn; XOutBtn: TBitBtn; YInBtn: TBitBtn; YOutBtn: TBitBtn; GroupInBtn: TBitBtn; GroupOutBtn: TBitBtn; ResetBtn: TButton; ComputeBtn: TButton; CloseBtn: TButton; LinesChk: TCheckBox; XEdit: TEdit; YEdit: TEdit; GroupEdit: TEdit; OptionsGroup: TGroupBox; LabelEdit: TEdit; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Label5: TLabel; VarList: TListBox; procedure CloseBtnClick(Sender: TObject); procedure ComputeBtnClick(Sender: TObject); procedure FormActivate(Sender: TObject); procedure FormCreate(Sender: TObject); procedure GroupInBtnClick(Sender: TObject); procedure GroupOutBtnClick(Sender: TObject); procedure HelpBtnClick(Sender: TObject); procedure ResetBtnClick(Sender: TObject); 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 } FAutoSized: Boolean; FReportFrame: TReportFrame; FChartFrame: TChartFrame; procedure PlotXY(const XValues, YValues: DblDyneMat; const Groups: StrDyneVec); procedure UpdateBtnStates; public { public declarations } procedure Reset; override; end; var MultXvsYFrm: TMultXvsYFrm; implementation {$R *.lfm} uses TATypes, Math, Utils; { TMultXvsYFrm } procedure TMultXvsYFrm.ComputeBtnClick(Sender: TObject); var lReport: TStrings; i, N, xCol, yCol, grpCol, grp, numGrps: integer; grpName: String; //minX, maxX, minY, maxY, X, Y: double; cellstring: string; maxGrpSize: integer = 0; numInGrp: IntDyneVec = nil; xValues: DblDyneMat = nil; yValues: DblDyneMat = nil; grps: StrDyneVec = nil; means: array[0..1] of Double = (0.0, 0.0); stdDevs: array[0..1] of Double = (0.0, 0.0); selected: array of Integer = nil; begin // Get selected variables xCol := 0; yCol := 0; grpCol := 0; for i := 1 to NoVariables do begin cellstring := OS3MainFrm.DataGrid.Cells[i, 0]; if (cellstring = XEdit.Text) then xCol := i; if (cellstring = YEdit.Text) then yCol := i; if (cellstring = GroupEdit.Text) then grpCol := i; end; if (xCol = 0) or (yCol = 0) or (grpCol = 0) then begin ErrorMsg('No variable selected.'); exit; end; SetLength(selected, 3); selected[0] := xCol; selected[1] := yCol; selected[2] := grpCol; // Get groups // minGrp := MaxInt; // maxGrp := -MaxInt; SetLength(grps, NoCases); numGrps := 0; for i := 1 to NoCases do begin grpName := Trim(OS3MainFrm.DataGrid.Cells[grpCol, i]); if IndexOfString(grps, grpName) = -1 then begin grps[numGrps] := grpName; inc(numGrps); end; end; SetLength(XValues, numGrps, NoCases); // NoCases is over-dimensioned and will be trimmed later. SetLength(YValues, numGrps, NoCases); // dto. SetLength(numInGrp, numGrps); N := 0; for i := 1 to NoCases do begin if not GoodRecord(i, Length(selected), selected) then continue; inc(N); X := StrToFloat(OS3MainFrm.DataGrid.Cells[XCol, i]); Y := StrToFloat(OS3MainFrm.DataGrid.Cells[YCol, i]); grpName := Trim(OS3MainFrm.DataGrid.Cells[grpCol, i]); grp := IndexOfString(grps, grpName); xValues[grp, numInGrp[grp]] := X; yValues[grp, numInGrp[grp]] := Y; inc(numInGrp[grp]); Means[0] := Means[0] + X; StdDevs[0] := StdDevs[0] + sqr(X); Means[1] := Means[1] + Y; StdDevs[1] := StdDevs[1] + sqr(Y); end; // Trim XValues and YValues to correct dimension. SetLength(xValues, numGrps); SetLength(yValues, numGrps); for grp := 0 to numGrps-1 do begin SetLength(xValues[grp], numInGrp[grp]); SetLength(yValues[grp], numInGrp[grp]); end; // Get descriptive data for i := 0 to 1 do begin stdDevs[i] := stdDevs[i] - sqr(means[i]) / N; stdDevs[i] := sqrt(StdDevs[i] / (N - 1)); means[i] := means[i] / N; end; // Print out descriptive data to report frame lReport := TStringList.Create; try lReport.Add('X VERSUS Y FOR GROUPS PLOT'); lReport.Add(''); lReport.Add('X variable: ' + XEdit.Text); lReport.Add('Y variable: ' + YEdit.Text); lReport.Add('Group variable: ' + GroupEdit.Text); lReport.Add(''); lReport.Add('VARIABLE MEAN STANDARD DEVIATION'); lReport.Add('-------- -------- ------------------'); lReport.Add(' X %8.3f %14.3f', [Means[0], StdDevs[0]]); lReport.Add(' Y %8.3f %14.3f', [Means[1], StdDevs[1]]); lReport.Add(''); FReportFrame.DisplayReport(lReport); finally lReport.Free; end; // sort on X for i := 0 to numGrps - 1 do SortOnX(XValues[i], YValues[i]); // Plot data PlotXY(XValues, YValues, grps); end; procedure TMultXvsYFrm.CloseBtnClick(Sender: TObject); begin Close; end; procedure TMultXvsYFrm.FormActivate(Sender: TObject); var w: Integer; begin if FAutoSized then exit; w := MaxValue([HelpBtn.Width, ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]); HelpBtn.Constraints.MinWidth := w; ResetBtn.Constraints.MinWidth := w; ComputeBtn.Constraints.MinWidth := w; CloseBtn.Constraints.MinWidth := w; ParamsPanel.Constraints.MinWidth := Max( 4*w + 3*CloseBtn.BorderSpacing.Left, OptionsGroup.Width - XInBtn.Width div 2); ParamsPanel.Constraints.MinHeight := GroupOutBtn.Top + GroupOutBtn.Height + OptionsGroup.BorderSpacing.Top + OptionsGroup.Height + OptionsGroup.BorderSpacing.Bottom + LabelEdit.Height + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; Constraints.MinWidth := ParamsPanel.Constraints.MinWidth + 300; Constraints.MinHeight := ParamsPanel.Constraints.MinHeight + ParamsPanel.BorderSpacing.Top*2; if Width < Constraints.MinWidth then Width := 1; // enforce contraints if Height < Constraints.MinHeight then Height := 1; Position := poDesigned; FAutoSized := true; end; procedure TMultXvsYFrm.FormCreate(Sender: TObject); begin Assert(OS3MainFrm <> nil); if DictionaryFrm = nil then Application.CreateForm(TDictionaryFrm, DictionaryFrm); InitForm(self); FReportFrame := TReportFrame.Create(self); FReportFrame.Parent := ReportPage; FReportFrame.Align := alClient; FChartFrame := TChartFrame.Create(self); FChartFrame.Parent := ChartPage; FChartFrame.Align := alClient; FChartFrame.Chart.BottomAxis.Intervals.MaxLength := 80; FChartFrame.Chart.BottomAxis.Intervals.MinLength := 30; Reset; end; procedure TMultXvsYFrm.GroupInBtnClick(Sender: TObject); var i: integer; begin i := VarList.ItemIndex; if (i > -1) and (GroupEdit.Text = '') then begin GroupEdit.Text := VarList.Items[i]; VarList.Items.Delete(i); end; UpdateBtnStates; end; procedure TMultXvsYFrm.GroupOutBtnClick(Sender: TObject); begin if GroupEdit.Text <> '' then begin VarList.Items.Add(GroupEdit.Text); GroupEdit.Text := ''; end; UpdateBtnStates; end; procedure TMultXvsYFrm.HelpBtnClick(Sender: TObject); begin if ContextHelpForm = nil then Application.CreateForm(TContextHelpForm, ContextHelpForm); ContextHelpForm.HelpMessage((Sender as TButton).Tag); end; // Routine to plot X versus multiple Y values for several groups // Layout of X and Y matrices: // 1st index: group index, 2nd index: point index within group procedure TMultXvsYFrm.PlotXY(const XValues, YValues: DblDyneMat; const Groups: StrDyneVec); var pt: TPlotType; grp: Integer; clr: TColor; grpName: String; sym: TSeriesPointerStyle; begin FChartFrame.Clear; // Titles FChartFrame.SetTitle(LabelEdit.Text); FChartFrame.SetXTitle(XEdit.Text); FChartFrame.SetYTitle(YEdit.Text); if LinesChk.Checked then pt := ptLinesAndSymbols else pt := ptSymbols; for grp := 0 to Length(XValues)-1 do begin clr := DATA_COLORS[grp mod Length(DATA_COLORS)]; sym := DATA_SYMBOLS[grp mod Length(DATA_SYMBOLS)]; grpName := Format('%s %s', [GroupEdit.Text, Groups[grp]]); FChartFrame.PlotXY(pt, XValues[grp], YValues[grp], nil, nil, grpName, clr, sym); end; end; procedure TMultXvsYFrm.Reset; var i : integer; begin VarList.Clear; for i := 1 to NoVariables do VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); XEdit.Text := ''; YEdit.Text := ''; GroupEdit.Text := ''; LinesChk.Checked := false; FReportFrame.Clear; FChartFrame.Clear; UpdateBtnStates; end; procedure TMultXvsYFrm.ResetBtnClick(Sender: TObject); begin Reset; end; procedure TMultXvsYFrm.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] else if GroupEdit.Text = '' then GroupEdit.Text := VarList.Items[index]; VarList.Items.Delete(index); UpdateBtnStates; end; end; procedure TMultXvsYFrm.UpdateBtnStates; var lSelected: Boolean; i: Integer; begin lSelected := false; for i:=0 to VarList.Items.Count-1 do if VarList.Selected[i] then begin lSelected := true; break; end; XInBtn.Enabled := lSelected and (XEdit.Text = ''); YInBtn.Enabled := lSelected and (YEdit.Text = ''); GroupInBtn.Enabled := lSelected and (GroupEdit.Text = ''); XOutBtn.Enabled := (XEdit.Text <> ''); YOutBtn.Enabled := (YEdit.Text <> ''); GroupOutBtn.Enabled := (GroupEdit.Text <> ''); FReportFrame.UpdateBtnStates; FChartFrame.UpdateBtnStates; end; procedure TMultXvsYFrm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; procedure TMultXvsYFrm.XInBtnClick(Sender: TObject); var i: integer; begin i := VarList.ItemIndex; if (i > -1) and (XEdit.Text = '') then begin XEdit.Text := VarList.Items[i]; VarList.Items.Delete(i); end; UpdateBtnStates; end; procedure TMultXvsYFrm.XOutBtnClick(Sender: TObject); begin if XEdit.Text <> '' then begin VarList.Items.Add(XEdit.Text); XEdit.Text := ''; end; UpdateBtnStates; end; procedure TMultXvsYFrm.YInBtnClick(Sender: TObject); var i: integer; begin i := VarList.ItemIndex; if (i > -1) and (YEdit.Text = '') then begin YEdit.Text := VarList.Items[i]; VarList.Items.Delete(i); end; UpdateBtnStates; end; procedure TMultXvsYFrm.YOutBtnClick(Sender: TObject); begin if YEdit.Text <> '' then begin VarList.Items.Add(YEdit.Text); YEdit.Text := ''; end; UpdateBtnStates; end; end.