Process-capability histogram with Cp/Cpk
Manufacturing — NIST MPC61 dataset
Example from the compendium of canonical charts
Python Code
"""Manufacturing — Process-capability histogram with Cp/Cpk (NIST MPC61 dataset)."""
from pathlib import Path
# ── Palette + theme (matching the Plotly Studio gallery these charts ship in) ──
VIOLET, TEAL, GREEN, PINK, ORANGE = "#845EEE", "#52B3D0", "#55B685", "#DA5597", "#E9A23B"
PRIMARY, SECONDARY = VIOLET, TEAL
COLORWAY = [VIOLET, TEAL, GREEN, PINK, ORANGE]
BG, TEXT, GRID, MUTED = "#ffffff", "#1c2024", "#d9d9e0", "#60646c"
FONT = "Inter, -apple-system, BlinkMacSystemFont, sans-serif"
COLORSCALE = [[0, "rgba(132, 94, 238, 0.05)"], [1, "rgba(132, 94, 238, 0.9)"]]
def apply_theme(fig):
"""Light gallery theme: white background, Inter font, soft gridlines."""
fig.update_layout(
paper_bgcolor=BG, plot_bgcolor=BG, colorway=COLORWAY,
font=dict(family=FONT, color=TEXT, size=12),
legend=dict(font=dict(color=TEXT)),
hoverlabel=dict(bgcolor="#f0f0f3", font=dict(color=TEXT, family=FONT), bordercolor=GRID),
)
fig.update_xaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
fig.update_yaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
def fetch_csv(url, **kwargs):
import io
import pandas as pd
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return pd.read_csv(io.StringIO(r.text), **kwargs)
def fetch_json(url):
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return r.json()
import io
import numpy as np
from scipy import stats
import plotly.graph_objects as go
import requests
import warnings
warnings.filterwarnings("ignore")
URL = "https://www.itl.nist.gov/div898/handbook/datasets/MPC61.DAT"
LSL = 97.5
USL = 102.5
def generate():
print("fetching MPC61.DAT from NIST …")
r = requests.get(URL, verify=False, timeout=60)
r.raise_for_status()
# Parse: skip header lines (non-numeric), extract column 6 (0-indexed)
data_vals = []
for line in r.text.splitlines():
line = line.strip()
if not line:
continue
parts = line.split()
# Try to parse first token as float
try:
float(parts[0])
except (ValueError, IndexError):
continue
# We have a numeric line — take column index 6 if available
if len(parts) >= 7:
try:
data_vals.append(float(parts[6]))
except ValueError:
pass
elif len(parts) > 0:
# fallback: use first column
try:
data_vals.append(float(parts[0]))
except ValueError:
pass
print(f"parsed {len(data_vals)} data points, first few: {data_vals[:5]}")
if len(data_vals) < 5:
raise ValueError("Not enough data points parsed from NIST file")
data = np.array(data_vals)
mu = data.mean()
sigma = data.std(ddof=1)
Cp = (USL - LSL) / (6 * sigma)
Cpk = min((USL - mu) / (3 * sigma), (mu - LSL) / (3 * sigma))
# Fit normal curve
x_fit = np.linspace(data.min() - sigma, data.max() + sigma, 300)
y_fit = stats.norm.pdf(x_fit, mu, sigma)
# Scale normal pdf to match histogram counts
n_bins = 20
bin_width = (data.max() - data.min()) / n_bins
y_fit_scaled = y_fit * len(data) * bin_width
fig = go.Figure()
fig.add_trace(go.Histogram(
x=data,
nbinsx=n_bins,
name="Resistivity",
marker_color=VIOLET,
marker_opacity=0.7,
hovertemplate="Value: %{x:.2f}<br>Count: %{y}<extra></extra>",
))
fig.add_trace(go.Scatter(
x=x_fit,
y=y_fit_scaled,
mode="lines",
name="Fitted normal",
line=dict(color=TEAL, width=2.5),
hovertemplate="x=%{x:.2f}<extra>Normal fit</extra>",
))
# Spec limits
fig.add_vline(x=LSL, line=dict(color=PINK, width=2, dash="dash"),
annotation_text=f"LSL={LSL}", annotation_position="top right",
annotation_font=dict(size=11, color=PINK))
fig.add_vline(x=USL, line=dict(color=PINK, width=2, dash="dash"),
annotation_text=f"USL={USL}", annotation_position="top left",
annotation_font=dict(size=11, color=PINK))
fig.add_vline(x=mu, line=dict(color=GREEN, width=1.5, dash="dot"),
annotation_text=f"μ={mu:.2f}", annotation_position="top right",
annotation_font=dict(size=11, color=GREEN))
# Cp / Cpk annotation
fig.add_annotation(
x=0.98, y=0.95,
xref="paper", yref="paper",
text=f"<b>Cp = {Cp:.3f}</b><br><b>Cpk = {Cpk:.3f}</b>",
showarrow=False,
align="right",
font=dict(size=13),
bgcolor="rgba(255,255,255,0.8)",
bordercolor=MUTED,
borderwidth=1,
)
fig.update_layout(
xaxis=dict(title="Resistivity"),
yaxis=dict(title="Count"),
legend=dict(orientation="h", y=-0.14),
margin=dict(t=50, b=70, l=70, r=40),
)
apply_theme(fig)
return fig
fig = generate()
fig.show()
Made with Plotly