{"spec_id":"probability-weibull","library":"echarts","language":"javascript","code":"// anyplot.ai\n// probability-weibull: Weibull Probability Plot for Reliability Analysis\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 90/100 | Created: 2026-06-07\n//# anyplot-orientation: landscape\n// anyplot.ai\n// probability-weibull: Weibull Probability Plot for Reliability Analysis\n// Library: echarts 5.5.1 | JavaScript 22\n// Quality: pending | Created: 2026-06-07\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: hydraulic pump service-life study (hours, n=20) ---\n// 14 failures, 6 right-censored (still running at inspection end)\n// Already sorted ascending by time — required for rank assignment\nconst observations = [\n  { time: 620,   fail: true  },\n  { time: 890,   fail: true  },\n  { time: 1100,  fail: false },\n  { time: 1340,  fail: true  },\n  { time: 1580,  fail: true  },\n  { time: 1920,  fail: false },\n  { time: 2200,  fail: true  },\n  { time: 2550,  fail: true  },\n  { time: 2900,  fail: false },\n  { time: 3300,  fail: true  },\n  { time: 3750,  fail: true  },\n  { time: 4100,  fail: true  },\n  { time: 4600,  fail: false },\n  { time: 5200,  fail: true  },\n  { time: 5900,  fail: true  },\n  { time: 6700,  fail: true  },\n  { time: 7800,  fail: false },\n  { time: 9200,  fail: true  },\n  { time: 11000, fail: true  },\n  { time: 13500, fail: false },\n];\n\nconst n = observations.length;\n\n// Median rank (i - 0.3) / (n + 0.4) where i = 1-based rank among ALL observations\nconst failurePoints = [];\nconst censoredTimes = [];\n\nobservations.forEach((obs, idx) => {\n  const i = idx + 1;\n  if (obs.fail) {\n    const F  = (i - 0.3) / (n + 0.4);\n    const yW = Math.log(-Math.log(1 - F));\n    failurePoints.push({ time: obs.time, F, yW });\n  } else {\n    censoredTimes.push(obs.time);\n  }\n});\n\n// Linear regression: log10(time) vs Weibull y  →  estimates beta (shape) and eta (scale)\nconst logT  = failurePoints.map(p => Math.log10(p.time));\nconst yVals = failurePoints.map(p => p.yW);\nconst nF    = failurePoints.length;\nconst mLogT = logT.reduce((a, b) => a + b, 0) / nF;\nconst mY    = yVals.reduce((a, b) => a + b, 0) / nF;\nlet ssXX = 0, ssXY = 0;\nfor (let i = 0; i < nF; i++) {\n  ssXX += (logT[i] - mLogT) ** 2;\n  ssXY += (logT[i] - mLogT) * (yVals[i] - mY);\n}\nconst beta      = ssXY / ssXX;                        // shape parameter β\nconst intercept = mY - beta * mLogT;\nconst eta       = Math.pow(10, -intercept / beta);    // characteristic life η (hours)\n\n// Fitted line spanning the full x range\nconst tMin = 350, tMax = 22000;\nconst fitData = [\n  [tMin, beta * Math.log10(tMin) + intercept],\n  [tMax, beta * Math.log10(tMax) + intercept],\n];\n\n// Censored markers: project each suspended time onto the fitted line for y-position\nconst censoredData = censoredTimes.map(time => [\n  time,\n  beta * Math.log10(time) + intercept,\n]);\n\n// Parameter labels\nconst betaStr = beta.toFixed(2);\nconst etaStr  = Math.round(eta).toLocaleString();\n\n// --- Init ---\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n\n  title: {\n    text: \"probability-weibull · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: \"bold\" },\n  },\n\n  legend: {\n    top: 65,\n    left: \"center\",\n    textStyle: { color: t.inkSoft, fontSize: 14 },\n    itemGap: 32,\n    data: [\n      { name: \"Failure\", icon: \"circle\" },\n      { name: \"Censored (suspended)\", icon: \"circle\" },\n      { name: \"Weibull Fit\" },\n      { name: \"63.2% Reference\" },\n    ],\n  },\n\n  grid: { left: 115, right: 70, top: 110, bottom: 90 },\n\n  xAxis: {\n    type: \"log\",\n    name: \"Time to Failure (hours)\",\n    nameLocation: \"middle\",\n    nameGap: 52,\n    nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n    min: tMin,\n    max: tMax,\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 13,\n      formatter: val => {\n        if (val >= 10000) return (val / 1000).toFixed(0) + \"k\";\n        if (val >= 1000)  return (val / 1000).toFixed(0) + \"k\";\n        return String(val);\n      },\n    },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { lineStyle: { color: t.grid, type: \"dashed\" } },\n    minorSplitLine: { show: true, lineStyle: { color: t.grid, opacity: 0.45 } },\n  },\n\n  yAxis: {\n    type: \"value\",\n    name: \"Cumulative Failure Probability\",\n    nameLocation: \"middle\",\n    nameGap: 88,\n    nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n    min: -4.5,\n    max: 2.0,\n    interval: 1,\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 13,\n      // Convert Weibull y-value back to a readable probability percentage\n      formatter: val => {\n        if (val < -4.3 || val > 1.85) return \"\";\n        const F   = 1 - Math.exp(-Math.exp(val));\n        const pct = F * 100;\n        return pct < 1 ? pct.toFixed(1) + \"%\" : Math.round(pct) + \"%\";\n      },\n    },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n\n  series: [\n    // Weibull fitted line\n    {\n      name: \"Weibull Fit\",\n      type: \"line\",\n      data: fitData,\n      symbol: \"none\",\n      lineStyle: { color: t.palette[2], width: 3 },\n      z: 2,\n    },\n    // Horizontal reference at 63.2% cumulative probability (y = 0)\n    {\n      name: \"63.2% Reference\",\n      type: \"line\",\n      data: [[tMin, 0], [tMax, 0]],\n      symbol: \"none\",\n      lineStyle: { color: t.palette[3], width: 2, type: \"dashed\" },\n      z: 1,\n    },\n    // Failure observations — filled circles\n    {\n      name: \"Failure\",\n      type: \"scatter\",\n      data: failurePoints.map(p => [p.time, p.yW]),\n      symbolSize: 16,\n      itemStyle: {\n        color: t.palette[0],\n        borderColor: t.pageBg,\n        borderWidth: 1.5,\n      },\n      z: 3,\n    },\n    // Censored (suspended) observations — hollow circles at projected y position\n    {\n      name: \"Censored (suspended)\",\n      type: \"scatter\",\n      data: censoredData,\n      symbolSize: 16,\n      itemStyle: {\n        color: \"transparent\",\n        borderColor: t.inkSoft,\n        borderWidth: 2,\n      },\n      z: 3,\n    },\n  ],\n\n  // Weibull parameter annotation\n  graphic: [\n    {\n      type: \"group\",\n      left: \"66%\",\n      top: \"18%\",\n      children: [\n        {\n          type: \"rect\",\n          shape: { width: 200, height: 66, r: 4 },\n          style: { fill: t.elevatedBg, stroke: t.grid, lineWidth: 1 },\n        },\n        {\n          type: \"text\",\n          style: {\n            text: `β (shape)  =  ${betaStr}`,\n            fill: t.ink,\n            fontSize: 15,\n            fontWeight: \"bold\",\n            x: 14,\n            y: 14,\n          },\n        },\n        {\n          type: \"text\",\n          style: {\n            text: `η (char. life)  =  ${etaStr} h`,\n            fill: t.ink,\n            fontSize: 15,\n            fontWeight: \"bold\",\n            x: 14,\n            y: 38,\n          },\n        },\n      ],\n    },\n  ],\n});\n"}