multivariate_spc
multivariate_spc ¤
Multivariate SPC: Hotelling T² across correlated process variables.
Univariate control charts check each variable against its own limits. On a process whose variables move together (temperature and pressure, feed and torque) a fault often breaks the relationship while every variable stays inside its own 3-sigma band. Hotelling's T² measures the distance of each observation from the in-control mean in units of the in-control covariance, so it sees exactly that.
Phase I estimates the mean vector and covariance matrix on a reference
period. Phase II scores every observation against them, with the exact
F-distribution control limit for a covariance estimated from m
reference observations (Montgomery, Introduction to Statistical Quality
Control, ch. 11). Reference observations themselves are judged against
the Phase I beta-distribution limit.
Signals are aligned on the timestamps of the first signal; every other
signal contributes the value in force at that time (held backward, never
across more than max_gap: no look-ahead).
MultivariateSPCEvents ¤
MultivariateSPCEvents(
dataframe: DataFrame,
signal_uuids: list[str],
*,
value_column: str = "value_double",
event_uuid: str = "quality:hotelling_t2",
max_gap: str | Timedelta | None = None,
time_column: str = "systime"
)
Bases: Base
Quality: multivariate SPC with Hotelling T².
Methods: - hotelling_t2: T² and its control limit for every aligned observation. - contributions: Which variables drive each out-of-control T². - detect_out_of_control: Runs of out-of-control observations as intervals, with the top contributing variable.
Example
import ts_shape df = ts_shape.make_timeseries(["temp", "pressure"], n_points=200) df.loc[150, "value_double"] += 8 # one disturbed temp sample mspc = ts_shape.MultivariateSPCEvents(df, ["temp", "pressure"]) out = mspc.detect_out_of_control() out[["start", "max_t2", "ucl", "top_contributor"]].round( ... {"max_t2": 1, "ucl": 1} ... ) start max_t2 ucl top_contributor 0 2025-01-01 01:15:00 56.9 11.5 temp
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataframe
|
DataFrame
|
Long-format data. |
required |
signal_uuids
|
list[str]
|
The process variables (at least two). |
required |
value_column
|
str
|
Column holding the values. |
'value_double'
|
event_uuid
|
str
|
|
'quality:hotelling_t2'
|
max_gap
|
str | Timedelta | None
|
Longest time a value of the second and later signals is
held to align it with the first signal; |
None
|
time_column
|
str
|
Timestamp column. |
'systime'
|
hotelling_t2 ¤
hotelling_t2(
reference_period: (
tuple[TimeLike, TimeLike] | None
) = None,
alpha: float = 0.0027,
) -> pd.DataFrame
Hotelling T² for every aligned observation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reference_period
|
tuple[TimeLike, TimeLike] | None
|
|
None
|
alpha
|
float
|
False-alarm probability per observation (0.0027 matches 3-sigma limits). |
0.0027
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Point events: |
DataFrame
|
uuids, comma-joined), |
DataFrame
|
reference observations, |
DataFrame
|
|
DataFrame
|
class: |
DataFrame
|
period has too few aligned observations ( |
DataFrame
|
for |
contributions ¤
contributions(
reference_period: (
tuple[TimeLike, TimeLike] | None
) = None,
alpha: float = 0.0027,
only_out_of_control: bool = True,
) -> pd.DataFrame
Per-variable decomposition of T².
Contribution of variable j: d_j * (S^-1 d)_j with d the
deviation from the reference mean. The contributions of one
observation sum to its T²; a large share points at the variable (or
broken relationship) behind the signal. z_score is the plain
univariate deviation, for comparison.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reference_period
|
tuple[TimeLike, TimeLike] | None
|
As in :meth: |
None
|
alpha
|
float
|
As in :meth: |
0.0027
|
only_out_of_control
|
bool
|
Keep only observations flagged out of control by
:meth: |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Point events, one row per (observation, variable): |
DataFrame
|
|
DataFrame
|
|
detect_out_of_control ¤
detect_out_of_control(
reference_period: (
tuple[TimeLike, TimeLike] | None
) = None,
alpha: float = 0.0027,
min_samples: int = 1,
) -> pd.DataFrame
Runs of consecutive out-of-control observations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reference_period
|
tuple[TimeLike, TimeLike] | None
|
As in :meth: |
None
|
alpha
|
float
|
As in :meth: |
0.0027
|
min_samples
|
int
|
Minimum observations in a run. |
1
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Intervals from the first to the last out-of-control observation |
DataFrame
|
of each run: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
largest summed contribution over the run). |
phase2_ucl ¤
phase2_ucl(p: int, m: int, alpha: float) -> float
Phase II T² upper control limit for new observations.
p (m + 1)(m - 1) / (m (m - p)) * F(1 - alpha; p, m - p) for a mean
and covariance estimated from m reference observations.
phase1_ucl ¤
phase1_ucl(p: int, m: int, alpha: float) -> float
Phase I T² limit for the reference observations themselves.
(m - 1)^2 / m * Beta(1 - alpha; p / 2, (m - p - 1) / 2).