{"spec_id":"scatter-lag","library":"d3","language":"javascript","code":"// anyplot.ai\n// scatter-lag: Lag Plot for Time Series Autocorrelation Diagnosis\n// Library: d3 7.9.0 | JavaScript 22.22.3\n// Quality: 91/100 | Created: 2026-06-24\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\nconst margin = { top: 90, right: 80, bottom: 100, left: 110 };\nconst iw = width - margin.left - margin.right;\nconst ih = height - margin.top - margin.bottom;\n\n// Park-Miller LCG — deterministic, no Date/Math.random\nlet _seed = 42;\nfunction rand() {\n  _seed = (_seed * 16807) % 2147483647;\n  return (_seed - 1) / 2147483646;\n}\nfunction randn() {\n  const u1 = rand();\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1 + 1e-12)) * Math.cos(2 * Math.PI * u2);\n}\n\n// AR(1) hourly temperature series — phi = 0.88 gives strong lag-1 autocorrelation\nconst n = 280;\nconst phi = 0.88;\nconst mu = 14.5;\nconst sigma = 1.8;\nconst series = [mu];\nfor (let i = 1; i < n; i++) {\n  series.push(mu + phi * (series[i - 1] - mu) + randn() * sigma);\n}\n\n// Lag-1 pairs: x = y(t), y = y(t+1)\nconst lag = 1;\nconst data = [];\nfor (let i = 0; i < n - lag; i++) {\n  data.push({ xt: series[i], xt1: series[i + lag], idx: i });\n}\n\n// SVG mount\nconst svg = d3.select(\"#container\").append(\"svg\")\n  .attr(\"width\", width)\n  .attr(\"height\", height);\nconst g = svg.append(\"g\").attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n// Symmetric axis domain so diagonal y=x is always at 45°\nconst allVals = data.flatMap((d) => [d.xt, d.xt1]);\nconst lo = d3.min(allVals);\nconst hi = d3.max(allVals);\nconst pad = (hi - lo) * 0.06;\nconst domMin = lo - pad;\nconst domMax = hi + pad;\n\nconst xScale = d3.scaleLinear().domain([domMin, domMax]).range([0, iw]);\nconst yScale = d3.scaleLinear().domain([domMin, domMax]).range([ih, 0]);\n\n// Sequential colormap for time index (Imprint seq: green → blue)\nconst colorSeq = d3.scaleSequential()\n  .domain([0, data.length - 1])\n  .interpolator(d3.interpolateRgbBasis(t.seq));\n\n// Gridlines\nconst xTicks = xScale.ticks(6);\nconst yTicks = yScale.ticks(6);\n\ng.append(\"g\").selectAll(\"line\").data(yTicks).join(\"line\")\n  .attr(\"x1\", 0).attr(\"x2\", iw)\n  .attr(\"y1\", (d) => yScale(d)).attr(\"y2\", (d) => yScale(d))\n  .attr(\"stroke\", t.grid).attr(\"stroke-width\", 1);\n\ng.append(\"g\").selectAll(\"line\").data(xTicks).join(\"line\")\n  .attr(\"x1\", (d) => xScale(d)).attr(\"x2\", (d) => xScale(d))\n  .attr(\"y1\", 0).attr(\"y2\", ih)\n  .attr(\"stroke\", t.grid).attr(\"stroke-width\", 1);\n\n// Diagonal reference line y = x\ng.append(\"line\")\n  .attr(\"x1\", xScale(domMin)).attr(\"y1\", yScale(domMin))\n  .attr(\"x2\", xScale(domMax)).attr(\"y2\", yScale(domMax))\n  .attr(\"stroke\", t.inkSoft)\n  .attr(\"stroke-width\", 1.8)\n  .attr(\"stroke-dasharray\", \"7,5\")\n  .attr(\"opacity\", 0.55);\n\n// Scatter points colored by time index\ng.selectAll(\"circle\").data(data).join(\"circle\")\n  .attr(\"cx\", (d) => xScale(d.xt))\n  .attr(\"cy\", (d) => yScale(d.xt1))\n  .attr(\"r\", 5.5)\n  .attr(\"fill\", (d) => colorSeq(d.idx))\n  .attr(\"opacity\", 0.78)\n  .attr(\"stroke\", t.pageBg)\n  .attr(\"stroke-width\", 0.9);\n\n// Axes\nconst xAxis = g.append(\"g\").attr(\"transform\", `translate(0,${ih})`).call(d3.axisBottom(xScale).ticks(6));\nconst yAxis = g.append(\"g\").call(d3.axisLeft(yScale).ticks(6));\nfor (const ax of [xAxis, yAxis]) {\n  ax.selectAll(\"text\").attr(\"fill\", t.inkSoft).style(\"font-size\", \"14px\");\n  ax.selectAll(\".tick line\").attr(\"stroke\", t.inkSoft);\n  ax.select(\".domain\").attr(\"stroke\", t.inkSoft);\n}\n\n// Axis labels\ng.append(\"text\")\n  .attr(\"x\", iw / 2).attr(\"y\", ih + 68)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"16px\")\n  .text(\"Temperature at time t  (°C)\");\n\ng.append(\"text\")\n  .attr(\"transform\", \"rotate(-90)\")\n  .attr(\"x\", -ih / 2).attr(\"y\", -85)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"16px\")\n  .text(\"Temperature at t + 1  (°C)\");\n\n// Pearson r computed in-snippet\nconst xMean = d3.mean(data, (d) => d.xt);\nconst yMean = d3.mean(data, (d) => d.xt1);\nconst num = d3.sum(data, (d) => (d.xt - xMean) * (d.xt1 - yMean));\nconst denX = Math.sqrt(d3.sum(data, (d) => (d.xt - xMean) ** 2));\nconst denY = Math.sqrt(d3.sum(data, (d) => (d.xt1 - yMean) ** 2));\nconst r = num / (denX * denY);\n\n// r annotation — top-left of plot area\ng.append(\"text\")\n  .attr(\"x\", 16).attr(\"y\", 28)\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"15px\")\n  .style(\"font-weight\", \"500\")\n  .text(`r = ${r.toFixed(3)}  (lag 1)`);\n\n// Colorbar legend — top-right of plot area\nconst lgW = 180;\nconst lgH = 12;\nconst lgX = iw - lgW - 10;\nconst lgY = 16;\n\nconst defs = svg.append(\"defs\");\nconst grad = defs.append(\"linearGradient\").attr(\"id\", \"seq-grad\")\n  .attr(\"x1\", \"0%\").attr(\"x2\", \"100%\");\ngrad.append(\"stop\").attr(\"offset\", \"0%\").attr(\"stop-color\", t.seq[0]);\ngrad.append(\"stop\").attr(\"offset\", \"100%\").attr(\"stop-color\", t.seq[1]);\n\ng.append(\"text\")\n  .attr(\"x\", lgX + lgW / 2).attr(\"y\", lgY - 4)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"14px\")\n  .text(\"time index\");\n\ng.append(\"rect\")\n  .attr(\"x\", lgX).attr(\"y\", lgY)\n  .attr(\"width\", lgW).attr(\"height\", lgH)\n  .attr(\"fill\", \"url(#seq-grad)\")\n  .attr(\"rx\", 3);\n\ng.append(\"text\")\n  .attr(\"x\", lgX).attr(\"y\", lgY + lgH + 16)\n  .attr(\"fill\", t.inkSoft).style(\"font-size\", \"14px\")\n  .text(\"t = 1\");\n\ng.append(\"text\")\n  .attr(\"x\", lgX + lgW).attr(\"y\", lgY + lgH + 16)\n  .attr(\"text-anchor\", \"end\")\n  .attr(\"fill\", t.inkSoft).style(\"font-size\", \"14px\")\n  .text(`t = ${n}`);\n\n// Title\nsvg.append(\"text\")\n  .attr(\"x\", width / 2).attr(\"y\", 52)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.ink)\n  .style(\"font-size\", \"22px\")\n  .style(\"font-weight\", \"600\")\n  .text(\"scatter-lag · javascript · d3 · anyplot.ai\");\n\n// Subtitle — surfaces configurable lag and interpretation anchor\nsvg.append(\"text\")\n  .attr(\"x\", width / 2).attr(\"y\", 76)\n  .attr(\"text-anchor\", \"middle\")\n  .attr(\"fill\", t.inkSoft)\n  .style(\"font-size\", \"14px\")\n  .text(`AR(1) hourly temperature  ·  lag k = ${lag}  ·  tight cluster = strong autocorrelation`);\n"}