{"spec_id":"histogram-capability","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nhistogram-capability: Process Capability Plot with Specification Limits\nLibrary: altair 6.2.1 | Python 3.13.14\nQuality: 92/100 | Updated: 2026-06-20\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent self-import: this script is named altair.py; drop its directory from\n# sys.path so `import altair` resolves to the installed package, not this file.\n_here = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.realpath(p or \".\") != os.path.realpath(_here)]\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom scipy import stats\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — semantic roles\nBRAND = \"#009E73\"  # histogram bars (brand green)\nCOLOR_LIMIT = \"#AE3030\"  # LSL/USL lines (semantic: out-of-spec = bad/red)\nCOLOR_TARGET = \"#4467A3\"  # target / nominal line (blue)\n\n# Data\nnp.random.seed(42)\nn_measurements = 200\ntarget = 10.00\nlsl = 9.95\nusl = 10.05\nmeasurements = np.random.normal(loc=10.002, scale=0.012, size=n_measurements)\n\nmean_val = measurements.mean()\nsigma = measurements.std(ddof=1)\ncp = (usl - lsl) / (6 * sigma)\ncpk = min((usl - mean_val) / (3 * sigma), (mean_val - lsl) / (3 * sigma))\n\ndf = pd.DataFrame({\"diameter\": measurements})\n\nx_lo, x_hi = lsl - 0.012, usl + 0.012\nx_scale = alt.Scale(domain=[x_lo, x_hi])\nbin_step = 0.004\n\n# Background zones (out-of-spec / in-spec)\nzone_df = pd.DataFrame({\"x\": [x_lo, lsl, usl], \"x2\": [lsl, usl, x_hi], \"zone\": [\"oos\", \"spec\", \"oos\"]})\nzones = (\n    alt.Chart(zone_df)\n    .mark_rect(opacity=0.12)\n    .encode(\n        x=alt.X(\"x:Q\", scale=x_scale),\n        x2=\"x2:Q\",\n        color=alt.Color(\"zone:N\", scale=alt.Scale(domain=[\"oos\", \"spec\"], range=[COLOR_LIMIT, BRAND]), legend=None),\n    )\n)\n\n# Idiomatic histogram — mark_bar with built-in bin transform\nhover = alt.selection_point(on=\"pointerover\", empty=False)\nhistogram = (\n    alt.Chart(df)\n    .mark_bar(stroke=\"white\", strokeWidth=0.8, cornerRadiusTopLeft=2, cornerRadiusTopRight=2)\n    .add_params(hover)\n    .encode(\n        x=alt.X(\n            \"diameter:Q\", bin=alt.Bin(step=bin_step, extent=[x_lo, x_hi]), title=\"Shaft Diameter (mm)\", scale=x_scale\n        ),\n        y=alt.Y(\"count():Q\", title=\"Frequency\"),\n        color=alt.value(BRAND),\n        opacity=alt.condition(hover, alt.value(1.0), alt.value(0.80)),\n        tooltip=[alt.Tooltip(\"diameter:Q\", bin=True, title=\"Range\"), alt.Tooltip(\"count():Q\", title=\"Count\")],\n    )\n)\n\n# Fitted normal distribution curve\nx_curve = np.linspace(x_lo, x_hi, 300)\ny_curve = stats.norm.pdf(x_curve, mean_val, sigma) * n_measurements * bin_step\ncurve_df = pd.DataFrame({\"x\": x_curve, \"y\": y_curve})\ncurve = (\n    alt.Chart(curve_df)\n    .mark_line(color=INK, strokeWidth=2.5, opacity=0.85, interpolate=\"monotone\")\n    .encode(x=alt.X(\"x:Q\", scale=x_scale), y=\"y:Q\")\n)\n\n# Specification limit lines (LSL / USL)\nspec_rules = (\n    alt.Chart(pd.DataFrame({\"value\": [lsl, usl]}))\n    .mark_rule(color=COLOR_LIMIT, strokeWidth=2.5, strokeDash=[8, 4])\n    .encode(x=alt.X(\"value:Q\", scale=x_scale))\n)\n\n# Target line\ntarget_rule = (\n    alt.Chart(pd.DataFrame({\"value\": [target]}))\n    .mark_rule(color=COLOR_TARGET, strokeWidth=2.0, strokeDash=[4, 3])\n    .encode(x=alt.X(\"value:Q\", scale=x_scale))\n)\n\n# Mean line\nmean_rule = (\n    alt.Chart(pd.DataFrame({\"value\": [mean_val]}))\n    .mark_rule(color=INK_MUTED, strokeWidth=1.5, strokeDash=[2, 2])\n    .encode(x=alt.X(\"value:Q\", scale=x_scale))\n)\n\n# Spec limit and target labels (near top of plot area)\nlsl_label = (\n    alt.Chart(pd.DataFrame({\"v\": [lsl], \"t\": [\"LSL 9.950\"]}))\n    .mark_text(align=\"right\", dx=-6, fontSize=11, fontWeight=\"bold\", color=COLOR_LIMIT)\n    .encode(x=alt.X(\"v:Q\", scale=x_scale), y=alt.value(12), text=\"t:N\")\n)\nusl_label = (\n    alt.Chart(pd.DataFrame({\"v\": [usl], \"t\": [\"USL 10.050\"]}))\n    .mark_text(align=\"left\", dx=6, fontSize=11, fontWeight=\"bold\", color=COLOR_LIMIT)\n    .encode(x=alt.X(\"v:Q\", scale=x_scale), y=alt.value(12), text=\"t:N\")\n)\ntarget_label = (\n    alt.Chart(pd.DataFrame({\"v\": [target], \"t\": [\"Target 10.000\"]}))\n    .mark_text(align=\"center\", fontSize=11, fontWeight=\"bold\", color=COLOR_TARGET)\n    .encode(x=alt.X(\"v:Q\", scale=x_scale), y=alt.value(12), text=\"t:N\")\n)\n\n# Capability indices and status annotation (top-right)\nstatus = \"CAPABLE\" if cpk >= 1.33 else \"NOT CAPABLE\"\nstatus_color = BRAND if cpk >= 1.33 else COLOR_LIMIT\nannot_df = pd.DataFrame({\"x\": [x_hi - 0.002]})\ncap_text = (\n    alt.Chart(annot_df)\n    .mark_text(align=\"right\", fontSize=12, fontWeight=\"bold\", color=INK)\n    .encode(x=alt.X(\"x:Q\", scale=x_scale), y=alt.value(28), text=alt.value(f\"Cp = {cp:.2f}   Cpk = {cpk:.2f}\"))\n)\nstatus_text = (\n    alt.Chart(annot_df)\n    .mark_text(align=\"right\", fontSize=11, fontWeight=\"bold\", color=status_color)\n    .encode(x=alt.X(\"x:Q\", scale=x_scale), y=alt.value(46), text=alt.value(status))\n)\n\n# Mean value label (near bottom of plot)\nmean_label = (\n    alt.Chart(pd.DataFrame({\"v\": [mean_val]}))\n    .mark_text(align=\"center\", baseline=\"top\", dy=4, fontSize=11, fontWeight=\"bold\", color=INK_MUTED)\n    .encode(x=alt.X(\"v:Q\", scale=x_scale), y=alt.value(300), text=alt.value(f\"x̄={mean_val:.3f}\"))\n)\n\n# Compose\ntitle_str = \"histogram-capability · python · altair · anyplot.ai\"\nn_chars = len(title_str)\nratio = 67 / n_chars if n_chars > 67 else 1.0\ntitle_fontsize = max(11, round(16 * ratio))\n\nchart = (\n    alt.layer(\n        zones,\n        histogram,\n        curve,\n        spec_rules,\n        target_rule,\n        mean_rule,\n        lsl_label,\n        usl_label,\n        target_label,\n        cap_text,\n        status_text,\n        mean_label,\n    )\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        padding={\"left\": 0, \"right\": 0, \"top\": 0, \"bottom\": 0},\n        title=alt.Title(\n            title_str,\n            fontSize=title_fontsize,\n            fontWeight=\"bold\",\n            anchor=\"start\",\n            color=INK,\n            offset=12,\n            subtitle=f\"n={n_measurements}   σ={sigma:.4f} mm   centered at {mean_val:.3f} mm\",\n            subtitleFontSize=12,\n            subtitleColor=INK_SOFT,\n        ),\n    )\n    .configure_axis(\n        labelFontSize=10,\n        titleFontSize=12,\n        titleColor=INK,\n        labelColor=INK_SOFT,\n        grid=False,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n    )\n    .configure_view(fill=PAGE_BG, strokeWidth=0)\n    .configure_title(color=INK)\n    .configure_legend(disable=True)\n)\n\n# Save — canonical 3200 × 1800 landscape with PIL padding\nTW, TH = 3200, 1800\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\nchart.save(f\"plot-{THEME}.html\")\n"}