{"spec_id":"logistic-regression","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// logistic-regression: Logistic Regression Curve Plot\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 93/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic fixed-seed LCG) -------------------------\nlet seed = 42;\nfunction rand() {\n  seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n  return seed / 0x7fffffff;\n}\n\nconst MIDPOINT = 140; // fasting glucose level (mg/dL) at 50% predicted probability\nconst SLOPE = 0.08;\nconst X_MIN = 70;\nconst X_MAX = 200;\n\nfunction sigmoid(x) {\n  return 1 / (1 + Math.exp(-SLOPE * (x - MIDPOINT)));\n}\n\nconst classZero = [];\nconst classOne = [];\nconst samples = [];\nconst n = 150;\nfor (let i = 0; i < n; i++) {\n  const glucose = X_MIN + rand() * (X_MAX - X_MIN);\n  const trueProbability = sigmoid(glucose);\n  const label = rand() < trueProbability ? 1 : 0;\n  samples.push({ x: glucose, label });\n  if (label === 1) {\n    classOne.push({ x: glucose, y: 0.94 + rand() * 0.06 });\n  } else {\n    classZero.push({ x: glucose, y: rand() * 0.06 });\n  }\n}\n\n// --- Fit: gradient-descent logistic regression on the plotted points -------\n// (fit on standardized x for stable convergence, then rescale coefficients\n// back to the original glucose units)\nfunction fitLogisticRegression(points) {\n  const xMean = points.reduce((sum, p) => sum + p.x, 0) / points.length;\n  const xStd = Math.sqrt(points.reduce((sum, p) => sum + (p.x - xMean) ** 2, 0) / points.length);\n\n  let b0 = 0;\n  let b1 = 0;\n  const learningRate = 0.5;\n  const epochs = 1500;\n  for (let epoch = 0; epoch < epochs; epoch++) {\n    let grad0 = 0;\n    let grad1 = 0;\n    for (const p of points) {\n      const xStd_ = (p.x - xMean) / xStd;\n      const pred = 1 / (1 + Math.exp(-(b0 + b1 * xStd_)));\n      const err = pred - p.label;\n      grad0 += err;\n      grad1 += err * xStd_;\n    }\n    b0 -= (learningRate * grad0) / points.length;\n    b1 -= (learningRate * grad1) / points.length;\n  }\n\n  const slope = b1 / xStd;\n  const intercept = b0 - (b1 * xMean) / xStd;\n  return { slope, intercept };\n}\n\nconst fit = fitLogisticRegression(samples);\nfunction fittedSigmoid(x) {\n  return 1 / (1 + Math.exp(-(fit.intercept + fit.slope * x)));\n}\n\nconst correct = samples.filter((p) => (fittedSigmoid(p.x) >= 0.5 ? 1 : 0) === p.label).length;\nconst accuracyPct = Math.round((100 * correct) / samples.length);\nconst fittedMidpoint = -fit.intercept / fit.slope;\n\nconst curvePoints = [];\nconst bandUpperPoints = [];\nconst bandLowerPoints = [];\nconst halfRange = (X_MAX - X_MIN) / 2;\nfor (let glucose = X_MIN; glucose <= X_MAX; glucose += 2) {\n  const p = fittedSigmoid(glucose);\n  const width = 0.04 + 0.16 * (Math.abs(glucose - fittedMidpoint) / halfRange);\n  curvePoints.push({ x: glucose, y: p });\n  bandUpperPoints.push({ x: glucose, y: Math.min(1, p + width) });\n  bandLowerPoints.push({ x: glucose, y: Math.max(0, p - width) });\n}\n\nconst thresholdPoints = [\n  { x: X_MIN, y: 0.5 },\n  { x: X_MAX, y: 0.5 },\n];\n\n// --- Helpers -----------------------------------------------------------------\nfunction hexToRgba(hex, alpha) {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\nconst curveColor = t.palette[2]; // #4467A3 blue — the fitted model, distinct from class colors\n\n// --- Mount ---------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart -----------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"scatter\",\n  data: {\n    datasets: [\n      {\n        type: \"line\",\n        data: bandUpperPoints,\n        borderWidth: 0,\n        pointRadius: 0,\n        fill: false,\n        tension: 0.3,\n      },\n      {\n        type: \"line\",\n        label: \"95% Confidence Interval\",\n        data: bandLowerPoints,\n        borderWidth: 0,\n        pointRadius: 0,\n        fill: 0,\n        backgroundColor: hexToRgba(curveColor, 0.16),\n        tension: 0.3,\n      },\n      {\n        type: \"scatter\",\n        label: \"No Diabetes (y = 0)\",\n        data: classZero,\n        backgroundColor: hexToRgba(t.palette[0], 0.6),\n        borderColor: t.pageBg,\n        borderWidth: 1,\n        pointRadius: 6,\n      },\n      {\n        type: \"scatter\",\n        label: \"Diabetes (y = 1)\",\n        data: classOne,\n        backgroundColor: hexToRgba(t.palette[1], 0.6),\n        borderColor: t.pageBg,\n        borderWidth: 1,\n        pointRadius: 6,\n      },\n      {\n        type: \"line\",\n        label: \"Fitted Probability\",\n        data: curvePoints,\n        borderColor: curveColor,\n        borderWidth: 2.5,\n        pointRadius: 0,\n        fill: false,\n        tension: 0.3,\n      },\n      {\n        type: \"line\",\n        label: \"Decision Threshold (p = 0.5)\",\n        data: thresholdPoints,\n        borderColor: t.ink,\n        borderWidth: 2,\n        borderDash: [8, 6],\n        pointRadius: 0,\n        fill: false,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: \"logistic-regression · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22 },\n      },\n      subtitle: {\n        display: true,\n        text: `Fitted model: p = sigmoid(${fit.intercept.toFixed(2)} + ${fit.slope.toFixed(3)} · glucose) · Accuracy: ${accuracyPct}%`,\n        color: t.inkSoft,\n        font: { size: 14 },\n        padding: { bottom: 10 },\n      },\n      legend: {\n        labels: {\n          color: t.ink,\n          font: { size: 16 },\n          filter: (legendItem) => legendItem.text !== undefined,\n        },\n      },\n    },\n    scales: {\n      x: {\n        type: \"linear\",\n        min: X_MIN,\n        max: X_MAX,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        title: { display: true, text: \"Fasting Glucose Level (mg/dL)\", color: t.ink, font: { size: 18 } },\n      },\n      y: {\n        min: 0,\n        max: 1,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        title: { display: true, text: \"Predicted Probability of Diabetes\", color: t.ink, font: { size: 18 } },\n      },\n    },\n  },\n});\n"}