diff --git a/applications/lazstats/docs/HelpNDoc/LazStats.hnd b/applications/lazstats/docs/HelpNDoc/LazStats.hnd index b9bb63698..b5b9c3062 100644 Binary files a/applications/lazstats/docs/HelpNDoc/LazStats.hnd and b/applications/lazstats/docs/HelpNDoc/LazStats.hnd differ diff --git a/applications/lazstats/source/forms/analysis/multivariate/medianpolishunit.lfm b/applications/lazstats/source/forms/analysis/multivariate/medianpolishunit.lfm index d7713a69f..e9e8178ae 100644 --- a/applications/lazstats/source/forms/analysis/multivariate/medianpolishunit.lfm +++ b/applications/lazstats/source/forms/analysis/multivariate/medianpolishunit.lfm @@ -1,100 +1,81 @@ object MedianPolishForm: TMedianPolishForm Left = 638 - Height = 408 + Height = 410 Top = 340 - Width = 462 + Width = 472 AutoSize = True Caption = 'Median Polishing for a Two-Way Table' - ClientHeight = 408 - ClientWidth = 462 + ClientHeight = 410 + ClientWidth = 472 OnActivate = FormActivate OnCreate = FormCreate OnShow = ResetBtnClick Position = poMainFormCenter LCLVersion = '2.1.0.0' object ResetBtn: TButton - AnchorSideRight.Control = CancelBtn - AnchorSideBottom.Control = Owner - AnchorSideBottom.Side = asrBottom - Left = 161 - Height = 25 - Top = 375 - Width = 54 - Anchors = [akRight, akBottom] - AutoSize = True - BorderSpacing.Left = 100 - BorderSpacing.Top = 8 - BorderSpacing.Right = 12 - BorderSpacing.Bottom = 8 - Caption = 'Reset' - OnClick = ResetBtnClick - TabOrder = 3 - end - object CancelBtn: TButton AnchorSideRight.Control = ComputeBtn AnchorSideBottom.Control = Owner AnchorSideBottom.Side = asrBottom - Left = 227 + Left = 263 Height = 25 - Top = 375 - Width = 62 + Top = 377 + Width = 54 Anchors = [akRight, akBottom] AutoSize = True - BorderSpacing.Left = 12 + BorderSpacing.Left = 8 BorderSpacing.Top = 8 - BorderSpacing.Right = 12 + BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 - Cancel = True - Caption = 'Cancel' - ModalResult = 2 - TabOrder = 4 + Caption = 'Reset' + OnClick = ResetBtnClick + TabOrder = 1 end object ComputeBtn: TButton - AnchorSideRight.Control = ReturnBtn + AnchorSideRight.Control = CloseBtn AnchorSideBottom.Control = Owner AnchorSideBottom.Side = asrBottom - Left = 301 + Left = 325 Height = 25 - Top = 375 + Top = 377 Width = 76 Anchors = [akRight, akBottom] AutoSize = True - BorderSpacing.Left = 12 + BorderSpacing.Left = 8 BorderSpacing.Top = 8 - BorderSpacing.Right = 12 + BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 Caption = 'Compute' OnClick = ComputeBtnClick - TabOrder = 5 + TabOrder = 2 end - object ReturnBtn: TButton + object CloseBtn: TButton AnchorSideRight.Control = Owner AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = Owner AnchorSideBottom.Side = asrBottom - Left = 389 + Left = 409 Height = 25 - Top = 375 - Width = 61 + Top = 377 + Width = 55 Anchors = [akRight, akBottom] AutoSize = True - BorderSpacing.Left = 12 + BorderSpacing.Left = 8 BorderSpacing.Top = 8 - BorderSpacing.Right = 12 + BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 - Caption = 'Return' - ModalResult = 1 - TabOrder = 6 + Caption = 'Close' + ModalResult = 11 + TabOrder = 3 end object Bevel1: TBevel AnchorSideLeft.Control = Owner AnchorSideRight.Control = Owner AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = ReturnBtn + AnchorSideBottom.Control = CloseBtn Left = 0 Height = 8 - Top = 359 - Width = 462 + Top = 361 + Width = 472 Anchors = [akLeft, akRight, akBottom] Shape = bsBottomLine end @@ -103,15 +84,16 @@ object MedianPolishForm: TMedianPolishForm AnchorSideTop.Control = Owner AnchorSideRight.Control = Owner AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = NormChk + AnchorSideBottom.Control = Bevel1 Left = 0 - Height = 340 + Height = 361 Top = 0 - Width = 462 + Width = 472 Anchors = [akTop, akLeft, akRight, akBottom] + AutoSize = True BevelOuter = bvNone - ClientHeight = 340 - ClientWidth = 462 + ClientHeight = 361 + ClientWidth = 472 TabOrder = 0 object Label1: TLabel AnchorSideLeft.Control = Panel1 @@ -129,58 +111,59 @@ object MedianPolishForm: TMedianPolishForm AnchorSideLeft.Control = Panel1 AnchorSideTop.Control = Label1 AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = DepIn1 + AnchorSideRight.Control = DepIn AnchorSideBottom.Control = Panel1 AnchorSideBottom.Side = asrBottom Left = 8 - Height = 309 + Height = 338 Top = 23 - Width = 201 + Width = 206 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 8 BorderSpacing.Right = 8 - BorderSpacing.Bottom = 8 ItemHeight = 0 + OnSelectionChange = VarListSelectionChange TabOrder = 0 end object DepVar: TEdit - AnchorSideLeft.Control = DepIn1 + AnchorSideLeft.Control = DepIn AnchorSideLeft.Side = asrBottom AnchorSideRight.Control = Panel1 AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = DepOut AnchorSideBottom.Side = asrBottom - Left = 253 + Left = 258 Height = 23 Top = 48 - Width = 201 + Width = 206 Anchors = [akLeft, akRight, akBottom] BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 12 + ReadOnly = True TabOrder = 4 Text = 'DepVar' end - object DepIn1: TBitBtn + object DepIn: TBitBtn AnchorSideLeft.Control = Panel1 AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = VarList - Left = 217 + Left = 222 Height = 28 Top = 23 Width = 28 Images = MainDataModule.ImageList ImageIndex = 1 - OnClick = DepIn1Click + OnClick = DepInClick Spacing = 0 TabOrder = 1 end object DepOut: TBitBtn AnchorSideLeft.Control = Panel1 AnchorSideLeft.Side = asrCenter - AnchorSideTop.Control = DepIn1 + AnchorSideTop.Control = DepIn AnchorSideTop.Side = asrBottom - Left = 217 + Left = 222 Height = 28 Top = 55 Width = 28 @@ -196,7 +179,7 @@ object MedianPolishForm: TMedianPolishForm AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = DepOut AnchorSideTop.Side = asrBottom - Left = 217 + Left = 222 Height = 28 Top = 107 Width = 28 @@ -212,7 +195,7 @@ object MedianPolishForm: TMedianPolishForm AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Fact1In AnchorSideTop.Side = asrBottom - Left = 217 + Left = 222 Height = 28 Top = 139 Width = 28 @@ -228,7 +211,7 @@ object MedianPolishForm: TMedianPolishForm AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Fact1Out AnchorSideTop.Side = asrBottom - Left = 217 + Left = 222 Height = 28 Top = 191 Width = 28 @@ -244,7 +227,7 @@ object MedianPolishForm: TMedianPolishForm AnchorSideLeft.Side = asrCenter AnchorSideTop.Control = Fact2In AnchorSideTop.Side = asrBottom - Left = 217 + Left = 222 Height = 28 Top = 222 Width = 28 @@ -262,16 +245,17 @@ object MedianPolishForm: TMedianPolishForm AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = Fact1Out AnchorSideBottom.Side = asrBottom - Left = 253 + Left = 258 Height = 23 Top = 132 - Width = 201 + Width = 206 Anchors = [akLeft, akRight, akBottom] BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 12 + ReadOnly = True TabOrder = 7 - Text = 'Edit1' + Text = 'Factor1' end object Factor2: TEdit AnchorSideLeft.Control = Fact2In @@ -280,22 +264,23 @@ object MedianPolishForm: TMedianPolishForm AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = Fact2Out AnchorSideBottom.Side = asrBottom - Left = 253 + Left = 258 Height = 23 Top = 215 - Width = 201 + Width = 206 Anchors = [akLeft, akRight, akBottom] BorderSpacing.Left = 8 BorderSpacing.Right = 8 BorderSpacing.Bottom = 12 + ReadOnly = True TabOrder = 10 - Text = 'Edit1' + Text = 'Factor2' end object Label4: TLabel AnchorSideLeft.Control = Fact2Out AnchorSideTop.Control = Fact2Out AnchorSideTop.Side = asrBottom - Left = 217 + Left = 222 Height = 15 Top = 274 Width = 81 @@ -308,10 +293,11 @@ object MedianPolishForm: TMedianPolishForm AnchorSideLeft.Side = asrBottom AnchorSideTop.Control = Label4 AnchorSideTop.Side = asrCenter - Left = 306 + Left = 311 Height = 23 Top = 270 Width = 35 + Alignment = taRightJustify BorderSpacing.Left = 8 TabOrder = 11 Text = '5' @@ -319,7 +305,7 @@ object MedianPolishForm: TMedianPolishForm object StaticText1: TStaticText AnchorSideLeft.Control = DepVar AnchorSideBottom.Control = DepVar - Left = 253 + Left = 258 Height = 15 Top = 31 Width = 73 @@ -331,7 +317,7 @@ object MedianPolishForm: TMedianPolishForm object Label2: TLabel AnchorSideLeft.Control = Factor1 AnchorSideBottom.Control = Factor1 - Left = 253 + Left = 258 Height = 15 Top = 115 Width = 42 @@ -343,7 +329,7 @@ object MedianPolishForm: TMedianPolishForm object Label3: TLabel AnchorSideLeft.Control = Factor2 AnchorSideBottom.Control = Factor2 - Left = 253 + Left = 258 Height = 15 Top = 198 Width = 42 @@ -352,32 +338,31 @@ object MedianPolishForm: TMedianPolishForm Caption = 'Factor 2' ParentColor = False end - end - object ItersBtn: TRadioButton - AnchorSideLeft.Control = NormChk - AnchorSideLeft.Side = asrBottom - AnchorSideTop.Control = NormChk - AnchorSideTop.Side = asrCenter - Left = 258 - Height = 19 - Top = 340 - Width = 182 - BorderSpacing.Left = 16 - BorderSpacing.Right = 8 - Caption = 'Show Results for Each Iteration' - TabOrder = 2 - Visible = False - end - object NormChk: TCheckBox - AnchorSideLeft.Control = Owner - AnchorSideBottom.Control = Bevel1 - Left = 8 - Height = 19 - Top = 340 - Width = 234 - Anchors = [akLeft, akBottom] - BorderSpacing.Left = 8 - Caption = 'Show Bill Miller''s Normalizing Procedure' - TabOrder = 1 + object NormChk: TCheckBox + AnchorSideLeft.Control = DepIn + AnchorSideTop.Control = MaxEdit + AnchorSideTop.Side = asrBottom + Left = 222 + Height = 19 + Top = 305 + Width = 234 + BorderSpacing.Top = 12 + Caption = 'Show Bill Miller''s Normalizing Procedure' + TabOrder = 12 + end + object ItersBtn: TRadioButton + AnchorSideLeft.Control = DepIn + AnchorSideTop.Control = NormChk + AnchorSideTop.Side = asrBottom + Left = 222 + Height = 19 + Top = 328 + Width = 182 + BorderSpacing.Top = 4 + BorderSpacing.Right = 8 + Caption = 'Show Results for Each Iteration' + TabOrder = 13 + Visible = False + end end end diff --git a/applications/lazstats/source/forms/analysis/multivariate/medianpolishunit.pas b/applications/lazstats/source/forms/analysis/multivariate/medianpolishunit.pas index 923e1dd7c..99c93a9e2 100644 --- a/applications/lazstats/source/forms/analysis/multivariate/medianpolishunit.pas +++ b/applications/lazstats/source/forms/analysis/multivariate/medianpolishunit.pas @@ -1,3 +1,7 @@ +// wp: Unit not tested due to missing documentation +// Tried to reproduce https://www.youtube.com/watch?v=RtC9ZMOYgk8 +// --> does not work... + unit MedianPolishUnit; {$mode objfpc}{$H+} @@ -21,10 +25,9 @@ type ItersBtn: TRadioButton; Panel1: TPanel; ResetBtn: TButton; - CancelBtn: TButton; ComputeBtn: TButton; - ReturnBtn: TButton; - DepIn1: TBitBtn; + CloseBtn: TButton; + DepIn: TBitBtn; DepOut: TBitBtn; DepVar: TEdit; Fact1In: TBitBtn; @@ -39,7 +42,7 @@ type StaticText1: TStaticText; VarList: TListBox; procedure ComputeBtnClick(Sender: TObject); - procedure DepIn1Click(Sender: TObject); + procedure DepInClick(Sender: TObject); procedure DepOutClick(Sender: TObject); procedure Fact1InClick(Sender: TObject); procedure Fact1OutClick(Sender: TObject); @@ -48,22 +51,25 @@ type procedure FormActivate(Sender: TObject); procedure FormCreate(Sender: TObject); procedure ResetBtnClick(Sender: TObject); + procedure VarListSelectionChange(Sender: TObject; User: boolean); private { private declarations } FAutoSized: boolean; - CumRowResiduals : DblDyneVec; - CumColResiduals : DblDyneVec; - function Median(VAR X : DblDyneVec; size : integer) : double; - procedure PrintObsTable(ObsTable : DblDyneMat; nrows, ncols : integer); - procedure PrintResults(ObsTable : DblDyneMat; rowmedian,rowresid : DblDyneVec; - comedian, colresid : DblDyneVec; nrows, ncols : integer); - procedure sortvalues(VAR X : DblDyneVec; size : integer); - procedure TwoWayPlot(NF1cells : integer; RowSums : DblDyneVec; - graphtitle : string; Heading : string); - procedure InteractPlot(NF1cells, NF2Cells : integer; - ObsTable :DblDyneMat; graphtitle : string; - Heading : string); + CumRowResiduals: DblDyneVec; + CumColResiduals: DblDyneVec; + function Median(const X: DblDyneVec; ASize: integer): double; + procedure PrintObsTable(const ObsTable: DblDyneMat; NRows, NCols: integer; + AReport: TStrings); + procedure PrintResults(const ObsTable: DblDyneMat; const + RowMedian, RowResid, ColMedian, ColResid: DblDyneVec; + NRows, NCols: integer; AReport: TStrings); + procedure SortValues(const X: DblDyneVec; ASize: integer); + procedure TwoWayPlot(NF1cells: integer; const RowSums: DblDyneVec; + const AGraphTitle, AHeading: string); + procedure InteractPlot(NF1cells, NF2Cells: integer; const ObsTable: DblDyneMat; + const AGraphTitle, AHeading: string); + procedure UpdateBtnStates; public { public declarations } end; @@ -74,31 +80,27 @@ var implementation uses - Math; + Math, Utils; { TMedianPolishForm } procedure TMedianPolishForm.ResetBtnClick(Sender: TObject); -var i : integer; +var + i: integer; begin VarList.Clear; for i := 1 to NoVariables do - VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); + VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); DepVar.Text := ''; Factor1.Text := ''; Factor2.Text := ''; - DepIn1.Enabled := true; - DepOut.Enabled := false; - Fact1In.Enabled := true; - Fact1Out.Enabled := false; - Fact2In.Enabled := true; - Fact2out.Enabled := false; ItersBtn.Checked := false; NormChk.Checked := false; + UpdateBtnStates; end; procedure TMedianPolishForm.ComputeBtnClick(Sender: TObject); -VAR +var NoSelected, DepVarCol, F1Col, F2Col, i, j, k : integer; minrow, maxrow, mincol, maxcol : integer; intvalue, xrange, yrange, row, col, N, count, iteration : integer; @@ -122,547 +124,590 @@ VAR WholeTable : DblDyneVec; RowEffects : DblDyneVec; ColEffects : DblDyneVec; + floatValue: Double; + lReport: TStrings; begin - OutputFrm.RichEdit.Clear; - for i := 1 to NoVariables do - begin - cellstring := Trim(OS3MainFrm.DataGrid.Cells[i,0]); - if cellstring = DepVar.Text then DepVarCol := i; - if cellstring = Factor1.Text then F1Col := i; - if cellstring = Factor2.Text then F2Col := i; - end; - NoSelected := 3; - SetLength(ColNoSelected,3); - ColNoSelected[0] := DepVarCol; - ColNoSelected[1] := F1Col; - ColNoSelected[2] := F2Col; - // get no. of rows and columns (Factor 1 and Factor 2) - mincol := 10000; - maxcol := 0; - minrow := 10000; - maxrow := 0; - for i := 1 to NoCases do - begin - intvalue := StrToInt(Trim(OS3MainFrm.DataGrid.Cells[F1Col,i])); - if intvalue > maxrow then maxrow := intvalue; - if intvalue < minrow then minrow := intvalue; - intvalue := StrToInt(Trim(OS3MainFrm.DataGrid.Cells[F2Col,i])); - if intvalue > maxcol then maxcol := intvalue; - if intvalue < mincol then mincol := intvalue; - end; - xrange := maxrow - minrow + 1; - yrange := maxcol - mincol + 1; + if DepVar.Text = '' then + begin + MessageDlg('No dependant variable selected.', mtError, [mbOK], 0); + exit; + end; + if Factor1.Text = '' then + begin + MessageDlg('No Factor 1 variable selected.', mtError, [mbOK], 0); + exit; + end; + if Factor2.Text = '' then + begin + MessageDlg('No Factor 2 variable selected.', mtError, [mbOK], 0); + exit; + end; + if MaxEdit.Text = '' then + begin + MaxEdit.SetFocus; + MessageDlg('Max. iterations not specified.', mtError, [mbOK], 0); + exit; + end; + if not TryStrToInt(MaxEdit.Text, NoIterations) or (NoIterations < 0) then + begin + MaxEdit.SetFocus; + MessageDlg('Invalid input for Max iterations.', mtError, [mbOk], 0); + exit; + end; + + lReport := TStringList.Create; + try + for i := 1 to NoVariables do + begin + cellstring := Trim(OS3MainFrm.DataGrid.Cells[i,0]); + if cellstring = DepVar.Text then DepVarCol := i; + if cellstring = Factor1.Text then F1Col := i; + if cellstring = Factor2.Text then F2Col := i; + end; + NoSelected := 3; + SetLength(ColNoSelected,3); + ColNoSelected[0] := DepVarCol; + ColNoSelected[1] := F1Col; + ColNoSelected[2] := F2Col; + + // get no. of rows and columns (Factor 1 and Factor 2) + mincol := 10000; + maxcol := 0; + minrow := 10000; + maxrow := 0; + for i := 1 to NoCases do + begin + if TryStrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F1Col,i]), floatValue) and + (Frac(floatValue) = 0) then + begin + intValue := trunc(floatValue); + if intvalue > maxrow then maxrow := intvalue; + if intvalue < minrow then minrow := intvalue; + end else + begin + MessageDlg(Format('Integer value expected in cell at column %d and cell %d', [F1Col, i]), mtError, [mbOK], 0); + exit; + end; + + if TryStrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F2Col,i]), floatValue) and + (Frac(floatvalue) = 0) then + begin + intValue := trunc(floatValue); + if intvalue > maxcol then maxcol := intvalue; + if intvalue < mincol then mincol := intvalue; + end else + begin + MessageDlg(Format('Integer value expected in cell at column %d and cell %d', [F2Col, i]), mtError, [mbOK], 0); + exit; + end; + end; + + xrange := maxrow - minrow + 1; + yrange := maxcol - mincol + 1; + // get no. of observations in each cell - SetLength(CellCount,xrange,yrange); - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - CellCount[i,j] := 0; - end; - end; - count := 0; - single := false; - for i := 1 to NoCases do - begin - row := StrToInt(Trim(OS3MainFrm.DataGrid.Cells[F1Col,i])); - row := row - minrow; - col := StrToInt(Trim(OS3MainFrm.DataGrid.Cells[F2Col,i])); - col := col - mincol; - CellCount[row,col] := CellCount[row,col] + 1; + SetLength(CellCount,xrange,yrange); + for i := 0 to xrange-1 do + for j := 0 to yrange-1 do + CellCount[i,j] := 0; + + count := 0; + for i := 1 to NoCases do + begin + row := trunc(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F1Col,i]))); + row := row - minrow; + col := trunc(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F2Col,i]))); + col := col - mincol; + CellCount[row,col] := CellCount[row,col] + 1; + count := count + 1; + end; + single := (count = (xrange * yrange)); + + SetLength(Observed, NoCases, xrange, yrange); + SetLength(Residuals, NoCases, xrange, yrange); + SetLength(RowResiduals, xrange); + SetLength(ColResiduals, yrange); + SetLength(GroupScores, NoCases); + SetLength(RowMedian, xrange); + SetLength(ColMedian, yrange); + SetLength(CumRowResiduals, xrange); + SetLength(CumColResiduals, yrange); + SetLength(WholeTable, xrange * yrange); + SetLength(RowEffects, xrange); + SetLength(ColEffects, yrange); + + for i := 0 to NoCases-1 do + begin + for j := 0 to xrange-1 do + for k := 0 to yrange-1 do + begin + Observed[i,j,k] := 0.0; + Residuals[i,j,k] := 0.0; + end; + end; + + for j := 0 to xrange-1 do + begin + RowResiduals[j] := 0.0; + CumRowResiduals[j] := 0.0; + end; + + for j := 0 to yrange-1 do + begin + ColResiduals[j] := 0.0; + CumColResiduals[j] := 0.0; + end; + + // Get observed scores + for i := 0 to xrange-1 do + for j := 0 to yrange-1 do + CellCount[i,j] := 0; + for i := 1 to NoCases do + begin + row := trunc(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F1Col,i]))) - minrow; + col := trunc(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[F2Col,i]))) - minCol; + X := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[DepVarCol,i])); + CellCount[row, col] := CellCount[row, col] + 1; + N := CellCount[row, col]; + Observed[N-1, row, col] := X; + end; + + // if not single case in each cell, obtain median for each cell + if not single then + begin + for i := 0 to xrange-1 do + begin + for j := 0 to yrange-1 do + begin + for k := 0 to CellCount[i,j]-1 do + GroupScores[k] := Observed[k,i,j]; + M := Median(GroupScores,CellCount[i,j]); + Observed[0,i,j] := M; + end; + end; + end; + + SetLength(ObsTable, xrange, yrange); + k := 0; + for i := 0 to xrange-1 do + begin + for j := 0 to yrange-1 do + begin + ObsTable[i,j] := Observed[0,i,j]; + WholeTable[k] := Observed[0,i,j]; + k := k + 1; + end; + end; + + SortValues(WholeTable, xrange * yrange); + Q1 := Quartiles(2, 0.25, xrange*yrange, WholeTable); + Q3 := Quartiles(2, 0.75, xrange*yrange, WholeTable); + QRange1 := Q3 - Q1; + lReport.Add('Quartiles of original data: %8.3f and %.3f', [Q1,Q3]); + lReport.Add(''); + lReport.Add(DIVIDER); + lReport.Add(''); + + // Bill Miller's solution + + if NormChk.Checked then + begin + lReport.Add('BILL MILLER''s SOLUTION'); + lReport.Add(''); + // get deviations of each cell from the grand mean, row and column residuals + // and row and column absolute deviations + k := 0; + for i := 0 to xrange-1 do + for j := 0 to yrange-1 do + begin + ObsTable[i,j] := Observed[0,i,j]; + WholeTable[k] := Observed[0,i,j]; + k := k + 1; + end; + + SortValues(WholeTable, xrange * yrange); + GrandMedian := Median(WholeTable, xrange * yrange); + + lReport.Add('Grand Median: %9.3f', [GrandMedian]); + lReport.Add(''); + + PrintObsTable(ObsTable, xrange, yrange, lReport); + + lReport.Add(''); + lReport.Add(DIVIDER); + lReport.Add(''); + + for i := 0 to xrange-1 do + begin + RowMedian[i] := 0.0; + RowResiduals[i] := 0.0; + CumRowResiduals[i] := 0.0; + end; + for j := 0 to yrange-1 do + begin + ColMedian[j] := 0.0; + ColResiduals[j] := 0.0; + CumColResiduals[j] := 0.0; + end; + + for i := 0 to xrange-1 do + begin + for j := 0 to yrange-1 do + GroupScores[j] := ObsTable[i,j]; + SortValues(GroupScores, yrange); + RowMedian[i] := Median(GroupScores, yrange); + end; + + for i := 0 to xrange-1 do + begin + for j := 0 to yrange-1 do + RowResiduals[i] := RowResiduals[i] + (ObsTable[i,j] - RowMedian[i]); + CumRowResiduals[i] := CumRowResiduals[i] + abs(RowResiduals[i]); + end; + + for j := 0 to yrange-1 do + begin + for i := 0 to xrange-1 do + GroupScores[i] := ObsTable[i,j]; + SortValues(GroupScores, xrange); + ColMedian[j] := Median(GroupScores, xrange); + end; + + for j := 0 to yrange-1 do + begin + for i := 0 to xrange-1 do + ColResiduals[j] := ColResiduals[j] + (ObsTable[i,j] - ColMedian[j]); + CumColResiduals[j] := CumColResiduals[j] + abs(ColResiduals[j]); + end; + + PrintResults(ObsTable, RowMedian, RowResiduals, ColMedian, ColResiduals, xrange, yrange, lReport); + + lReport.Add(''); + lReport.Add(DIVIDER); + lReport.Add(''); + + // TwoWayPlot(xrange, RowMedian,'Rows','ROW MEDIANS'); + // TwoWayPlot(yrange, ColMedian,'Columns','COL. MEDIANS'); + + // Normalize medians and raw data + // This will result in the sum of column, row and table residuals all + // summing to zero. The model is X = Total Median + Row effects + + // col. effects + interaction effects and the row, col and interaction + // effects each sum to zero (as in ANOVA) + TableSum := 0.0; + scale := 0; + for i := 0 to xrange-1 do + for j := 0 to yrange-1 do + scale := scale + ObsTable[i,j]; + scale := scale / (xrange * yrange); + for i := 0 to xrange-1 do + for j := 0 to yrange-1 do + begin + ObsTable[i,j] := ObsTable[i,j] - scale; + TableSum := TableSum + abs(ObsTable[i,j]); + end; + + lReport.Add('Normalized Data'); + PrintObsTable(ObsTable, xrange, yrange, lReport); + lReport.Add(''); + + scale := 0; + for i := 0 to xrange-1 do scale := scale + RowMedian[i]; + scale := scale / xrange; + for i := 0 to xrange-1 do RowMedian[i] := RowMedian[i] - scale; + scale := 0; + for j := 0 to yrange-1 do scale := scale + ColMedian[j]; + scale := scale / yrange; + for j := 0 to yrange-1 do ColMedian[j] := ColMedian[j] - scale; + + lReport.Add('Normalized Adjusted Data'); + PrintResults(ObsTable, RowMedian, RowResiduals, ColMedian, ColResiduals, xrange, yrange, lReport); + lReport.Add(''); + + for i := 0 to xrange-1 do + for j := 0 to yrange-1 do + ObsTable[i,j] := ObsTable[i,j] - (RowMedian[i] + ColMedian[j]); + + lReport.Add('Normalized Table minus Row and Column Medians'); + PrintObsTable(ObsTable, xrange, yrange, lReport); + lReport.Add(''); + + for i := 0 to xrange-1 do RowResiduals[i] := 0.0; + for j := 0 to yrange-1 do ColResiduals[j] := 0.0; + TotResid := 0.0; + for i := 0 to xrange-1 do + for j := 0 to yrange-1 do + begin + RowResiduals[i] := RowResiduals[i] + ObsTable[i,j]; + ColResiduals[j] := ColResiduals[j] + ObsTable[i,j]; + TotResid := TotResid + ObsTable[i,j]; + end; + + lReport.Add('Normalized Adjusted Data'); + PrintResults(ObsTable, RowMedian, RowResiduals, ColMedian, ColResiduals, xrange, yrange, lReport); + lReport.Add(''); + lReport.Add('Total Table Residuals: %8.3f', [TotResid]); + lReport.Add(''); + + SumAbsRows := 0.0; + SumAbsCols := 0.0; + SumAbsTable := 0.0; + for i := 0 to xrange-1 do + SumAbsRows := SumAbsRows + abs(RowMedian[i]); + for j := 0 to yrange-1 do + SumAbsCols := SumAbsCols + abs(ColMedian[j]); + for i := 0 to xrange - 1 do + for j := 0 to yrange - 1 do + SumAbsTable := SumAbsTable + abs(ObsTable[i,j]); + lReport.Add('Absolute Sums of Row: %8.3f', [SumAbsRows]); + lReport.Add(' of Col: %8.3f', [SumAbsCols]); + lReport.Add(' of Interactions: %8.3f', [SumAbsTable]); + total := SumAbsRows + SumAbsCols + SumAbsTable; + lReport.Add('Absolute Sums of Table Values prior to Extracting Row and Col.: %8.3f', [TableSum]); + lReport.Add('Percentages explained by rows: %8.3f %%', [100.0 * SumAbsRows / total]); + lReport.Add(' cols: %8.3f %%', [100.0 * SumAbscols / total]); + lReport.Add(' interactions plus error: %8.3f %%', [100*SumAbsTable/total]); + + explained := 100*SumAbsRows/total + 100*SumAbsCols/total + 100*SumAbsTable/total; + lReport.Add('Percentage explained: %8.3f %%', [explained]); + + lReport.Add(''); + lReport.Add(DIVIDER); + lReport.Add(''); + + TwoWayPlot(xrange, RowMedian, 'Rows', 'ROW MEDIANS'); + TwoWayPlot(yrange, ColMedian, 'Columns', 'COL. MEDIANS'); + end; // Bills method + + // Now do traditional median smoothing + lReport.Add('TUKEY ITERATIVE MEDIAN SMOOTHING METHOD'); + lReport.Add(''); + + done := false; + iteration := 1; + for i := 0 to xrange-1 do + RowEffects[i] := 0.0; + for j := 0 to yrange-1 do + ColEffects[j] := 0.0; + while not done do + begin + // Get residuals from the median for each row + count := 0; + for i := 0 to xrange-1 do + begin + count := 0; + for j := 0 to yrange-1 do + begin + GroupScores[count] := Observed[0,i,j]; count := count + 1; - end; - if count = (xrange * yrange) then single := true; - SetLength(Observed,NoCases,xrange,yrange); - SetLength(Residuals,NoCases,xrange,yrange); - SetLength(RowResiduals,xrange); - SetLength(ColResiduals,yrange); - SetLength(GroupScores,NoCases); - SetLength(RowMedian,xrange); - SetLength(ColMedian,yrange); - SetLength(CumRowResiduals,xrange); - SetLength(CumColResiduals,yrange); - SetLength(WholeTable,xrange * yrange); - SetLength(RowEffects,xrange); - SetLength(ColEffects,yrange); + end; + SortValues(GroupScores, count); + M := Median(GroupScores, count); + RowMedian[i] := M; + for j := 0 to yrange-1 do Observed[0,i,j] := Observed[0,i,j] - M; + for j := 0 to yrange-1 do RowResiduals[i] := RowResiduals[i] + Observed[0,i,j]; + CumRowResiduals[i] := CumRowResiduals[i] + abs(RowResiduals[i]); + end; - for i := 0 to NoCases-1 do - begin - for j := 0 to xrange-1 do - begin - for k := 0 to yrange-1 do - begin - Observed[i,j,k] := 0.0; - Residuals[i,j,k] := 0.0; - end; - end; - end; - for j := 0 to xrange-1 do - begin - RowResiduals[j] := 0.0; - CumRowResiduals[j] := 0.0; - end; - for j := 0 to yrange-1 do - begin - ColResiduals[j] := 0.0; - CumColResiduals[j] := 0.0; - end; - // Get observed scores - for i := 0 to xrange-1 do - begin + // get sum of residuals for cols + count := 0; + for i := 0 to yrange-1 do + begin + count := 0; + for j := 0 to xrange-1 do + begin + GroupScores[count] := Observed[0,j,i]; + count := count + 1; + end; + SortValues(GroupScores, count); + M := Median(GroupScores, count); + ColMedian[i] := M; + for j := 0 to xrange-1 do Observed[0,j,i] := Observed[0,j,i] - M; + for j := 0 to xrange-1 do ColResiduals[i] := ColResiduals[i] + Observed[0,j,i]; + CumColResiduals[i] := CumColResiduals[i] + abs(ColResiduals[i]); + end; + + // build table of results + for i := 0 to xrange-1 do + for j := 0 to yrange-1 do + ObsTable[i,j] := Observed[0,i,j]; // Residuals[0,i,j]; + +// if ItersBtn.Checked then +// begin + lReport.Add('Iteration %d', [iteration]); + PrintResults(ObsTable, RowMedian, RowResiduals, ColMedian, ColResiduals, xrange, yrange, lReport); + lReport.Add(''); + + lReport.Add('Row Effects'); + for i := 0 to xrange-1 do + GroupScores[i] := RowMedian[i]; + + SortValues(GroupScores, xrange); + M := Median(GroupScores, xrange); + lReport.Add('Overall Median: %8.3f', [M]); + lReport.Add(''); + + for i := 0 to xrange-1 do + begin + RowEffects[i] := RowEffects[i] + (RowMedian[i] - M); + lReport.Add('Row %3d Effect: %8.3f', [i+1, RowEffects[i]]); + end; + lReport.Add(''); + + lReport.Add('Column Effects'); + for j := 0 to yrange-1 do + begin + ColEffects[j] := ColEffects[j] + ColMedian[j]; + lReport.Add('Col. %3d Effect: %8.3f', [j+1, ColEffects[j]]); + end; + lReport.Add(''); +// OutputFrm.ShowModal; +// OutputFrm.RichEdit.Clear; +// end; + + for i := 0 to xrange-1 do RowResiduals[i] := 0.0; + for j := 0 to yrange-1 do ColResiduals[j] := 0.0; + NoIterations := NoIterations - 1; + iteration := iteration + 1; + if NoIterations = 0 then done := true; + sumrowmedians := 0.0; + sumcolmedians := 0.0; + for i := 0 to xrange-1 do sumrowmedians := sumrowmedians + RowMedian[i]; + for i := 0 to yrange-1 do sumcolmedians := sumcolmedians + ColMedian[i]; + if (sumrowmedians + sumcolmedians) = 0.0 then done := true; + if done then + begin + lReport.Add('SUMMARY OF THE ANALYSIS'); + PrintResults(ObsTable, RowMedian, RowResiduals, ColMedian, ColResiduals, xrange, yrange, lReport); + for i := 0 to xrange-1 do + begin + RowEffects[i] := RowEffects[i] + (RowMedian[i] - M); + lReport.Add('Row %3d Effect: %8.3f', [i+1, RowEffects[i]]); + end; + lReport.Add(''); + lReport.Add('Column Effects'); + for j := 0 to yrange-1 do + begin + ColEffects[j] := ColEffects[j] + ColMedian[j]; + lReport.Add('Col. %3d Effect: %8.3f', [j+1, ColEffects[j]]); + end; + lReport.Add(''); + + k := 0; + for i := 0 to xrange-1 do for j := 0 to yrange-1 do begin - CellCount[i,j] := 0; - end; - end; - for i := 1 to NoCases do - begin - row := StrToInt(Trim(OS3MainFrm.DataGrid.Cells[F1Col,i])); - row := row - minrow; - col := StrToInt(Trim(OS3MainFrm.DataGrid.Cells[F2Col,i])); - col := col - mincol; - X := StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[DepVarCol,i])); - CellCount[row,col] := CellCount[row,col] + 1; - N := CellCount[row,col]; - Observed[N-1,row,col] := X; - end; - - // if not single case in each cell, obtain median for each cell - if not single then - begin - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - for k := 0 to CellCount[i,j]-1 do - begin - GroupScores[k] := Observed[k,i,j]; - end; - M := Median(GroupScores,CellCount[i,j]); - Observed[0,i,j] := M; - end; - end; - end; - SetLength(ObsTable,xrange,yrange); - k := 0; - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - ObsTable[i,j] := Observed[0,i,j]; - WholeTable[k] := Observed[0,i,j]; - k := k + 1; - end; - end; - sortvalues(WholeTable,xrange*yrange); - Q1 := Quartiles(2,0.25,xrange*yrange,WholeTable); - Q3 := Quartiles(2,0.75,xrange*yrange,WholeTable); - Qrange1 := Q3 - Q1; - cellstring := format('Quartiles of original data = %8.3f %8.3f',[Q1,Q3]); - OutputFrm.RichEdit.Lines.Add(cellstring); - - if NormChk.Checked = true then - begin - // Bill Miller's solution - // get deviations of each cell from the grand mean, row and column residuals - // and row and column absolute deviations - k := 0; - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - ObsTable[i,j] := Observed[0,i,j]; - WholeTable[k] := Observed[0,i,j]; - k := k + 1; - end; - end; - sortvalues(WholeTable,xrange*yrange); - M := Median(WholeTable,xrange*yrange); - GrandMedian := M; -// OutputFrm.RichEdit.Clear; - cellstring := Format('Grand Median = %9.3f',[M]); - OutputFrm.RichEdit.Lines.Add(cellstring); - OutputFrm.RichEdit.Lines.Add(''); - PrintObsTable(ObsTable,xrange,yrange); - OutputFrm.ShowModal; - OutputFrm.RichEdit.Clear; - for i := 0 to xrange-1 do - begin - RowMedian[i] := 0.0; - RowResiduals[i] := 0.0; - CumRowResiduals[i] := 0.0; - end; - for j := 0 to yrange-1 do - begin - ColMedian[j] := 0.0; - ColResiduals[j] := 0.0; - CumColResiduals[j] := 0.0; - end; - - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - GroupScores[j] := ObsTable[i,j]; - end; - sortvalues(GroupScores,yrange); - M := Median(GroupScores,yrange); - RowMedian[i] := M; - end; - - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - RowResiduals[i] := RowResiduals[i] + (ObsTable[i,j] - RowMedian[i]); - end; - CumRowResiduals[i] := CumRowResiduals[i] + abs(RowResiduals[i]); - end; - - for j := 0 to yrange-1 do - begin - for i := 0 to xrange-1 do - begin - GroupScores[i] := ObsTable[i,j]; - end; - sortvalues(GroupScores,xrange); - M := Median(GroupScores,xrange); - ColMedian[j] := M; - end; - - for j := 0 to yrange-1 do - begin - for i := 0 to xrange-1 do - begin - ColResiduals[j] := ColResiduals[j] + (ObsTable[i,j] - ColMedian[j]); - end; - CumColResiduals[j] := CumColResiduals[j] + abs(ColResiduals[j]); - end; - PrintResults(ObsTable,RowMedian,RowResiduals,ColMedian,ColResiduals,xrange,yrange); - OutputFrm.ShowModal; - OutputFrm.RichEdit.Clear; -// TwoWayPlot(xrange, RowMedian,'Rows','ROW MEDIANS'); -// TwoWayPlot(yrange, ColMedian,'Columns','COL. MEDIANS'); - // Normalize medians and raw data - // This will result in the sum of column, row and table residuals all - // summing to zero. The model is X = Total Median + Row effects + - // col. effects + interaction effects and the row, col and interaction - // effects each sum to zero (as in ANOVA) - TableSum := 0.0; - scale := 0; - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - scale := scale + ObsTable[i,j]; - end; - end; - scale := scale / (xrange * yrange); - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - ObsTable[i,j] := ObsTable[i,j] - scale; - TableSum := TableSum + abs(ObsTable[i,j]); - end; - end; - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Normalized Data'); - PrintObsTable(ObsTable,xrange,yrange); - scale := 0; - for i := 0 to xrange-1 do scale := scale + RowMedian[i]; - scale := scale / xrange; - for i := 0 to xrange-1 do RowMedian[i] := RowMedian[i] - scale; - scale := 0; - for j := 0 to yrange-1 do scale := scale + ColMedian[j]; - scale := scale / yrange; - for j := 0 to yrange-1 do ColMedian[j] := ColMedian[j] - scale; - - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Normalized Adjusted Data'); - PrintResults(ObsTable,RowMedian,RowResiduals,ColMedian,ColResiduals,xrange,yrange); - OutputFrm.RichEdit.Lines.Add(''); - - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - ObsTable[i,j] := ObsTable[i,j] - (RowMedian[i] + ColMedian[j]); - end; - end; - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Normalized Table minus Row and Column Medians'); - PrintObsTable(ObsTable,xrange,yrange); - for i := 0 to xrange-1 do RowResiduals[i] := 0.0; - for j := 0 to yrange-1 do ColResiduals[j] := 0.0; - TotResid := 0.0; - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - RowResiduals[i] := RowResiduals[i] + ObsTable[i,j]; - ColResiduals[j] := ColResiduals[j] + ObsTable[i,j]; - TotResid := TotResid + ObsTable[i,j]; - end; - end; - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Normalized Adjusted Data'); - PrintResults(ObsTable,RowMedian,RowResiduals,ColMedian,ColResiduals,xrange,yrange); - OutputFrm.RichEdit.Lines.Add(''); - cellstring := format('Total Table Residuals = %8.3f',[TotResid]); - OutputFrm.RichEdit.Lines.Add(cellstring); - - SumAbsRows := 0.0; - SumAbsCols := 0.0; - SumAbsTable := 0.0; - for i := 0 to xrange-1 do SumAbsRows := SumAbsRows + abs(RowMedian[i]); - for j := 0 to yrange-1 do SumAbsCols := SumAbsCols + abs(ColMedian[j]); - for i := 0 to xrange - 1 do - for j := 0 to yrange - 1 do - SumAbsTable := SumAbsTable + abs(ObsTable[i,j]); - cellstring := format('Absolute Sums of Row, Col and Interactions = %8.3f %8.3f %8.3f', - [SumAbsRows, SumAbsCols, SumAbsTable]); - OutputFrm.RichEdit.Lines.Add(cellstring); - total := SumAbsRows + SumAbsCols + SumAbsTable; - cellstring := format('Absolute Sums of Table Values prior to Extracting Row and Col. = %8.3f', - [TableSum]); - OutputFrm.RichEdit.Lines.Add(cellstring); - cellstring := format('Percentages explained by rows, col.s, interactions plus error %8.3f %8.3f %8.3f', - [100*SumAbsRows/total, 100*SumAbsCols/total, 100*SumAbsTable/total]); - OutputFrm.RichEdit.Lines.Add(cellstring); - explained := 100*SumAbsRows/total + 100*SumAbsCols/total + 100*SumAbsTable/total; - cellstring := format('Percentage explained = %8.3f percent',[explained]); - OutputFrm.RichEdit.Lines.Add(cellstring); - OutputFrm.ShowModal; - OutputFrm.RichEdit.Clear; - - TwoWayPlot(xrange, RowMedian,'Rows','ROW MEDIANS'); - TwoWayPlot(yrange, ColMedian,'Columns','COL. MEDIANS'); - end; // Bills method - - // Now do traditional median smoothing - OutputFrm.RichEdit.Lines.Add('Tukey Iterative Median Smoothing Method'); - OutputFrm.RichEdit.Lines.Add(''); - NoIterations := StrToInt(MaxEdit.Text); - done := false; - iteration := 1; - for i := 0 to xrange-1 do RowEffects[i] := 0.0; - for j := 0 to yrange-1 do ColEffects[j] := 0.0; - while not done do - begin - // Get residuals from the median for each row - count := 0; - for i := 0 to xrange-1 do - begin - count := 0; - for j := 0 to yrange-1 do - begin - GroupScores[count] := Observed[0,i,j]; - count := count + 1; - end; - sortvalues(GroupScores,count); - M := Median(GroupScores,count); - RowMedian[i] := M; - for j := 0 to yrange-1 do Observed[0,i,j] := Observed[0,i,j] - M; - for j := 0 to yrange-1 do RowResiduals[i] := RowResiduals[i] + Observed[0,i,j]; - CumRowResiduals[i] := CumRowResiduals[i] + abs(RowResiduals[i]); + WholeTable[k] := ObsTable[i,j]; + k := k + 1; end; - // get sum of residuals for cols - count := 0; - for i := 0 to yrange-1 do - begin - count := 0; - for j := 0 to xrange-1 do - begin - GroupScores[count] := Observed[0,j,i]; - count := count + 1; - end; - sortvalues(GroupScores,count); - M := Median(GroupScores,count); - ColMedian[i] := M; - for j := 0 to xrange-1 do Observed[0,j,i] := Observed[0,j,i] - M; - for j := 0 to xrange-1 do ColResiduals[i] := ColResiduals[i] + Observed[0,j,i]; - CumColResiduals[i] := CumColResiduals[i] + abs(ColResiduals[i]); - end; + SortValues(WholeTable, xrange * yrange); + M := Median(WholeTable, xrange * yrange); + Q1 := Quartiles(2, 0.25, xrange * yrange, WholeTable); + Q3 := Quartiles(2, 0.75, xrange * yrange, WholeTable); + lReport.Add('Quartiles of the residuals: %8.3f and %.3f', [Q1, Q3]); + Qrange2 := Q3 - Q1; + lReport.Add('Original interquartile and final interquartile ranges: %8.3f and %.3f', [Qrange1, Qrange2]); + if QRange1 <> 0 then + lReport.Add('Quality of the additive fit: %8.3f %%', [100 * (QRange1 - QRange2) / QRange1]); + end; + end; // while not done - // build table of results - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - ObsTable[i,j] := Observed[0,i,j]; // Residuals[0,i,j]; - end; - end; + DisplayReport(lReport); -// if ItersBtn.Checked then -// begin - OutputFrm.RichEdit.Lines.Add(''); - cellstring := format('Iteration = %d',[iteration]); - OutputFrm.RichEdit.Lines.Add(cellstring); - PrintResults(ObsTable,RowMedian,RowResiduals,ColMedian,ColResiduals,xrange,yrange); - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Row Effects'); - for i := 0 to xrange-1 do - begin - GroupScores[i] := RowMedian[i]; - end; - sortvalues(GroupScores,xrange); - M := Median(GroupScores,xrange); - cellstring := format('Overall Median = %8.3f',[m]); - OutputFrm.RichEdit.Lines.Add(cellstring); - OutputFrm.RichEdit.Lines.Add(''); - for i := 0 to xrange-1 do - begin - RowEffects[i] := RowEffects[i] + (RowMedian[i] - M); - cellstring := format('Row %d Effect = %8.3f',[i+1,RowEffects[i]]); - OutputFrm.RichEdit.Lines.Add(cellstring); - end; - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Column Effects'); - for j := 0 to yrange-1 do - begin - ColEffects[j] := ColEffects[j] + ColMedian[j]; - cellstring := format('Col. %d Effect = %8.3f',[j+1,ColEffects[j]]); - OutputFrm.RichEdit.Lines.Add(cellstring); - end; -// OutputFrm.ShowModal; -// OutputFrm.RichEdit.Clear; -// end; - for i := 0 to xrange-1 do RowResiduals[i] := 0.0; - for j := 0 to yrange-1 do ColResiduals[j] := 0.0; - NoIterations := NoIterations - 1; - iteration := iteration + 1; - if NoIterations = 0 then done := true; - sumrowmedians := 0.0; - sumcolmedians := 0.0; - for i := 0 to xrange-1 do sumrowmedians := sumrowmedians + RowMedian[i]; - for i := 0 to yrange-1 do sumcolmedians := sumcolmedians + ColMedian[i]; - if (sumrowmedians + sumcolmedians) = 0.0 then done := true; - if done then - begin - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('SUMMARY OF THE ANALYSIS'); - PrintResults(ObsTable,RowMedian,RowResiduals,ColMedian,ColResiduals,xrange,yrange); - for i := 0 to xrange-1 do - begin - RowEffects[i] := RowEffects[i] + (RowMedian[i] - M); - cellstring := format('Row %d Effect = %8.3f',[i+1,RowEffects[i]]); - OutputFrm.RichEdit.Lines.Add(cellstring); - end; - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Column Effects'); - for j := 0 to yrange-1 do - begin - ColEffects[j] := ColEffects[j] + ColMedian[j]; - cellstring := format('Col. %d Effect = %8.3f',[j+1,ColEffects[j]]); - OutputFrm.RichEdit.Lines.Add(cellstring); - end; - k := 0; - OutputFrm.RichEdit.Lines.Add(''); - for i := 0 to xrange-1 do - begin - for j := 0 to yrange-1 do - begin - WholeTable[k] := ObsTable[i,j]; - k := k + 1; - end; - end; - sortvalues(WholeTable,xrange*yrange); - M := Median(WholeTable,xrange*yrange); - Q1 := Quartiles(2,0.25,xrange*yrange,WholeTable); - Q3 := Quartiles(2,0.75,xrange*yrange,WholeTable); - cellstring := format('Quartiles of the residuals = %8.3f %8.3f', - [Q1, Q3]); - OutputFrm.RichEdit.Lines.Add(cellstring); - Qrange2 := Q3 - Q1; - cellstring := format('Original interquartile and final interquartile ranges = %8.3f %8.3f', - [Qrange1, Qrange2]); - OutputFrm.RichEdit.Lines.Add(cellstring); - cellstring := format('Quality of the additive fit = %8.3f percent', - [100 * (Qrange1 - Qrange2) / Qrange1]); - OutputFrm.RichEdit.Lines.Add(cellstring); - OutputFrm.ShowModal; - OutputFrm.RichEdit.Clear; - end; - end; // while not done -// if ItersBtn.Checked then -// begin - TwoWayPlot(xrange, RowEffects,'Rows','CUMULATIVE ROW EFFECTS'); - TwoWayPlot(yrange, ColEffects,'Columns','CUMULATIVE COL. EFFECTS'); - InteractPlot(xrange, yrange, ObsTable, 'Interaction', - 'RESIDUALS OF ROWS AND COLUMNS'); -// end; - // cleanup - ColEffects := nil; - RowEffects := nil; - WholeTable := nil; - CumColResiduals := nil; - CumRowResiduals := nil; - ObsTable := nil; - ColMedian := nil; - RowMedian := nil; - GroupScores := nil; - CellCount := nil; - ColResiduals := nil; - RowResiduals := nil; - Residuals := nil; - Observed := nil; - ColNoSelected := nil; +// if ItersBtn.Checked then +// begin + TwoWayPlot(xrange, RowEffects,'Rows','CUMULATIVE ROW EFFECTS'); + TwoWayPlot(yrange, ColEffects,'Columns','CUMULATIVE COL. EFFECTS'); + InteractPlot(xrange, yrange, ObsTable, 'Interaction', 'RESIDUALS OF ROWS AND COLUMNS'); +// end; + finally + lReport.Free; + ColEffects := nil; + RowEffects := nil; + WholeTable := nil; + CumColResiduals := nil; + CumRowResiduals := nil; + ObsTable := nil; + ColMedian := nil; + RowMedian := nil; + GroupScores := nil; + CellCount := nil; + ColResiduals := nil; + RowResiduals := nil; + Residuals := nil; + Observed := nil; + ColNoSelected := nil; + end; end; -procedure TMedianPolishForm.DepIn1Click(Sender: TObject); -var index : integer; +procedure TMedianPolishForm.DepInClick(Sender: TObject); +var + index: integer; begin - index := VarList.ItemIndex; - DepVar.Text := VarList.Items.Strings[index]; + index := VarList.ItemIndex; + if (index > -1) and (DepVar.Text = '') then + begin + DepVar.Text := VarList.Items[index]; VarList.Items.Delete(index); - DepIn1.Enabled := false; - DepOut.Enabled := true;; + UpdateBtnStates; + end; end; procedure TMedianPolishForm.DepOutClick(Sender: TObject); begin - VarList.Items.Add(DepVar.Text); - DepVar.Text := ''; - DepIn1.Enabled := true; - DepOut.Enabled := false; + if DepVar.Text <> '' then + begin + VarList.Items.Add(DepVar.Text); + DepVar.Text := ''; + UpdateBtnStates; + end; end; procedure TMedianPolishForm.Fact1InClick(Sender: TObject); -var index : integer; +var + index: integer; begin - index := VarList.ItemIndex; - Factor1.Text := VarList.Items.Strings[index]; - VarList.Items.Delete(index); - Fact1In.Enabled := false; - Fact1Out.Enabled := true;; + index := VarList.ItemIndex; + if (index > -1) and (Factor1.Text = '') then + begin + Factor1.Text := VarList.Items[index]; + VarList.Items.Delete(index); + UpdateBtnStates; + end; end; procedure TMedianPolishForm.Fact1OutClick(Sender: TObject); begin - VarList.Items.Add(Factor1.Text); - Factor1.Text := ''; - Fact1In.Enabled := true; - Fact1Out.Enabled := false; + if Factor1.Text <> '' then + begin + VarList.Items.Add(Factor1.Text); + Factor1.Text := ''; + UpdateBtnStates; + end; end; procedure TMedianPolishForm.Fact2InClick(Sender: TObject); -VAR index : integer; +var + index: integer; begin - index := VarList.ItemIndex; - Factor2.Text := VarList.Items.Strings[index]; - VarList.Items.Delete(index); - Fact2In.Enabled := false; - Fact2Out.Enabled := true;; + index := VarList.ItemIndex; + if (index > -1) and (Factor2.Text = '') then + begin + Factor2.Text := VarList.Items[index]; + VarList.Items.Delete(index); + UpdateBtnStates; + end; end; procedure TMedianPolishForm.Fact2OutClick(Sender: TObject); begin - VarList.Items.Add(Factor2.Text); - Factor2.Text := ''; - Fact2In.Enabled := true; - Fact2Out.Enabled := false; + if Factor2.Text <> '' then + begin + VarList.Items.Add(Factor2.Text); + Factor2.Text := ''; + UpdateBtnStates; + end; end; procedure TMedianPolishForm.FormActivate(Sender: TObject); @@ -672,11 +717,12 @@ begin if FAutoSized then exit; - w := MaxValue([ResetBtn.Width, CancelBtn.Width, ComputeBtn.Width, ReturnBtn.Width]); + w := MaxValue([ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]); ResetBtn.Constraints.MinWidth := w; - CancelBtn.Constraints.MinWidth := w; ComputeBtn.Constraints.MinWidth := w; - ReturnBtn.Constraints.MinWidth := w; + CloseBtn.Constraints.MinWidth := w; + + Panel1.Constraints.MinWidth := NormChk.Width * 2 - DepIn.Width div 2; Constraints.MinWidth := Width; Constraints.MinHeight := Height; @@ -690,259 +736,240 @@ begin Application.CreateForm(TOutputFrm, OutputFrm); end; -function TMedianPolishForm.Median(VAR X : DblDyneVec; size : integer) : double; +function TMedianPolishForm.Median(const X: DblDyneVec; ASize: integer): double; var - midpt : integer; - value : double; + midpt: integer; begin -(* check for correct median calculation - OutputFrm.RichEdit.Lines.Add('Sorted values to get median'); - cellstring := format('size of array = %d',[size]); - OutputFrm.RichEdit.Lines.Add(cellstring); - for i := 0 to size do - begin - cellstring := format('no. %d = %9.3f',[i+1,X[i]]); - OutputFrm.RichEdit.Lines.Add(cellstring); - end; -*) - if size > 2 then - begin - midpt := size div 2; - if 2 * midpt = size then // even no. of values - begin - value := (X[midpt-1] + X[midpt]) / 2; - end - else value := X[midpt]; // odd no. of values - Median := value; - end - else if size = 2 then Median := (X[0] + X[1]) / 2; -// cellstring := format('Median = %9.3f',[value]); -// OutputFrm.ShowModal; + if ASize > 2 then + begin + midpt := ASize div 2; + if odd(ASize) then + Result := X[midPt] + else + Result := (X[midPt-1] + x[midPt]) / 2; + { + if 2 * midpt = ASize then // even no. of values + Result := (X[midpt-1] + X[midpt]) / 2 + else + Result := X[midpt]; // odd no. of values + } + end + else if ASize = 2 then + Result := (X[0] + X[1]) / 2; end; -procedure TMedianPolishForm.PrintObsTable(ObsTable : DblDyneMat; nrows, ncols : integer); -VAR - cellstring, outline : string; - i, j : integer; -begin - outline := 'Observed Data'; - OutputFrm.RichEdit.Lines.Add(outline); - outline := 'ROW COLUMNS'; - OutputFrm.RichEdit.Lines.Add(outline); - outline := ' '; - for i := 1 to ncols do - begin - outline := outline + format('%10d',[i]); - end; - OutputFrm.RichEdit.Lines.Add(outline); - for i := 1 to nrows do - begin - outline := format('%3d ',[i]); - for j := 1 to ncols do - begin - cellstring := format('%9.3f ',[ObsTable[i-1,j-1]]); - outline := outline + cellstring; - end; - OutputFrm.RichEdit.Lines.Add(outline); - end; -end; - - procedure TMedianPolishForm.PrintResults(ObsTable : DblDyneMat; - rowmedian,rowresid : DblDyneVec; - comedian, colresid : DblDyneVec; nrows, ncols : integer); +procedure TMedianPolishForm.PrintObsTable(const ObsTable: DblDyneMat; + NRows, NCols: integer; AReport: TStrings); var - i, j : integer; - cellstring, outline : string; + outline: string; + i, j: integer; begin - OutputFrm.RichEdit.Lines.Add(''); - outline := 'Adjusted Data'; - OutputFrm.RichEdit.Lines.Add(outline); - outline := 'MEDIAN'; - for i := 1 to ncols do - begin - outline := outline + format('%10d',[i]); - end; - outline := outline + ' Residuals'; - OutputFrm.RichEdit.Lines.Add(outline); - outline := '---------------------------------------------------------'; - OutputFrm.RichEdit.Lines.Add(outline); - for i := 0 to nrows-1 do - begin - cellstring := format('%9.3f ',[rowmedian[i]]); - outline := cellstring; - for j := 0 to ncols-1 do - begin - cellstring := format('%9.3f ',[ObsTable[i,j]]); - outline := outline + cellstring; - end; - cellstring := format('%9.3f ',[rowresid[i]]); - outline := outline + cellstring; - OutputFrm.RichEdit.Lines.Add(outline); - end; - outline := '---------------------------------------------------------'; - OutputFrm.RichEdit.Lines.Add(outline); - cellstring := 'Col.Resid.'; - outline := cellstring; - for j := 0 to ncols-1 do - begin - cellstring := format('%9.3f ',[colresid[j]]); - outline := outline + cellstring; - end; - OutputFrm.RichEdit.Lines.Add(outline); - cellstring := 'Col.Median'; - outline := cellstring; - for j := 0 to ncols-1 do - begin - cellstring := format('%9.3f ',[comedian[j]]); - outline := outline + cellstring; - end; - OutputFrm.RichEdit.Lines.Add(outline); - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Cumulative absolute value of Row Residuals'); - for j := 0 to nrows-1 do - begin - outline := format('Row = %d Cum.Residuals = %9.3f',[j+1,CumRowResiduals[j]]); - OutputFrm.RichEdit.Lines.Add(outline); - end; - OutputFrm.RichEdit.Lines.Add(''); - OutputFrm.RichEdit.Lines.Add('Cumulative absolute value of Column Residuals'); - for j := 0 to ncols-1 do - begin - outline := format('Column = %d Cum.Residuals = %9.3f',[j+1,CumColResiduals[j]]); - OutputFrm.RichEdit.Lines.Add(outline); - end; + AReport.Add('Observed Data'); + AReport.Add('ROW COLUMNS'); + + outline := ' '; + for i := 1 to NCols do + outline := outline + Format('%10d', [i]); + AReport.Add(outline); + + for i := 1 to NRows do + begin + outline := Format('%3d ', [i]); + for j := 1 to ncols do + outline := outline + Format('%9.3f ', [ObsTable[i-1, j-1]]); + AReport.Add(outline); + end; end; -procedure TMedianPolishForm.sortvalues(VAR X : DblDyneVec; size : integer); -VAR - i, j : integer; - temp : double; -begin - for i := 0 to size-2 do - begin - for j := i+1 to size-1 do - begin - if X[i] > X[j] then // swap - begin - temp := X[i]; - X[i] := X[j]; - X[j] := temp; - end; - end; - end; -// OutputFrm.RichEdit.Lines.Add('Sorted values'); -// for i := 0 to size-1 do -// begin -// cellstring := format('no. %d = %9.3f',[i+1,X[i]]); -// OutputFrm.RichEdit.Lines.Add(cellstring); -// end; -// OutputFrm.RichEdit.Lines.Add(''); -end; -//----------------------------------------------------------------------- -procedure TMedianPolishForm.TwoWayPlot(NF1cells : integer; - RowSums : DblDyneVec; graphtitle : string; Heading : string); +procedure TMedianPolishForm.PrintResults(const ObsTable: DblDyneMat; + const RowMedian, RowResid, ColMedian, ColResid: DblDyneVec; + NRows, NCols: integer; AReport: TStrings); var - i: integer; - minmean, maxmean: double; - XValue : DblDyneVec; - title : string; - plottype : integer; - setstring : string[11]; - + i, j: integer; + outline: string; begin - SetLength(XValue,Nf1cells); - plottype := 2; - setstring := 'Group'; - GraphFrm.SetLabels[1] := setstring; - maxmean := -10000.0; - minmean := 10000.0; - SetLength(GraphFrm.Xpoints,1,NF1cells); - SetLength(GraphFrm.Ypoints,1,NF1cells); - for i := 1 to NF1cells do - begin - GraphFrm.Ypoints[0,i-1] := RowSums[i-1]; - if RowSums[i-1] > maxmean then maxmean := RowSums[i-1]; - if RowSums[i-1] < minmean then minmean := RowSums[i-1]; - XValue[i-1] := i; - GraphFrm.Xpoints[0,i-1] := XValue[i-1]; - end; - GraphFrm.nosets := 1; - GraphFrm.nbars := NF1cells; - GraphFrm.Heading := Heading; - title := graphtitle; - GraphFrm.XTitle := title; - GraphFrm.YTitle := 'Y Values'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := minmean; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := clYellow; - GraphFrm.WallColor := clBlack; - GraphFrm.FloorColor := clLtGray; - GraphFrm.ShowBackWall := true; - GraphFrm.ShowModal; - GraphFrm.Xpoints := nil; - GraphFrm.Ypoints := nil; + AReport.Add('Adjusted Data'); + outline := 'MEDIAN'; + for i := 1 to NCols do + outline := outline + Format('%10d',[i]); + outline := outline + ' Residuals'; + AReport.Add(outline); + + AReport.Add('---------------------------------------------------------'); + + for i := 0 to NRows-1 do + begin + outline := Format('%9.3f ', [rowmedian[i]]); + for j := 0 to NCols-1 do + outline := outline + Format('%9.3f ', [ObsTable[i,j]]); + AReport.Add(outline + Format('%9.3f ', [RowResid[i]])); + end; + + AReport.Add('---------------------------------------------------------'); + + outline := 'Col.Resid.'; + for j := 0 to NCols-1 do + outline := outline + Format('%9.3f ', [ColResid[j]]); + AReport.Add(outline); + + outline := 'Col.Median'; + for j := 0 to NCols-1 do + outline := outline + Format('%9.3f ', [ColMedian[j]]); + AReport.Add(outline); + AReport.Add(''); + + AReport.Add('Cumulative absolute value of Row Residuals'); + for j := 0 to NRows-1 do + AReport.Add('Row %3d: Cum.Residuals: %9.3f', [j+1, CumRowResiduals[j]]); + AReport.Add(''); + + AReport.Add('Cumulative absolute value of Column Residuals'); + for j := 0 to NCols-1 do + AReport.Add('Column %3d: Cum.Residuals: %9.3f', [j+1, CumColResiduals[j]]); end; -procedure TMedianPolishForm.InteractPlot(NF1cells, NF2Cells : integer; - ObsTable :DblDyneMat; graphtitle : string; - Heading : string); -VAR - i, j : integer; - minmean, maxmean, XBar : double; - XValue: DblDyneVec; - title : string; - plottype : integer; - setstring : string[11]; - +procedure TMedianPolishForm.SortValues(const X: DblDyneVec; ASize: integer); +var + i, j: integer; begin - SetLength(GraphFrm.Ypoints,NF1cells,NF2cells); - SetLength(GraphFrm.Xpoints,1,NF2cells); - SetLength(XValue,Nf1cells+Nf2cells); - plottype := 2; - maxmean := -1e308; - minmean := 1e308; - for i := 1 to NF1cells do - begin - setstring := 'Row ' + IntToStr(i); - GraphFrm.SetLabels[i] := setstring; - for j := 1 to NF2cells do - begin - XBar := ObsTable[i-1,j-1]; - if XBar > maxmean then maxmean := XBar; - if XBar < minmean then minmean := XBar; - GraphFrm.Ypoints[i-1,j-1] := XBar; - end; - end; - for j := 1 to NF2cells do - begin - XValue[j-1] := j; - GraphFrm.Xpoints[0,j-1] := XValue[j-1]; - end; - - GraphFrm.nosets := NF1cells; - GraphFrm.nbars := NF2cells; - GraphFrm.Heading := 'Factor X x Factor Y'; - title := 'Column Codes'; - GraphFrm.XTitle := title; - GraphFrm.YTitle := 'Mean'; - GraphFrm.barwideprop := 0.5; - GraphFrm.AutoScaled := false; - GraphFrm.miny := minmean; - GraphFrm.maxy := maxmean; - GraphFrm.GraphType := plottype; - GraphFrm.BackColor := clYellow; - GraphFrm.WallColor := clBlack; - GraphFrm.FloorColor := clLtGray; - GraphFrm.ShowBackWall := true; - GraphFrm.ShowModal; - XValue := nil; - GraphFrm.Xpoints := nil; - GraphFrm.Ypoints := nil; + for i := 0 to ASize-2 do + for j := i+1 to ASize-1 do + if X[i] > X[j] then // swap + Exchange(X[i], X[j]); end; +procedure TMedianPolishForm.TwoWayPlot(NF1cells: integer; + const RowSums: DblDyneVec; const AGraphTitle, AHeading: string); +var + i: integer; + minmean, maxmean: double; +begin + GraphFrm.SetLabels[1] := 'Group'; + + SetLength(GraphFrm.Xpoints, 1, NF1cells); + SetLength(GraphFrm.Ypoints, 1, NF1cells); + maxmean := -1E308; + minmean := 1E308; + for i := 0 to NF1cells - 1 do + begin + GraphFrm.XPoints[0, i] := i + 1; + GraphFrm.YPoints[0, i] := RowSums[i]; + if RowSums[i] > maxMean then maxMean := RowSums[i]; + if RowSums[i] < minMean then minMean := RowSums[i]; + end; + { + for i := 1 to NF1cells do + begin + GraphFrm.Xpoints[0,i-1] := i; + GraphFrm.Ypoints[0,i-1] := RowSums[i-1]; + if RowSums[i-1] > maxmean then maxmean := RowSums[i-1]; + if RowSums[i-1] < minmean then minmean := RowSums[i-1]; + end; + } + + GraphFrm.NoSets := 1; + GraphFrm.NBars := NF1cells; + GraphFrm.Heading := AHeading; + GraphFrm.XTitle := AGraphTitle; + GraphFrm.YTitle := 'Y Values'; + GraphFrm.BarWideProp := 0.5; + GraphFrm.AutoScaled := false; + GraphFrm.miny := minmean; + GraphFrm.maxy := maxmean; + GraphFrm.GraphType := 2; + GraphFrm.BackColor := GRAPH_BACK_COLOR; + GraphFrm.WallColor := GRAPH_WALL_COLOR; + GraphFrm.FloorColor := GRAPH_WALL_COLOR; + GraphFrm.ShowBackWall := true; + + GraphFrm.ShowModal; + + GraphFrm.Xpoints := nil; + GraphFrm.Ypoints := nil; +end; + +procedure TMedianPolishForm.InteractPlot(NF1cells, NF2Cells: integer; + const ObsTable: DblDyneMat; const AGraphTitle, AHeading: string); +var + i, j: integer; + minmean, maxmean, XBar: double; +begin + SetLength(GraphFrm.Ypoints, NF1cells, NF2cells); + SetLength(GraphFrm.Xpoints,1, NF2cells); + + maxmean := -1e308; + minmean := 1e308; + for i := 0 to NF1Cells-1 do + begin + GraphFrm.SetLabels[i+1] := 'Row ' + IntToStr(i+1); + for j := 0 to NF2Cells - 1 do + begin + xbar := ObsTable[i, j]; + if xbar > maxMean then maxMean := xbar; + if xbar < minMean then minMean := xbar; + GraphFrm.YPoints[i, j] := xbar; + end; + end; + for j := 0 to NF2cells-1 do + GraphFrm.XPoints[0, j] := j+1; + + { + for i := 1 to NF1cells do + begin + GraphFrm.SetLabels[i] := 'Row ' + IntToStr(i); + for j := 1 to NF2cells do + begin + XBar := ObsTable[i-1,j-1]; + if XBar > maxmean then maxmean := XBar; + if XBar < minmean then minmean := XBar; + GraphFrm.Ypoints[i-1,j-1] := XBar; + end; + end; + for j := 1 to NF2cells do + begin + XValue[j-1] := j; + GraphFrm.Xpoints[0,j-1] := XValue[j-1]; + end; + } + + GraphFrm.nosets := NF1cells; + GraphFrm.nbars := NF2cells; + GraphFrm.Heading := 'Factor X x Factor Y'; + GraphFrm.XTitle := 'Column Codes'; + GraphFrm.YTitle := 'Mean'; + GraphFrm.barwideprop := 0.5; + GraphFrm.AutoScaled := false; + GraphFrm.miny := minmean; + GraphFrm.maxy := maxmean; + GraphFrm.GraphType := 2; + GraphFrm.BackColor := GRAPH_BACK_COLOR; + GraphFrm.WallColor := GRAPH_WALL_COLOR; + GraphFrm.FloorColor := GRAPH_FLOOR_COLOR; + GraphFrm.ShowBackWall := true; + + GraphFrm.ShowModal; + + GraphFrm.Xpoints := nil; + GraphFrm.Ypoints := nil; +end; + +procedure TMedianPolishForm.VarListSelectionChange(Sender: TObject; + User: boolean); +begin + UpdateBtnStates; +end; + +procedure TMedianPolishForm.UpdateBtnStates; +begin + DepIn.Enabled := (VarList.ItemIndex > -1) and (DepVar.Text = ''); + Fact1In.Enabled := (VarList.itemIndex > -1) and (Factor1.Text = ''); + Fact2In.Enabled := (VarList.ItemIndex > -1) and (Factor2.Text = ''); + DepOut.Enabled := (DepVar.Text <> ''); + Fact1Out.Enabled := (Factor1.Text <> ''); + Fact2Out.Enabled := (Factor2.Text <> ''); +end; + + initialization {$I medianpolishunit.lrs} diff --git a/applications/lazstats/source/units/globals.pas b/applications/lazstats/source/units/globals.pas index 278037c40..8674474b3 100644 --- a/applications/lazstats/source/units/globals.pas +++ b/applications/lazstats/source/units/globals.pas @@ -90,6 +90,9 @@ const DIVIDER = '==========================================================================='; + GRAPH_BACK_COLOR = clCream; + GRAPH_WALL_COLOR = clGray; + GRAPH_FLOOR_COLOR = clLtGray; implementation