{"spec_id":"scatter-regression-linear","library":"echarts","language":"javascript","code":"// anyplot.ai\n// scatter-regression-linear: Scatter Plot with Linear Regression\n// Library: echarts 6.1.0 | JavaScript 22.23.1\n// Quality: 89/100 | Created: 2026-08-05\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic mulberry32 PRNG) ------------------------\nfunction mulberry32(seed) {\n  return function () {\n    seed = (seed + 0x6d2b79f5) | 0;\n    let x = Math.imul(seed ^ (seed >>> 15), 1 | seed);\n    x = (x + Math.imul(x ^ (x >>> 7), 61 | x)) ^ x;\n    return ((x ^ (x >>> 14)) >>> 0) / 4294967296;\n  };\n}\nconst rng = mulberry32(42);\nconst randNormal = () => {\n  const u1 = Math.max(rng(), 1e-9);\n  const u2 = rng();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n};\n\nconst n = 70;\nconst trueSlope = 4.8;\nconst trueIntercept = 35;\nconst adSpend = [];\nconst salesRevenue = [];\nfor (let i = 0; i < n; i++) {\n  const spend = 5 + rng() * 55; // $1,000s of weekly ad spend\n  const revenue = Math.max(\n    8,\n    trueIntercept + trueSlope * spend + randNormal() * 32,\n  );\n  adSpend.push(spend);\n  salesRevenue.push(revenue);\n}\n\n// --- Ordinary least squares fit + 95% confidence band -----------------------\nconst meanX = adSpend.reduce((a, b) => a + b, 0) / n;\nconst meanY = salesRevenue.reduce((a, b) => a + b, 0) / n;\nlet sumXY = 0;\nlet sumXX = 0;\nfor (let i = 0; i < n; i++) {\n  sumXY += (adSpend[i] - meanX) * (salesRevenue[i] - meanY);\n  sumXX += (adSpend[i] - meanX) ** 2;\n}\nconst slope = sumXY / sumXX;\nconst intercept = meanY - slope * meanX;\n\nlet sse = 0;\nfor (let i = 0; i < n; i++) {\n  sse += (salesRevenue[i] - (slope * adSpend[i] + intercept)) ** 2;\n}\nconst sst = salesRevenue.reduce((a, y) => a + (y - meanY) ** 2, 0);\nconst rSquared = 1 - sse / sst;\nconst df = n - 2;\nconst residualSe = Math.sqrt(sse / df);\n// 95% two-sided t-quantile via Cornish-Fisher expansion (avoids a t-table)\nconst z = 1.959964;\nconst tCrit = z + (z ** 3 + z) / (4 * df);\n\nconst xMin = Math.min(...adSpend);\nconst xMax = Math.max(...adSpend);\nconst gridPoints = 40;\nconst fitLine = [];\nconst ciLower = [];\nconst ciBand = [];\nfor (let i = 0; i <= gridPoints; i++) {\n  const x = xMin + (i / gridPoints) * (xMax - xMin);\n  const yFit = slope * x + intercept;\n  const se = residualSe * Math.sqrt(1 / n + (x - meanX) ** 2 / sumXX);\n  const margin = tCrit * se;\n  fitLine.push([x, yFit]);\n  ciLower.push([x, yFit - margin]);\n  ciBand.push([x, 2 * margin]);\n}\n\nconst equation = `y = ${slope.toFixed(2)}x + ${intercept.toFixed(1)}   ·   R² = ${rSquared.toFixed(3)}`;\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  title: {\n    text: \"Ad Spend vs Sales Revenue · scatter-regression-linear · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 20,\n    textStyle: { color: t.ink, fontSize: 17, fontWeight: 500 },\n  },\n  legend: {\n    data: [\n      \"Weekly observations\",\n      \"Linear fit\",\n      { name: \"95% confidence band\", icon: \"roundRect\" },\n    ],\n    top: 66,\n    left: \"center\",\n    textStyle: { color: t.inkSoft, fontSize: 15 },\n  },\n  grid: { left: 110, right: 80, top: 120, bottom: 90 },\n  xAxis: {\n    type: \"value\",\n    name: \"Advertising Spend ($1,000s)\",\n    nameLocation: \"middle\",\n    nameGap: 45,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.grid } },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Weekly Sales Revenue ($1,000s)\",\n    nameLocation: \"middle\",\n    nameGap: 70,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.grid } },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      name: \"ci-lower\",\n      type: \"line\",\n      data: ciLower,\n      stack: \"ci-band\",\n      symbol: \"none\",\n      lineStyle: { opacity: 0 },\n      silent: true,\n    },\n    {\n      name: \"95% confidence band\",\n      type: \"line\",\n      data: ciBand,\n      stack: \"ci-band\",\n      symbol: \"none\",\n      lineStyle: { color: t.palette[1], opacity: 0.4, width: 1, type: \"dashed\" },\n      areaStyle: { color: t.palette[1], opacity: 0.16 },\n      itemStyle: { color: t.palette[1], opacity: 0.35 },\n      silent: true,\n    },\n    {\n      name: \"Weekly observations\",\n      type: \"scatter\",\n      data: adSpend.map((x, i) => [x, salesRevenue[i]]),\n      symbolSize: 12,\n      itemStyle: { color: t.palette[0], opacity: 0.55 },\n    },\n    {\n      name: \"Linear fit\",\n      type: \"line\",\n      data: fitLine,\n      symbol: \"none\",\n      lineStyle: { color: t.palette[1], width: 3.5 },\n      z: 3,\n    },\n  ],\n  graphic: {\n    type: \"text\",\n    right: 90,\n    bottom: 130,\n    style: {\n      text: equation,\n      fill: t.ink,\n      fontSize: 15,\n      fontWeight: 600,\n      backgroundColor: t.elevatedBg,\n      padding: [8, 12],\n      borderRadius: 4,\n      borderColor: t.grid,\n      borderWidth: 1,\n    },\n  },\n});\n"}