Monitoring multivariate processes with IQCC
Source:vignettes/multivariate-monitoring.Rmd
multivariate-monitoring.RmdScope
Multivariate process monitoring separates two questions that are often mixed in practice. Hotelling T-squared charts monitor shifts in a process mean vector, while generalized-variance and trace charts monitor changes in the covariance structure. IQCC contains all three workflows:
| Question | Statistic | Main functions |
|---|---|---|
| Has the mean vector shifted? | Hotelling T-squared |
T2.1(), T2.2(),
cchart.T2.1(), cchart.T2.2()
|
| Has total generalized variance changed? | |S| |
gv_stat(), gv_limits(),
gv_alpha_risk(), cchart.GV()
|
| Has standardized covariance structure changed? | tr(V) |
trv_stat(), trv_limits(),
trv_alpha_risk(), cchart.trV()
|
The covariance charts are complementary. The determinant
|S| summarizes the volume of the subgroup covariance
matrix. The trace statistic (n - 1) tr(Sigma0^-1 S) can
change even when the determinant is nearly unchanged.
Input formats
The generalized-variance and trace-statistic functions use the
multivariate subgroup formats accepted by gv_stat() and
cchart.GV(): a list of subgroup matrices, a
subgroup-by-observation-by-variable array, or a stacked matrix with the
subgroup size supplied separately. The examples below use a list because
it makes the subgroup boundaries explicit.
g1 <- cbind(
x = c(-1, 0, 1, -1, 1),
y = c(-1, 1, 0, 1, -1)
)
g2 <- sweep(g1, 2, c(0.2, -0.1), "+")
g3 <- g1 %*% matrix(c(1.1, 0.1, 0, 0.9), nrow = 2)
groups <- list(g1, g2, g3)
data.frame(
subgroup = seq_along(groups),
generalized_variance = gv_stat(groups),
trace_statistic = trv_stat(groups, Sigma0 = diag(2))
)
#> subgroup generalized_variance trace_statistic
#> 1 1 0.9375000 8.0
#> 2 2 0.9375000 8.0
#> 3 3 0.9188438 7.9The legacy Hotelling helpers use a different array convention:
observation-by-variable-by-subgroup. Check dim() before
moving data between the Hotelling helpers and the generalized-variance
or trace workflows.
Hotelling T-squared
The Hotelling workflow estimates Phase I location and covariance, computes Phase I T-squared statistics, and then evaluates Phase II observations against the Phase II reference distribution.
set.seed(2026)
phase1 <- data.1(m = 8, n = 5, mu = c(0, 0), Sigma = diag(2))
reference <- IQCC::stats(phase1, m = 8, n = 5, p = 2)
t2_phase1 <- T2.1(reference, m = 8, n = 5)
phase2 <- data.2(reference, n = 5, p = 2)
t2_phase2 <- T2.2(phase2, reference, n = 5)
data.frame(
phase = c(rep("Phase I", length(t2_phase1)), "Phase II"),
statistic = c(t2_phase1, t2_phase2)
)
#> phase statistic
#> 1 Phase I 6.5124530
#> 2 Phase I 2.8083634
#> 3 Phase I 8.1145908
#> 4 Phase I 0.5432524
#> 5 Phase I 1.5197014
#> 6 Phase I 4.1999052
#> 7 Phase I 1.6098032
#> 8 Phase I 3.6227786
#> 9 Phase II 3.8565799cchart.T2.1() and cchart.T2.2() draw the
two chart types. Their control limits are not interchangeable because
Phase I and Phase II use different reference distributions.
Generalized variance
For each subgroup, gv_stat() computes the determinant of
the sample covariance matrix. gv_limits() exposes normal,
Cornish-Fisher, selected exact, and simulation-based limits. The exact
implementation is deliberately scoped; generic Meijer-G quantiles are
not claimed by the package.
gv_methods <- do.call(
rbind,
lapply(c("normal", "cf", "exact"), function(method) {
limits <- gv_limits(n = 5, p = 2, det_sigma = 1, type = method)
data.frame(
method = method,
center = limits$center,
lcl = limits$lcl,
ucl = limits$ucl
)
})
)
gv_sim <- gv_limits(
n = 5,
p = 2,
det_sigma = 1,
type = "simulation",
nsim = 2000,
seed = 2026
)
gv_methods
#> method center lcl ucl
#> 1 normal 0.75 0 3.305568
#> 2 cf 0.75 0 6.769365
#> 3 exact 0.75 0 6.288749
gv_sim[c("type", "ucl", "nsim", "seed")]
#> $type
#> [1] "simulation"
#>
#> $ucl
#> [1] 5.379544
#>
#> $nsim
#> [1] 2000
#>
#> $seed
#> [1] 2026The chart wrapper records the phase split, limits, covariance source,
and signaled subgroups. Use summary() to inspect the fitted
object instead of reading values from a plot.
gv_chart <- cchart.GV(
x1 = groups[1:2],
x2 = groups[3],
type = "cf",
plot = FALSE
)
summary(gv_chart)
#> Generalized Variance Control Chart
#> Dimension: p = 2 ; subgroup size n = 5
#> Subgroups: 3 (Phase I: 2 ; Phase II: 1 )
#> Limits: cf / upper ; nominal alpha = 0.0027
#> Covariance: estimated from Phase I subgroups
#> LCL = 0 ; center = 0.8036 ; UCL = 7.253
#> Signals: 0If Sigma is omitted, IQCC uses Phase I covariance
information as a plug-in reference. The nominal known-parameter
calibration does not include the extra uncertainty introduced by
estimating the reference from a finite Phase I sample.
Auxiliary trace chart
The trace statistic is
Under multivariate normality and known in-control covariance
Sigma0, T has a chi-square distribution with
p * (n - 1) degrees of freedom.
trv_limits(n = 5, p = 2, alpha = 0.0027)
#> $lcl
#> [1] 0
#>
#> $ucl
#> [1] 23.57439
#>
#> $center
#> [1] 8
#>
#> $type
#> [1] "chisq"
#>
#> $alpha
#> [1] 0.0027
#>
#> $n
#> [1] 5
#>
#> $p
#> [1] 2
#>
#> $df
#> [1] 8
#>
#> $nsim
#> [1] 1e+05
#>
#> $seed
#> NULL
trv_alpha_risk(n = 5, p = 2, ucl = trv_limits(5, 2)$ucl)
#> $alpha
#> [1] 0.0027
#>
#> $arl0
#> [1] 370.3704
#>
#> $ucl
#> [1] 23.57439
#>
#> $n
#> [1] 5
#>
#> $p
#> [1] 2
#>
#> $df
#> [1] 8
#>
#> $method
#> [1] "exact chi-square"The trace chart is most useful beside |S|, not as a
replacement for it. The following deterministic example has two
covariance matrices with the same determinant but different standardized
trace.
base <- sqrt(3 / 4) * rbind(
c(1, 1), c(1, -1), c(-1, 1), c(-1, -1)
)
same_volume <- list(
base %*% chol(diag(2)),
base %*% chol(diag(c(4, 0.25)))
)
data.frame(
generalized_variance = gv_stat(same_volume),
trace_statistic = trv_stat(same_volume, Sigma0 = diag(2))
)
#> generalized_variance trace_statistic
#> 1 1 6.00
#> 2 1 12.75
trv_chart <- cchart.trV(
x = same_volume[1],
newdata = same_volume[2],
Sigma0 = diag(2),
alpha = 0.05,
type = "chisq",
plot = FALSE
)
summary(trv_chart)
#> Trace-Statistic Control Chart
#> Dimension: p = 2 ; subgroup size n = 4
#> Subgroups: 2 (Phase I: 1 ; Phase II: 1 )
#> Limits: chisq ; nominal alpha = 0.05 ; df = 6
#> Covariance: supplied Sigma0
#> Center = 6 ; UCL = 12.59
#> Signals: 1
#> index phase statistic
#> 2 II 12.75The published generalized-variance report contains a Case B where the printed summary matrices keep determinants close while the trace chart signals in Phase II. IQCC reproduces the printed determinants and the published trace UCL, but the full signal sequence cannot be recomputed because the report does not print all simulated subgroups or the random seed.
Practical sequence
- Use Hotelling T-squared when the primary question is a shift in the mean vector.
- Use
|S|when the primary question is a change in generalized variance or covariance volume. - Add
tr(V)when covariance structure may change without a large determinant change. - State whether covariance references are known parameters or Phase I plug-in estimates.
- Use simulated limits as Monte Carlo approximations and report
nsim,seed, and simulation error when they support a scientific claim.
cat("<!-- IQCC_EXECUTED_MULTIVARIATE_MONITORING -->\n")