{"spec_id":"probability-weibull","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nprobability-weibull: Weibull Probability Plot for Reliability Analysis\nLibrary: plotly 6.8.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-07\n\"\"\"\n\nimport os\nimport sys\n\n\n# Script name matches the library — remove script dir to avoid shadowing the package\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _script_dir]\n\nimport numpy as np\nimport plotly.graph_objects as go\nfrom scipy import stats\n\n\n# Theme-adaptive chrome — Imprint palette\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\nGRID_MINOR = \"rgba(26,26,23,0.07)\" if THEME == \"light\" else \"rgba(240,239,232,0.07)\"\n\n# Imprint palette — positions 1 & 2\nFAILURE_COLOR = \"#009E73\"  # brand green — failures / fit line\nCENSORED_COLOR = \"#C475FD\"  # lavender — censored/suspended\n\nfont_family = \"Helvetica Neue, Helvetica, Arial, sans-serif\"\n\n# Data — turbine blade fatigue life (hours)\nnp.random.seed(42)\nn_failures = 25\nn_censored = 5\nn_total = n_failures + n_censored\n\nshape_true = 2.5\nscale_true = 5000\nfailure_times = np.sort(stats.weibull_min.rvs(shape_true, scale=scale_true, size=n_failures))\ncensored_times = np.sort(np.random.uniform(1000, 4500, size=n_censored))\n\nall_times = np.concatenate([failure_times, censored_times])\nis_censored = np.concatenate([np.zeros(n_failures, dtype=bool), np.ones(n_censored, dtype=bool)])\nsort_idx = np.argsort(all_times)\nall_times = all_times[sort_idx]\nis_censored = is_censored[sort_idx]\n\n# Median rank plotting positions for failures only\nfailure_ranks = np.zeros(n_total)\nrank = 0\nfor i in range(n_total):\n    if not is_censored[i]:\n        rank += 1\n        failure_ranks[i] = (rank - 0.3) / (n_failures + 0.4)\n\nfailure_mask = ~is_censored\nfailure_t = all_times[failure_mask]\nfailure_prob = failure_ranks[failure_mask]\n\n# Weibull linearization: y = ln(-ln(1-F))\nweibull_y_failures = np.log(-np.log(1 - failure_prob))\n\n# Fit line in Weibull space\nslope, intercept, r_value, _, _ = stats.linregress(np.log(failure_t), weibull_y_failures)\nbeta_fit = slope\neta_fit = np.exp(-intercept / beta_fit)\n\n# Probability axis ticks (Weibull paper y-axis)\nprob_ticks = [0.01, 0.02, 0.05, 0.10, 0.20, 0.50, 0.632, 0.90, 0.95, 0.99]\nprob_labels = [\"1%\", \"2%\", \"5%\", \"10%\", \"20%\", \"50%\", \"63.2%\", \"90%\", \"95%\", \"99%\"]\nweibull_tick_vals = [np.log(-np.log(1 - p)) for p in prob_ticks]\n\n# Confidence band (90% bounds for fitted line)\nt_range = np.logspace(np.log10(failure_t.min() * 0.5), np.log10(failure_t.max() * 1.5), 200)\nfit_weibull_y = beta_fit * np.log(t_range) - beta_fit * np.log(eta_fit)\nse_fit = np.sqrt(\n    np.sum((weibull_y_failures - (beta_fit * np.log(failure_t) - beta_fit * np.log(eta_fit))) ** 2) / (n_failures - 2)\n)\nconf_upper = fit_weibull_y + 1.645 * se_fit\nconf_lower = fit_weibull_y - 1.645 * se_fit\n\n# Figure\nfig = go.Figure()\n\n# 90% confidence band (trace 0 — toggleable via update menu)\nband_fill = \"rgba(0,158,115,0.10)\" if THEME == \"light\" else \"rgba(0,158,115,0.16)\"\nfig.add_trace(\n    go.Scatter(\n        x=np.concatenate([t_range, t_range[::-1]]),\n        y=np.concatenate([conf_upper, conf_lower[::-1]]),\n        fill=\"toself\",\n        fillcolor=band_fill,\n        line={\"width\": 0},\n        name=\"90% Confidence Band\",\n        hoverinfo=\"skip\",\n        showlegend=True,\n    )\n)\n\n# Fitted line (trace 1)\nfig.add_trace(\n    go.Scatter(\n        x=t_range,\n        y=fit_weibull_y,\n        mode=\"lines\",\n        name=\"Weibull Fit\",\n        line={\"color\": FAILURE_COLOR, \"width\": 3},\n        hovertemplate=\"Time: %{x:.0f}h<br>Probability: %{customdata:.1%}<extra>Weibull Fit</extra>\",\n        customdata=1 - np.exp(-np.exp(fit_weibull_y)),\n    )\n)\n\n# Failure data points (trace 2)\nfig.add_trace(\n    go.Scatter(\n        x=failure_t,\n        y=weibull_y_failures,\n        mode=\"markers\",\n        name=\"Failures\",\n        marker={\"size\": 14, \"color\": FAILURE_COLOR, \"line\": {\"color\": PAGE_BG, \"width\": 2}, \"opacity\": 0.9},\n        hovertemplate=(\n            \"<b>Failure #%{text}</b><br>Time: %{x:.0f} hours<br>Cum. Probability: %{customdata:.1%}<extra></extra>\"\n        ),\n        customdata=failure_prob,\n        text=[f\"{i + 1}/{n_failures}\" for i in range(n_failures)],\n    )\n)\n\n# Censored data points (trace 3)\ncensored_t = all_times[is_censored]\ncensored_weibull_y = beta_fit * np.log(censored_t) - beta_fit * np.log(eta_fit)\ncensored_prob_est = 1 - np.exp(-np.exp(censored_weibull_y))\nfig.add_trace(\n    go.Scatter(\n        x=censored_t,\n        y=censored_weibull_y,\n        mode=\"markers\",\n        name=\"Censored (suspended)\",\n        marker={\n            \"size\": 14,\n            \"color\": \"rgba(196,117,253,0.15)\",\n            \"line\": {\"color\": CENSORED_COLOR, \"width\": 2.5},\n            \"symbol\": \"diamond\",\n        },\n        hovertemplate=(\n            \"<b>Censored Observation</b><br>Time: %{x:.0f} hours<br>Est. Probability: %{customdata:.1%}<extra></extra>\"\n        ),\n        customdata=censored_prob_est,\n    )\n)\n\n# 63.2% characteristic life reference line\nweibull_632 = np.log(-np.log(1 - 0.632))\nfig.add_hline(\n    y=weibull_632,\n    line_dash=\"dot\",\n    line_color=INK_MUTED,\n    line_width=1.5,\n    annotation_text=f\"63.2% — η ≈ {eta_fit:.0f}h\",\n    annotation_position=\"bottom left\",\n    annotation_font={\"size\": 10, \"color\": INK_MUTED, \"family\": font_family},\n)\nfig.add_vline(x=eta_fit, line_dash=\"dot\", line_color=INK_MUTED, line_width=1, opacity=0.5)\n\n# Parameter box (lower-right corner, paper coordinates to avoid log-scale issues)\nannot_bg = \"rgba(255,253,246,0.93)\" if THEME == \"light\" else \"rgba(36,36,32,0.93)\"\nannot_border = \"rgba(0,158,115,0.35)\"\nfig.add_annotation(\n    x=0.98,\n    y=0.05,\n    xref=\"paper\",\n    yref=\"paper\",\n    text=(\n        f\"<b>Weibull Parameters</b><br>\"\n        f\"β (shape) = {beta_fit:.2f}<br>\"\n        f\"η (scale) = {eta_fit:.0f}h<br>\"\n        f\"R² = {r_value**2:.4f}\"\n    ),\n    showarrow=False,\n    font={\"size\": 11, \"color\": INK, \"family\": font_family},\n    align=\"left\",\n    bgcolor=annot_bg,\n    bordercolor=annot_border,\n    borderwidth=1.5,\n    borderpad=10,\n    xanchor=\"right\",\n    yanchor=\"bottom\",\n)\n\n# B10 life annotation — points to actual data coordinate\nb10_life = eta_fit * (-np.log(1 - 0.10)) ** (1 / beta_fit)\nweibull_10 = np.log(-np.log(1 - 0.10))\nfig.add_annotation(\n    x=b10_life,\n    y=weibull_10,\n    xref=\"x\",\n    yref=\"y\",\n    text=f\"B10 = {b10_life:.0f}h\",\n    showarrow=True,\n    arrowhead=2,\n    arrowsize=1,\n    arrowcolor=INK_SOFT,\n    ax=55,\n    ay=28,\n    font={\"size\": 10, \"color\": INK_MUTED, \"family\": font_family},\n    bgcolor=annot_bg,\n    bordercolor=\"rgba(26,26,23,0.12)\" if THEME == \"light\" else \"rgba(240,239,232,0.12)\",\n    borderwidth=1,\n    borderpad=5,\n)\n\n# Update menu — toggle confidence band on/off (LM-01 advanced Plotly pattern)\nfig.update_layout(\n    updatemenus=[\n        {\n            \"type\": \"buttons\",\n            \"showactive\": True,\n            \"x\": 0.99,\n            \"y\": 0.99,\n            \"xanchor\": \"right\",\n            \"yanchor\": \"top\",\n            \"bgcolor\": ELEVATED_BG,\n            \"bordercolor\": INK_SOFT,\n            \"font\": {\"color\": INK_SOFT, \"size\": 10, \"family\": font_family},\n            \"buttons\": [\n                {\"label\": \"Show Band\", \"method\": \"update\", \"args\": [{\"visible\": [True, True, True, True]}]},\n                {\"label\": \"Hide Band\", \"method\": \"update\", \"args\": [{\"visible\": [False, True, True, True]}]},\n            ],\n        }\n    ]\n)\n\n# Layout\nfig.update_layout(\n    autosize=False,\n    width=800,\n    height=450,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK, \"family\": font_family},\n    template=\"plotly_white\",\n    title={\n        \"text\": \"probability-weibull · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 16, \"family\": font_family, \"color\": INK},\n        \"x\": 0.5,\n        \"y\": 0.98,\n        \"xanchor\": \"center\",\n        \"yanchor\": \"top\",\n    },\n    xaxis={\n        \"title\": {\n            \"text\": \"Time to Failure (hours)\",\n            \"font\": {\"size\": 12, \"family\": font_family, \"color\": INK},\n            \"standoff\": 15,\n        },\n        \"tickfont\": {\"size\": 10, \"family\": font_family, \"color\": INK_SOFT},\n        \"type\": \"log\",\n        \"showgrid\": True,\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"showline\": False,\n        \"minor\": {\"showgrid\": True, \"gridcolor\": GRID_MINOR},\n        \"zeroline\": False,\n    },\n    yaxis={\n        \"title\": {\n            \"text\": \"Cumulative Failure Probability (Weibull Scale)\",\n            \"font\": {\"size\": 12, \"family\": font_family, \"color\": INK},\n            \"standoff\": 10,\n        },\n        \"tickfont\": {\"size\": 10, \"family\": font_family, \"color\": INK_SOFT},\n        \"tickmode\": \"array\",\n        \"tickvals\": weibull_tick_vals,\n        \"ticktext\": prob_labels,\n        \"showgrid\": True,\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"showline\": False,\n        \"range\": [weibull_tick_vals[0] - 0.3, weibull_tick_vals[-1] + 0.3],\n        \"zeroline\": False,\n    },\n    legend={\n        \"font\": {\"size\": 10, \"family\": font_family, \"color\": INK_SOFT},\n        \"x\": 0.02,\n        \"y\": 0.98,\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n    },\n    margin={\"l\": 90, \"r\": 40, \"t\": 80, \"b\": 60},\n    hoverlabel={\"font\": {\"size\": 10, \"family\": font_family}, \"bgcolor\": ELEVATED_BG, \"bordercolor\": FAILURE_COLOR},\n)\n\n# Save\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}