This tutorial will get you up and running with ProcessBehavior in about 5 minutes.
What You’ll Learn¶
Chart a plain list of values in two calls
Load data into a ProcessBehavior wrapper
Formulate a study with response, factors, and time
Execute the analysis and create control charts
Detect signals (out-of-control points)
Export results
Setup¶
First, let’s import the necessary libraries:
import numpy as np
import pandas as pd
from processbehavior import ProcessBehaviorThe two-call version: a list of values¶
A column of values with no time column is charted in the order received. The rows are numbered 1..N in an Observation column and the study is a single stream (ADS 2), so the recommended chart is X, with its mR companion:
values = pd.DataFrame({'weight': [50.2, 49.8, 50.5, 50.1, 49.6, 50.9, 50.3, 49.9, 50.4, 50.0, 49.7, 50.6]})
quick = ProcessBehavior(values).formulate(response='weight')
x = quick.execute(companion=True).statistics('X')
print(f"Recommended chart: {quick.recommended_chart}")
print(quick.series_length_description)
print(f"Center {x['center']}, limits {x['lpl']} to {x['upl']}")Recommended chart: X
T=12. Sigma for X/mR rests on 11 moving ranges.
Center 50.167, limits 48.619 to 51.714
If the rows are not in time order, pass the time column with time=. The rest of this tutorial works with a richer dataset: factors, a time order and replicates.
Step 1: Create Sample Data¶
Let’s create a realistic manufacturing dataset. We’ll simulate a filling machine with:
4 filling lanes (A, B, C, D)
Measurements over 10 time periods
3 replicate measurements per lane per time period
np.random.seed(42)
# Create replicated design: 4 lanes x 10 times x 3 replicates
n_lanes = 4
n_times = 10
n_reps = 3
# Build the data
lanes = ['A', 'B', 'C', 'D']
data = []
for t in range(n_times):
for lane in lanes:
# Each lane has slightly different mean
lane_offset = {'A': 0, 'B': 1, 'C': -0.5, 'D': 0.5}[lane]
for rep in range(n_reps):
# Add some special cause variation to Lane C at time 7
special_cause = 8 if (lane == 'C' and t == 7) else 0
value = 100 + lane_offset + special_cause + np.random.normal(0, 2)
data.append({
'time': t + 1,
'lane': lane,
'weight': round(value, 2)
})
df = pd.DataFrame(data)
print(f"Dataset shape: {df.shape}")
df.head(12)Dataset shape: (120, 3)
Step 2: Create a ProcessBehavior Wrapper¶
Wrap your pandas DataFrame in a ProcessBehavior to enable IDE auto-completion and the fluent API:
pb = ProcessBehavior(df)
# Explore available columns with auto-completion
print("Available columns:")
print(f" - {pb.cols.time}")
print(f" - {pb.cols.lane}")
print(f" - {pb.cols.weight}")Available columns:
- time
- lane
- weight
Step 3: Formulate Your Study¶
Use formulate() to specify:
response: The measurement variable
factors: Categorical variables that define subgroups
time: Time ordering variable
ProcessBehavior automatically detects the Sampling Design State (SDS):
study = pb.formulate(
response=pb.cols.weight,
factors=[pb.cols.lane],
time=pb.cols.time
)
print(f"Observed: ODS {study.observed_design_state.sds}")
print(f"Analytical: ADS {study.analytical_design_state.sds}")
print(f"Recommended chart: {study.recommended_chart}")
print(f"Valid charts: {study.valid_charts}")Observed: ODS 1
Analytical: ADS 1
Recommended chart: Xbar
Valid charts: ['Histogram', 'Xbar', 'S', 'X', 'mR']
Understanding the design-state lineage¶
The output above reports two design states:
ODS (Observed Design State) - what the raw data structure looks like before any cleaning. Computed on the (factor x time) grid as Bishop’s classification of the N_kt distribution.
ADS (Analytical Design State) - what survives tidying and drives chart selection, residual availability, and variance decomposition.
For this dataset, both classify as Bishop’s Code 1 (“full replication”): every cell has at least 2 observations. That’s the gold-standard design - it supports the full residual decomposition (R1-R5) and Xbar-S charts.
Other Bishop codes you may see:
| Code | Cell distribution | Notes |
|---|---|---|
| 1 | Complete grid, all N_kt >= 2 | Full residual decomposition |
| 2 | Complete grid, all N_kt = 1 | No replication; X/mR only |
| 3 | Complete grid, mixed N_kt | Partial replication |
| 4, 5, 6 | Incomplete grid (some cells empty) | ODS only; collapses to ADS 1/2/3 |
Step 4: Execute the Analysis¶
The execute() method computes control chart statistics:
# Execute with the recommended chart (Xbar-S)
result = study.execute()
print(f"Charts calculated: {result.all_charts}")
print(f"Has residuals: {result.has_residuals}")Charts calculated: ['Xbar']
Has residuals: True
View Chart Statistics¶
Results are returned as plain pandas DataFrames:
# Get the Xbar chart data
xbar_data = result.get_chart('Xbar')
print("Xbar Chart Data:")
xbar_data.head(10)Xbar Chart Data:
# View statistics
print("\nXbar Statistics:")
result.get_statistics('Xbar')
Xbar Statistics:
{'center': np.float64(100.292),
'N': np.int64(3),
'upl': np.float64(103.696),
'lpl': np.float64(96.887)}Step 5: Visualize with Control Charts¶
Create interactive Plotly charts with one line:
# Basic control chart
fig = result.plot()
fig.show()Enhanced Visualization¶
Add zone shading, signal markers, and statistics:
fig = result.plot(
chart='Xbar',
show_zones=True, # Show 1σ, 2σ, 3σ zones
highlight_signals=True, # Highlight out-of-control points
show_stats=True, # Display statistics box
theme='processbehavior' # Use default theme
)
fig.show()Step 6: Detect Signals¶
Use Western Electric rules to detect out-of-control conditions:
# Detect signals on Xbar chart
signals = result.detect_signals(chart='Xbar')
print(f"Signals detected: {signals.count}")
print(f"Has signals: {signals.has_signals}")
if signals.has_signals:
print("\nViolations:")
display(signals.violations)Signals detected: 1
Has signals: True
Violations:
Signal Detection Rules¶
For Xbar and S charts (categorical comparisons), only Rule 1 applies:
Points beyond the control limits
For IMR charts (time series), all 8 Western Electric rules apply:
Rule 1: Point beyond 3σ
Rule 2: 2 of 3 consecutive in Zone A
Rule 3: 4 of 5 consecutive in Zone B or beyond
Rule 4: 8+ consecutive same side of center
Rule 5: 6+ consecutive trending
Rule 6: 14+ consecutive alternating
Rule 7: 15+ consecutive in Zone C
Rule 8: 8+ consecutive avoiding Zone C
Step 7: Stratified Analysis¶
For time series analysis by lane, use stratified IMR charts. The by parameter controls how data is grouped:
# Execute stratified X/mR (one chart pair per lane)
# `by=['lane']` separates the chart into one lane each;
# `companion=True` gives both X (Individuals) and mR (Moving Range).
result_imr = study.execute(chart='X', by=['lane'], companion=True)
# Each lane gets its own X and mR chart
print(f"X chart strata: {result_imr.charts['X']['strata']}")
print(f"mR chart strata: {result_imr.charts['mR']['strata']}")X chart strata: ['A', 'B', 'C', 'D']
mR chart strata: ['A', 'B', 'C', 'D']
# Plot stratified IMR with all rules
fig = result_imr.plot(
show_zones=True,
highlight_signals=True,
show_rules=True, # Show all WECO rule violations
theme='dark'
)
fig.show()Step 8: Export Results¶
Export everything to Excel for sharing:
# Export to Excel (requires openpyxl)
# result.to_excel('analysis_results.xlsx')
# Or get results as DataFrames for custom export
print("Available as plain DataFrames:")
print(f" - result.get_chart('Xbar')")
print(f" - result.get_statistics('Xbar')")
print(f" - result.residuals # VAS residuals")Available as plain DataFrames:
- result.get_chart('Xbar')
- result.get_statistics('Xbar')
- result.residuals # VAS residuals
Summary¶
You’ve learned the core ProcessBehavior workflow:
ProcessBehavior(df) - Wrap your data
pb.formulate(...) - Define response, factors, time
study.execute() - Compute statistics
result.plot() - Visualize
result.detect_signals() - Find out-of-control points
Next Steps¶
Key Concepts - Understand DS, VAS, and Wheeler terminology
Xbar-S Analysis - Deep dive into Xbar-S charts
Stratified Analysis - Multi-factor analysis
Plotting & Themes - Advanced visualization options