From ed04056db40a8e78cf838c19f363f04d3b2c3636 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Sat, 12 Sep 2020 22:29:19 +0000 Subject: [PATCH] LazStats: Fix XBar Control Chart UCL/LCL values to agree with commercial JMP statistics software. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7664 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../basicspcunit.lfm | 31 +++-- .../basicspcunit.pas | 4 +- .../xbarchartunit.lfm | 106 +++++++++--------- .../xbarchartunit.pas | 73 ++++++++---- .../lazstats/source/frames/chartframeunit.lfm | 2 + 5 files changed, 121 insertions(+), 95 deletions(-) diff --git a/applications/lazstats/source/forms/analysis/statistical_process_control/basicspcunit.lfm b/applications/lazstats/source/forms/analysis/statistical_process_control/basicspcunit.lfm index f75123cb4..b43a6e0e4 100644 --- a/applications/lazstats/source/forms/analysis/statistical_process_control/basicspcunit.lfm +++ b/applications/lazstats/source/forms/analysis/statistical_process_control/basicspcunit.lfm @@ -11,11 +11,12 @@ object BasicSPCForm: TBasicSPCForm OnShow = FormShow LCLVersion = '2.1.0.0' object SpecsPanel: TPanel - Left = 0 + Left = 8 Height = 438 Top = 0 Width = 357 Align = alLeft + BorderSpacing.Left = 8 BorderSpacing.Right = 3 BevelOuter = bvNone ClientHeight = 438 @@ -111,11 +112,10 @@ object BasicSPCForm: TBasicSPCForm object VarListLabel: TLabel AnchorSideLeft.Control = SpecsPanel AnchorSideTop.Control = SpecsPanel - Left = 8 + Left = 0 Height = 15 Top = 8 Width = 97 - BorderSpacing.Left = 8 BorderSpacing.Top = 8 Caption = 'Selection Variables' ParentColor = False @@ -126,12 +126,11 @@ object BasicSPCForm: TBasicSPCForm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = MeasInBtn AnchorSideBottom.Control = ButtonPanel - Left = 8 + Left = 0 Height = 363 Top = 25 - Width = 150 + Width = 158 Anchors = [akTop, akLeft, akRight, akBottom] - BorderSpacing.Left = 8 BorderSpacing.Top = 2 BorderSpacing.Right = 8 BorderSpacing.Bottom = 8 @@ -266,17 +265,17 @@ object BasicSPCForm: TBasicSPCForm end end object SpecsSplitter: TSplitter - Left = 360 + Left = 368 Height = 438 Top = 0 Width = 5 ResizeStyle = rsPattern end object PageControl: TPageControl - Left = 368 + Left = 376 Height = 426 Top = 6 - Width = 553 + Width = 545 ActivePage = ReportPage Align = alClient BorderSpacing.Left = 3 @@ -288,24 +287,24 @@ object BasicSPCForm: TBasicSPCForm object ReportPage: TTabSheet Caption = 'Report' ClientHeight = 398 - ClientWidth = 545 + ClientWidth = 537 object Panel1: TPanel Left = 6 Height = 358 Top = 34 - Width = 533 + Width = 525 Align = alClient BorderSpacing.Around = 6 BevelOuter = bvNone BorderStyle = bsSingle ClientHeight = 354 - ClientWidth = 529 + ClientWidth = 521 TabOrder = 0 object ReportMemo: TMemo Left = 4 Height = 346 Top = 4 - Width = 521 + Width = 513 Align = alClient BorderSpacing.Around = 4 BorderStyle = bsNone @@ -320,7 +319,7 @@ object BasicSPCForm: TBasicSPCForm Left = 0 Height = 22 Top = 6 - Width = 541 + Width = 533 AutoSize = True BorderSpacing.Top = 6 BorderSpacing.Right = 4 @@ -359,12 +358,12 @@ object BasicSPCForm: TBasicSPCForm object ChartPage: TTabSheet Caption = 'Chart' ClientHeight = 398 - ClientWidth = 545 + ClientWidth = 537 object ChartToolBar: TToolBar Left = 0 Height = 22 Top = 6 - Width = 541 + Width = 533 AutoSize = True BorderSpacing.Top = 6 BorderSpacing.Right = 4 diff --git a/applications/lazstats/source/forms/analysis/statistical_process_control/basicspcunit.pas b/applications/lazstats/source/forms/analysis/statistical_process_control/basicspcunit.pas index 5cb76d137..d348a51e3 100644 --- a/applications/lazstats/source/forms/analysis/statistical_process_control/basicspcunit.pas +++ b/applications/lazstats/source/forms/analysis/statistical_process_control/basicspcunit.pas @@ -95,7 +95,7 @@ var // Constants for correction of standard deviation, needed by some charts. const - C4: array[1..24] of double = ( + C4: array[2..25] of double = ( 0.7979, 0.8862, 0.9213, 0.9400, 0.9515, 0.9594, 0.9650, 0.9693, 0.9727, 0.9754, 0.9776, 0.9794, 0.9810, 0.9823, 0.9835, 0.9845, 0.9854, 0.9862, 0.9869, 0.9876, 0.9882, 0.9887, 0.9892, 0.9896); @@ -193,7 +193,7 @@ begin CloseBtn.Constraints.MinWidth := w; SpecsPanel.Constraints.MinWidth := Max( - VarListLabel.Left + VarListLabel.Width + MeasLabel.Width, + VarListLabel.Left + VarListLabel.Width + Varlist.BorderSpacing.Right * 2 + MeasInBtn.Width + MeasLabel.Width, CloseBtn.Left + CloseBtn.Width - HelpBtn.Left + HelpBtn.BorderSpacing.Around ); Constraints.MinHeight := MeasEdit.Top + MeasEdit.Height + MeasEdit.BorderSpacing.Bottom + ButtonPanel.Height; diff --git a/applications/lazstats/source/forms/analysis/statistical_process_control/xbarchartunit.lfm b/applications/lazstats/source/forms/analysis/statistical_process_control/xbarchartunit.lfm index b240dc531..aa72371ef 100644 --- a/applications/lazstats/source/forms/analysis/statistical_process_control/xbarchartunit.lfm +++ b/applications/lazstats/source/forms/analysis/statistical_process_control/xbarchartunit.lfm @@ -1,82 +1,82 @@ inherited XBarChartForm: TXBarChartForm Left = 572 - Height = 443 + Height = 431 Top = 215 HelpType = htKeyword HelpKeyword = 'html/XBarChart.htm' Caption = 'X-Bar Control Chart' - ClientHeight = 443 + ClientHeight = 431 OnActivate = FormActivate inherited SpecsPanel: TPanel - Height = 443 - Width = 475 - ClientHeight = 443 - ClientWidth = 475 + Height = 431 + Width = 379 + ClientHeight = 431 + ClientWidth = 379 inherited ButtonPanel: TPanel - Top = 401 - Width = 475 - ClientWidth = 475 + Top = 389 + Width = 379 + ClientWidth = 379 TabOrder = 5 inherited CloseBtn: TButton - Left = 420 + Left = 324 end inherited ComputeBtn: TButton - Left = 336 + Left = 240 end inherited ResetBtn: TButton - Left = 274 + Left = 178 end inherited HelpBtn: TButton - Left = 223 + Left = 127 end inherited Bevel1: TBevel - Width = 467 + Width = 371 end end inherited VarList: TListBox - Height = 368 - Width = 209 + Height = 356 + Width = 169 end inherited GroupLabel: TLabel - Left = 257 + Left = 209 end inherited GroupEdit: TEdit - Left = 257 - Width = 218 + Left = 209 + Width = 170 end inherited MeasLabel: TLabel - Left = 257 + Left = 209 end inherited MeasEdit: TEdit AnchorSideRight.Control = LevelOptns - Left = 257 - Width = 218 + Left = 209 + Width = 170 end inherited Bevel2: TBevel - Left = 226 + Left = 178 end inherited MeasInBtn: TSpeedButton - Left = 225 + Left = 177 end inherited MeasOutBtn: TSpeedButton - Left = 226 + Left = 178 end inherited GroupInBtn: TSpeedButton - Left = 225 + Left = 177 end inherited GroupOutBtn: TSpeedButton - Left = 226 + Left = 178 end object SigmaOpts: TRadioGroup[12] - AnchorSideLeft.Control = MeasEdit - AnchorSideTop.Control = GroupEdit + AnchorSideLeft.Control = MeasInBtn + AnchorSideTop.Control = GroupOutBtn AnchorSideTop.Side = asrBottom AnchorSideRight.Control = LevelOptns AnchorSideRight.Side = asrBottom - Left = 257 + Left = 177 Height = 128 - Top = 145 - Width = 218 + Top = 153 + Width = 202 Anchors = [akTop, akLeft, akRight] AutoFill = True BorderSpacing.Top = 12 @@ -90,7 +90,7 @@ inherited XBarChartForm: TXBarChartForm ChildSizing.Layout = cclLeftToRightThenTopToBottom ChildSizing.ControlsPerLine = 1 ClientHeight = 108 - ClientWidth = 214 + ClientWidth = 198 ItemIndex = 0 Items.Strings = ( '3 Sigma' @@ -106,7 +106,7 @@ inherited XBarChartForm: TXBarChartForm Left = 128 Height = 23 Top = 80 - Width = 74 + Width = 58 Alignment = taRightJustify Anchors = [akLeft, akRight, akBottom] BorderSpacing.Right = 8 @@ -115,22 +115,22 @@ inherited XBarChartForm: TXBarChartForm end end object LevelOptns: TGroupBox[13] - AnchorSideLeft.Control = MeasEdit + AnchorSideLeft.Control = MeasInBtn AnchorSideTop.Control = SigmaOpts AnchorSideTop.Side = asrBottom AnchorSideRight.Control = MeasEdit AnchorSideRight.Side = asrBottom - Left = 257 + Left = 177 Height = 103 - Top = 285 - Width = 218 + Top = 293 + Width = 202 Anchors = [akTop, akLeft, akRight] AutoSize = True BorderSpacing.Top = 12 BorderSpacing.Bottom = 8 Caption = 'Show...' ClientHeight = 83 - ClientWidth = 214 + ClientWidth = 198 TabOrder = 4 object UpperSpecChk: TCheckBox AnchorSideLeft.Control = LevelOptns @@ -221,28 +221,28 @@ inherited XBarChartForm: TXBarChartForm end end inherited SpecsSplitter: TSplitter - Left = 478 - Height = 443 + Left = 390 + Height = 431 end inherited PageControl: TPageControl - Left = 486 - Height = 431 - Width = 435 + Left = 398 + Height = 419 + Width = 523 inherited ReportPage: TTabSheet - ClientHeight = 403 - ClientWidth = 427 + ClientHeight = 391 + ClientWidth = 515 inherited Panel1: TPanel - Height = 363 - Width = 415 - ClientHeight = 359 - ClientWidth = 411 + Height = 351 + Width = 503 + ClientHeight = 347 + ClientWidth = 499 inherited ReportMemo: TMemo - Height = 351 - Width = 403 + Height = 339 + Width = 491 end end inherited ReportToolBar: TToolBar - Width = 423 + Width = 511 end end inherited ChartPage: TTabSheet diff --git a/applications/lazstats/source/forms/analysis/statistical_process_control/xbarchartunit.pas b/applications/lazstats/source/forms/analysis/statistical_process_control/xbarchartunit.pas index bb6dbee2b..433918b7c 100644 --- a/applications/lazstats/source/forms/analysis/statistical_process_control/xbarchartunit.pas +++ b/applications/lazstats/source/forms/analysis/statistical_process_control/xbarchartunit.pas @@ -59,12 +59,15 @@ begin VarList.Constraints.MinWidth := VarListLabel.Width; SpecsPanel.Constraints.MinWidth := Max( CloseBtn.Left + CloseBtn.Width - HelpBtn.Left + HelpBtn.BorderSpacing.Around, - LevelOptns.Width * 2 + VarList.BorderSpacing.Right + VarList.BorderSpacing.Left + LevelOptns.Width * 2 + VarList.BorderSpacing.Right //* 2 + MeasInBtn.Width ); Constraints.MinHeight := LevelOptns.Top + LevelOptns.Height + LevelOptns.BorderSpacing.Bottom + ButtonPanel.Height; LevelOptns.AnchorSideRight.Control := MeasEdit; LevelOptns.AnchorSideRight.Side := asrBottom; + + if Height < Constraints.MinHeight then + Height := 1; // enforce height autosizing finally EnableAutoSizing; end; @@ -74,7 +77,6 @@ end; procedure TXBarChartForm.Compute; var i, j: Integer; - sigma: Double; upperSpec: Double = NaN; lowerSpec: Double = NaN; targetSpec: Double = NaN; @@ -83,12 +85,10 @@ var means: DblDyneVec = nil; stdDev: DblDyneVec = nil; count: IntDyneVec = nil; - numGrps: Integer; + numGrps, grpIndex, totalNumCases, grpSize: Integer; grp: String; - grpIndex: Integer; - totalNumCases: Integer; X, Xsq: Double; - UCL, LCL, grandMean, grandSD, stdErrMean, C4Value: Double; + sigma, aveStdDev, UCL, LCL, grandMean, grandSD, SEMean, C4Value: Double; lReport: TStrings; begin if GroupEdit.Text <> '' then @@ -126,11 +126,11 @@ begin SetLength(means, numGrps); SetLength(count, numGrps); SetLength(stddev, numGrps); - stdErrMean := 0.0; + SEMean := 0.0; grandMean := 0.0; totalNumCases := 0; - // calculate group means, grand mean, group sd's, semeans + // calculate group means, grand mean, group sd's, seMean for i := 1 to NoCases do begin if not GoodRecord(i, Length(ColNoSelected), ColNoSelected) then continue; @@ -151,30 +151,48 @@ begin means[grpIndex] := means[grpIndex] + X; stddev[grpIndex] := stddev[grpIndex] + Xsq; grandMean := grandMean + X; - stdErrMean := stdErrMean + Xsq; + SEMean := SEMean + Xsq; inc(totalNumCases); end; - stdErrMean := stdErrMean - sqr(grandMean) / totalNumCases; - stdErrMean := sqrt(stderrMean / (totalNumCases - 1)); - grandSD := stdErrMean; - stdErrMean := stdErrMean / sqrt(totalNumCases); + SEMean := SEMean - sqr(grandMean) / totalNumCases; + SEMean := sqrt(SEMean / (totalNumCases - 1)); + grandSD := SEMean; + SEMean := SEMean / sqrt(totalNumCases); grandMean := grandMean / totalNumCases; if (GroupEdit.Text = '') then begin // Individuals chart + grpSize := 1; SetLength(means, totalNumCases); SetLength(stddev, totalNumCases); Setlength(count, totalNumCases); for i := 0 to totalNumCases-1 do - stddev[i] := stdErrMean; - C4Value := 1.0 / C4[totalNumCases-1]; - UCL := grandMean + sigma * stdErrMean * C4Value; - LCL := grandMean - sigma * stdErrMean * C4Value; + stddev[i] := SEMean; + aveStdDev := NaN; + if totalNumCases <= 25 then + C4Value := C4[totalNumCases] + else + C4Value := 1.0; + UCL := grandMean + sigma * grandSD / C4Value; + LCL := grandMean - sigma * grandSD / C4Value; end else begin // Grouped chart + + // Check group size - it is assumed that all groups are equally sized + grpSize := count[0]; + for i := 1 to numGrps-1 do + if count[i] <> grpSize then + begin + ErrorMsg('All groups must have the same size.'); + exit; + end; + + aveStdDev := aveStdDev + stdDev[i]; + + aveStdDev := 0; for i := 0 to numGrps-1 do begin if count[i] = 0 then @@ -188,14 +206,18 @@ begin else begin stddev[i] := stddev[i] - sqr(means[i]) / count[i]; - stddev[i] := stddev[i] / (count[i] - 1); - stddev[i] := sqrt(stddev[i]); + stddev[i] := stddev[i] / (count[i] - 1); // Variance of group i + aveStdDev := aveStdDev + stdDev[i]; // Sum of variances + stddev[i] := sqrt(stddev[i]); // StdDev of group i end; means[i] := means[i] / count[i]; end; end; - UCL := grandMean + sigma * stdErrMean; - LCL := grandMean - sigma * stdErrMean; + aveStdDev := sqrt(aveStdDev / (numGrps * grpSize)); + UCL := grandMean + sigma * aveStdDev; + LCL := grandMean - sigma * aveStdDev; +// UCL := grandMean + sigma * SEMean; // old LazStats calculation -- does not agree with JMP software. +// LCL := grandMean - sigma * SEMean; end; // Print results @@ -203,11 +225,14 @@ begin try lReport.Add('X BAR CHART RESULTS'); lReport.Add(''); - lReport.Add('Number of samples: %8d', [totalNumCases]); + lReport.Add('Number of values: %8d', [totalNumCases]); + lReport.Add('Number of groups: %8d', [numGrps]); + lReport.Add('Group size: %8d', [grpSize]); + lReport.Add(''); lReport.Add('Grand Mean: %8.3f', [grandMean]); lReport.Add('Standard Deviation: %8.3f', [grandSD]); - lReport.Add('Standard Error of Mean: %8.3f', [stdErrMean]); - lReport.Add(''); + lReport.Add('Standard Error of Mean: %8.3f', [SEMean]); + lReport.Add('Average Std Deviation: %8.3f', [aveStdDev]); lReport.Add('Lower Control Limit: %8.3f', [LCL]); lReport.Add('Upper Control Limit: %8.3f', [UCL]); lReport.Add(''); diff --git a/applications/lazstats/source/frames/chartframeunit.lfm b/applications/lazstats/source/frames/chartframeunit.lfm index 2d484f8c0..7df5009d2 100644 --- a/applications/lazstats/source/frames/chartframeunit.lfm +++ b/applications/lazstats/source/frames/chartframeunit.lfm @@ -24,6 +24,8 @@ object ChartFrame: TChartFrame end item Grid.Color = clSilver + Intervals.MaxLength = 80 + Intervals.MinLength = 30 Alignment = calBottom Marks.LabelBrush.Style = bsClear Minors = <>