{"spec_id":"probability-weibull","library":"makie","language":"julia","code":"# anyplot.ai\n# probability-weibull: Weibull Probability Plot for Reliability Analysis\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 91/100 | Created: 2026-06-07\n\nusing CairoMakie\nusing Colors\nusing Random\nusing Statistics\n\nRandom.seed!(42)\n\n# Theme tokens — Imprint palette\nconst THEME       = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG     = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst ELEVATED_BG = THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"\nconst INK         = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT    = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst INK_MUTED   = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\n\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\",  # 1 — brand green (failure data points)\n    colorant\"#C475FD\",  # 2 — lavender\n    colorant\"#4467A3\",  # 3 — blue (Weibull fit line)\n    colorant\"#BD8233\",  # 4 — ochre (censored points)\n    colorant\"#AE3030\",  # 5 — matte red\n    colorant\"#2ABCCD\",  # 6 — cyan\n    colorant\"#954477\",  # 7 — rose\n    colorant\"#99B314\",  # 8 — lime\n]\n\n# Data: bearing fatigue-life qualification test (hours)\n# True Weibull: shape β = 2.5, characteristic life η = 8500 h\nn_total   = 35\nbeta_true = 2.5\neta_true  = 8500.0\n\nall_lifetimes  = eta_true .* (-log.(rand(n_total))).^(1.0 / beta_true)\ncensor_time    = 9200.0   # inspection end time — survivors are right-censored here\n\nobserved_times  = min.(all_lifetimes, censor_time)\ncensored_mask   = all_lifetimes .> censor_time\n\n# Sort all observations by observed time\norder           = sortperm(observed_times)\ntimes_sorted    = observed_times[order]\ncensored_sorted = censored_mask[order]\n\n# Johnson's method: adjusted mean order number for each failure\n# accounting for right-censored suspensions\nadj_ranks, failure_times, cens_times = let\n    prev = 0.0\n    _ranks = Float64[]\n    _ftimes = Float64[]\n    _ctimes = Float64[]\n    for i in 1:n_total\n        if !censored_sorted[i]\n            prev += (n_total + 1 - prev) / (n_total - i + 2)\n            push!(_ranks, prev)\n            push!(_ftimes, times_sorted[i])\n        else\n            push!(_ctimes, times_sorted[i])\n        end\n    end\n    _ranks, _ftimes, _ctimes\nend\n\n# Bernard's median rank: F_i = (rank_i - 0.3) / (n + 0.4)\nF_vals  = clamp.((adj_ranks .- 0.3) ./ (n_total + 0.4), 1e-6, 1.0 - 1e-6)\n\n# Weibull linearization: y = ln(−ln(1−F)), x = ln(t)\nlog_t_fail = log.(failure_times)\ny_fail     = log.(-log.(1.0 .- F_vals))\n\n# OLS regression in linearized space → β̂, η̂\nx_bar     = mean(log_t_fail)\ny_bar     = mean(y_fail)\nbeta_hat  = sum((log_t_fail .- x_bar) .* (y_fail .- y_bar)) /\n             sum((log_t_fail .- x_bar).^2)\nintercept = y_bar - beta_hat * x_bar\neta_hat   = exp(-intercept / beta_hat)\n\n# B10 life: time at which 10% of units have failed\nt_b10 = exp((log(-log(1.0 - 0.10)) - intercept) / beta_hat)\n\n# Fitted line spanning the observed time range\nt_lo  = minimum(times_sorted) * 0.65\nt_hi  = maximum(times_sorted) * 1.40\nt_fit = exp.(range(log(t_lo), log(t_hi); length = 200))\ny_fit = beta_hat .* log.(t_fit) .+ intercept\n\n# Custom y-axis ticks on the Weibull probability scale\nprob_pct    = [1.0, 5.0, 10.0, 20.0, 50.0, 63.2, 90.0, 99.0]\ny_tick_vals = log.(-log.(1.0 .- prob_pct ./ 100.0))\ny_tick_lbls = [\"1%\", \"5%\", \"10%\", \"20%\", \"50%\", \"63.2%\", \"90%\", \"99%\"]\n\n# Grid color: INK at 15% opacity\ngrid_col = RGBAf(INK.r, INK.g, INK.b, 0.15f0)\n\n# Figure\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    xscale            = log10,\n    title             = \"probability-weibull · julia · makie · anyplot.ai\",\n    titlesize         = 20,\n    titlecolor        = INK,\n    xlabel            = \"Time to Failure (hours)\",\n    ylabel            = \"Cumulative Failure Probability\",\n    xlabelsize        = 14,\n    ylabelsize        = 14,\n    xticklabelsize    = 12,\n    yticklabelsize    = 12,\n    xlabelcolor       = INK,\n    ylabelcolor       = INK,\n    xticklabelcolor   = INK_SOFT,\n    yticklabelcolor   = INK_SOFT,\n    xtickcolor        = INK_SOFT,\n    ytickcolor        = INK_SOFT,\n    backgroundcolor   = PAGE_BG,\n    topspinevisible   = false,\n    rightspinevisible = false,\n    leftspinecolor    = INK_SOFT,\n    bottomspinecolor  = INK_SOFT,\n    xgridcolor        = grid_col,\n    ygridcolor        = grid_col,\n    yticks            = (y_tick_vals, y_tick_lbls),\n)\n\n# 63.2% reference line — time axis spans the full x range\ny_632 = log(-log(1.0 - 0.632))\nhlines!(ax, [y_632]; color = INK_MUTED, linewidth = 1.5, linestyle = :dash,\n        label = \"63.2% (η)\")\n\n# Fitted Weibull line\nlines!(ax, t_fit, y_fit; color = IMPRINT_PALETTE[3], linewidth = 2.5,\n       label = \"Weibull fit\")\n\n# Failure observations — filled circles (first Imprint series, brand green)\nscatter!(ax, failure_times, y_fail;\n    color       = IMPRINT_PALETTE[1],\n    markersize  = 12,\n    marker      = :circle,\n    strokewidth = 0,\n    label       = \"Failure\",\n)\n\n# Censored observations — hollow circles at their time, y interpolated from fit\nif length(cens_times) > 0\n    y_cens = beta_hat .* log.(cens_times) .+ intercept\n    scatter!(ax, cens_times, y_cens;\n        color       = :transparent,\n        strokecolor = IMPRINT_PALETTE[4],\n        strokewidth = 2.0,\n        markersize  = 12,\n        marker      = :circle,\n        label       = \"Censored\",\n    )\nend\n\n# Parameter annotation (upper-left, away from dense data region)\nann_x   = t_lo * 1.6\nann_y   = y_tick_vals[end] - 0.10\nann_txt = \"β = $(round(beta_hat, digits=2))\\nη = $(round(Int, eta_hat)) h\\nB10 = $(round(Int, t_b10)) h\"\ntext!(ax, [ann_x], [ann_y];\n    text    = [ann_txt],\n    fontsize = 13,\n    color   = INK_SOFT,\n    align   = (:left, :top),\n)\n\n# B10 life graphical marker — vertical dashed line from bottom of plot to the 10% level\ny_b10 = log(-log(1.0 - 0.10))\nlines!(ax, [t_b10, t_b10], [y_tick_vals[1] - 0.35, y_b10];\n    color     = INK_MUTED,\n    linewidth = 1.5,\n    linestyle = :dash,\n)\nscatter!(ax, [t_b10], [y_b10];\n    color       = INK_SOFT,\n    marker      = :diamond,\n    markersize  = 10,\n    strokewidth = 0,\n)\ntext!(ax, [t_b10], [y_b10 + 0.08];\n    text     = [\"B10\"],\n    fontsize = 12,\n    color    = INK_SOFT,\n    align    = (:center, :bottom),\n)\n\n# Legend\naxislegend(ax;\n    position        = :rb,\n    backgroundcolor = ELEVATED_BG,\n    framecolor      = INK_SOFT,\n    labelcolor      = INK,\n    labelsize       = 11,\n)\n\n# Constrain y to the tick range with small padding\nylims!(ax, y_tick_vals[1] - 0.35, y_tick_vals[end] + 0.30)\n\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}