Quick Start#
Installation#
pip install process-improve
PCA Example#
import pandas as pd
from process_improve.multivariate.methods import PCA, MCUVScaler
# Load and scale data
X = pd.read_csv("your_data.csv", index_col=0)
scaler = MCUVScaler().fit(X)
X_scaled = scaler.transform(X)
# Fit model
pca = PCA(n_components=3).fit(X_scaled)
# Results
pca.scores_ # Score matrix (N x A)
pca.loadings_ # Loading matrix (K x A)
pca.r2_cumulative_ # Cumulative R² per component
# Diagnostics
pca.detect_outliers()
pca.score_contributions(X_scaled, component=1) # per-variable, sums to t1
# Plots
pca.score_plot()
pca.loading_plot()
pca.spe_plot()
pca.t2_plot()
PLS Example#
from process_improve.multivariate.methods import PLS, MCUVScaler
# Scale X and Y separately
scaler_x = MCUVScaler().fit(X)
scaler_y = MCUVScaler().fit(Y)
X_scaled = scaler_x.transform(X)
Y_scaled = scaler_y.transform(Y)
# Fit model
pls = PLS(n_components=3).fit(X_scaled, Y_scaled)
# Predict new observations (sklearn-compatible: returns the y_hat array)
y_pred = pls.predict(scaler_x.transform(X_new))
# Predict with full per-row diagnostics (scores, T², SPE, plus y_hat)
result = pls.diagnose(scaler_x.transform(X_new))
result.y_hat # Predicted Y
result.spe # SPE diagnostics
result.hotellings_t2 # Hotelling's T² diagnostics
# Diagnostics
pls.detect_outliers()
pls.score_contributions(X_scaled, component=1) # per-variable, sums to t1
Component Selection#
Use cross-validation to select the number of PCA components:
result = PCA.select_n_components(X_scaled, max_components=10)
print(f"Recommended: {result.n_components} components")
print(f"PRESS ratios: {result.press_ratio}")
On-line Monitoring (Adaptive PCA / PLS)#
A static model goes stale as soon as the process drifts. AdaptivePCA and
AdaptivePLS start from an initial fit, then keep learning as data streams in,
one observation at a time.
from process_improve.multivariate import AdaptivePCA
# Seed on a block of known-good ("common cause") data
monitor = AdaptivePCA(n_components=3).fit(X_reference)
# Feed live observations one row at a time
for _, row in X_stream.iterrows():
result = monitor.update(row.to_numpy())
if not result.in_control:
print(f"Out of control: SPE={result.spe:.2f}, T2={result.hotellings_t2:.2f}")
# Drift diagnostics accumulated over the stream
monitor.distance_ # subspace drift, in units of components
monitor.center_shift_ # operating-point migration, in training-SD units
AdaptivePLS does the same for regression and soft sensing, and supports
infrequently-sampled responses: the X-space model adapts every step while the
regression part waits for the next response.
Optimal & OMARS Designs#
Beyond the textbook designs, ask for an OMARS (orthogonal minimally aliased response surface) or an optimal design, then grade its quality:
from process_improve.experiments import Factor, generate_design, evaluate_design
factors = [
Factor(name="A", low=-1, high=1),
Factor(name="B", low=-1, high=1),
Factor(name="C", low=-1, high=1),
]
# OMARS: main effects clear of every second-order term
design = generate_design(factors, design_type="omars")
# A run-budgeted D-optimal design, scored on every quality metric
d_opt = generate_design(factors, design_type="d_optimal", budget=14)
metrics = evaluate_design(d_opt, metric="all") # D/I/G-efficiency, aliasing, ...
DOE Strategy Example#
Plan a multi-stage experimental strategy before running any experiments:
from process_improve.experiments.factor import Factor, Response
from process_improve.experiments.strategy import recommend_strategy
factors = [
Factor(name="Temperature", low=25, high=40, units="degC"),
Factor(name="pH", low=5.0, high=7.5),
Factor(name="Glucose", low=10, high=50, units="g/L"),
Factor(name="Yeast extract", low=1, high=10, units="g/L"),
Factor(name="Agitation", low=100, high=400, units="rpm"),
Factor(name="Aeration", low=0.5, high=2.0, units="vvm"),
Factor(name="Inoculum", low=2, high=10, units="%v/v"),
]
result = recommend_strategy(
factors=factors,
responses=[Response(name="Yield", goal="maximize", units="g/L")],
budget=40,
domain="fermentation",
)
for stage in result["stages"]:
print(f"Stage {stage['stage_number']}: {stage['stage_name']} "
f"({stage['design_type']}, {stage['estimated_runs']} runs)")