Shewhart individuals (I) chart
NIST MAVRO filter-transmittance data
Example from the compendium of canonical charts
Python Code
"""Shewhart individuals (I) chart — NIST MAVRO filter-transmittance data."""
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 numpy as np
import pandas as pd
import plotly.graph_objects as go
import requests, warnings
URL = "https://www.itl.nist.gov/div898/handbook/datasets/MAVRO.DAT"
D2 = 1.128 # unbiasing constant for n=2 moving range
def generate():
print("fetching NIST MAVRO.DAT …")
warnings.filterwarnings("ignore")
requests.packages.urllib3.disable_warnings()
r = requests.get(URL, verify=False, timeout=30)
r.raise_for_status()
lines = r.text.strip().split("\n")
# Skip first 25 header lines; remaining lines are the data values
data_lines = lines[25:]
values = []
for line in data_lines:
for token in line.split():
try:
values.append(float(token))
except ValueError:
pass
values = values[:50] # spec says 50 values
print(f" parsed {len(values)} values")
x = list(range(1, len(values) + 1))
y = np.array(values)
# Center line and control limits via moving-range method
mr = np.abs(np.diff(y))
mr_bar = np.mean(mr)
y_bar = np.mean(y)
sigma = mr_bar / D2
ucl = y_bar + 3 * sigma
lcl = y_bar - 3 * sigma
# Flag out-of-control: beyond ±3σ
ooc_mask = (y > ucl) | (y < lcl)
# Western Electric run rule: 8 consecutive points on same side of center
run8_idx = []
for i in range(7, len(y)):
window = y[i-7:i+1]
if np.all(window > y_bar) or np.all(window < y_bar):
run8_idx.append(i)
fig = go.Figure()
# UCL / LCL / Center
for val, label, color, dash in [
(ucl, f"UCL = {ucl:.4f}", PINK, "dash"),
(y_bar, f"CL = {y_bar:.4f}", VIOLET, "solid"),
(lcl, f"LCL = {lcl:.4f}", PINK, "dash"),
]:
fig.add_hline(y=val, line=dict(color=color, width=1.5, dash=dash),
annotation_text=label, annotation_position="right",
annotation_font=dict(size=10))
# All points
fig.add_trace(go.Scatter(
x=x, y=values,
mode="lines+markers",
name="Transmittance",
line=dict(color=VIOLET, width=1.5),
marker=dict(color=VIOLET, size=6),
hovertemplate="Sample %{x}<br>Value=%{y:.4f}<extra></extra>",
))
# Run-rule violations
if run8_idx:
fig.add_trace(go.Scatter(
x=[x[i] for i in run8_idx],
y=[values[i] for i in run8_idx],
mode="markers", name="8-pt run rule",
marker=dict(color=TEAL, size=16, symbol="diamond"),
hovertemplate="Run rule: Sample %{x}<extra></extra>",
))
# Out-of-control points
if ooc_mask.any():
ooc_x = [x[i] for i in range(len(x)) if ooc_mask[i]]
ooc_y = [values[i] for i in range(len(values)) if ooc_mask[i]]
fig.add_trace(go.Scatter(
x=ooc_x, y=ooc_y,
mode="markers", name="Out of control",
marker=dict(color=PINK, size=12, symbol="circle-open", line=dict(width=2)),
hovertemplate="OOC: Sample %{x}, Value=%{y:.4f}<extra></extra>",
))
fig.update_layout(
title=dict(text="Shewhart I-Chart — NIST MAVRO Filter Transmittance", x=0.5),
xaxis=dict(title="Sample"),
yaxis=dict(title="Transmittance"),
legend=dict(orientation="h", y=1.08),
margin=dict(t=60, b=50, l=80, r=130),
height=460,
)
apply_theme(fig)
return fig
fig = generate()
fig.show()
Made with Plotly