{"spec_id":"scatter-regression-linear","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// scatter-regression-linear: Scatter Plot with Linear Regression\n// Library: highcharts 12.6.0 | JavaScript 22.23.1\n// Quality: 89/100 | Created: 2026-08-05\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: advertising spend vs. sales revenue, fixed-seed LCG -------------\nfunction lcg(seed) {\n  let state = seed >>> 0;\n  return function () {\n    state = (Math.imul(state, 1664525) + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\nconst rand = lcg(42);\nfunction randNormal() {\n  const u1 = Math.max(rand(), 1e-9);\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\nconst n = 160;\nconst points = [];\nfor (let i = 0; i < n; i++) {\n  const spend = 10 + rand() * 90; // advertising spend, $k\n  const revenue = 25 + 3.4 * spend + randNormal() * 45; // sales revenue, $k\n  points.push([spend, revenue]);\n}\n\n// --- Ordinary least-squares fit ---------------------------------------------\nconst xs = points.map((p) => p[0]);\nconst ys = points.map((p) => p[1]);\nconst xBar = xs.reduce((a, b) => a + b, 0) / n;\nconst yBar = ys.reduce((a, b) => a + b, 0) / n;\nlet sxx = 0;\nlet sxy = 0;\nlet syy = 0;\nfor (let i = 0; i < n; i++) {\n  const dx = xs[i] - xBar;\n  const dy = ys[i] - yBar;\n  sxx += dx * dx;\n  sxy += dx * dy;\n  syy += dy * dy;\n}\nconst slope = sxy / sxx;\nconst intercept = yBar - slope * xBar;\nconst r2 = (sxy * sxy) / (sxx * syy);\nconst sse = ys.reduce((acc, y, i) => acc + (y - (slope * xs[i] + intercept)) ** 2, 0);\nconst residualStdErr = Math.sqrt(sse / (n - 2));\nconst tCrit = 1.975; // ~95% two-tail critical value at df=158\n\n// Regression line + 95% CI band, sampled across the observed x-range.\nconst xMin = Math.min(...xs);\nconst xMax = Math.max(...xs);\nconst steps = 40;\nconst lineData = [];\nconst ciUpper = [];\nconst ciLower = [];\nfor (let i = 0; i <= steps; i++) {\n  const x0 = xMin + ((xMax - xMin) * i) / steps;\n  const yHat = slope * x0 + intercept;\n  const se = residualStdErr * Math.sqrt(1 / n + (x0 - xBar) ** 2 / sxx);\n  lineData.push([x0, yHat]);\n  ciUpper.push([x0, yHat + tCrit * se]);\n  ciLower.push([x0, yHat - tCrit * se]);\n}\n\n// --- Chart -------------------------------------------------------------------\n// arearange lives in highcharts-more (not vendored), so the CI band is drawn\n// as a plain SVG path in the core renderer, redrawn on every chart render.\nlet bandPath;\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"scatter\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n    events: {\n      render: function () {\n        const xAxis = this.xAxis[0];\n        const yAxis = this.yAxis[0];\n        const upper = ciUpper.map(\n          (p, i) => `${i === 0 ? \"M\" : \"L\"} ${xAxis.toPixels(p[0], false)} ${yAxis.toPixels(p[1], false)}`,\n        );\n        const lower = ciLower\n          .slice()\n          .reverse()\n          .map((p) => `L ${xAxis.toPixels(p[0], false)} ${yAxis.toPixels(p[1], false)}`);\n        const d = `${upper.join(\" \")} ${lower.join(\" \")} Z`;\n        if (bandPath) {\n          bandPath.attr({ d });\n        } else {\n          bandPath = this.renderer\n            .path()\n            .attr({ d, fill: Highcharts.color(t.palette[2]).setOpacity(0.18).get(), zIndex: 2 })\n            .add();\n        }\n      },\n    },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"Ad Spend vs. Sales Revenue · scatter-regression-linear · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: `R² = ${r2.toFixed(3)}  ·  y = ${slope.toFixed(2)}x + ${intercept.toFixed(1)}`,\n    style: { color: t.inkSoft, fontSize: \"15px\" },\n  },\n  xAxis: {\n    title: { text: \"Advertising Spend ($k)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    min: xMin - 2,\n    max: xMax + 2,\n  },\n  yAxis: {\n    title: { text: \"Sales Revenue ($k)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  legend: {\n    align: \"left\",\n    verticalAlign: \"top\",\n    x: 60,\n    y: 10,\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    pointFormat: \"Spend: {point.x:.1f}k<br/>Revenue: {point.y:.1f}k\",\n  },\n  plotOptions: {\n    series: { animation: false },\n  },\n  series: [\n    {\n      type: \"scatter\",\n      name: \"Observations\",\n      data: points,\n      zIndex: 5,\n      marker: {\n        radius: 5,\n        fillColor: Highcharts.color(t.palette[0]).setOpacity(0.65).get(),\n        lineColor: t.pageBg,\n        lineWidth: 0.5,\n      },\n    },\n    {\n      type: \"line\",\n      name: \"Regression fit\",\n      data: lineData,\n      color: t.palette[2],\n      lineWidth: 2.5,\n      zIndex: 4,\n      marker: { enabled: false },\n      enableMouseTracking: false,\n    },\n    {\n      type: \"column\",\n      name: \"95% Confidence Interval\",\n      data: [],\n      color: Highcharts.color(t.palette[2]).setOpacity(0.18).get(),\n      legendSymbol: \"rectangle\",\n      showInLegend: true,\n      enableMouseTracking: false,\n    },\n  ],\n});\n"}