{"spec_id":"scatter-lag","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// scatter-lag: Lag Plot for Time Series Autocorrelation Diagnosis\n// Library: chartjs 4.4.7 | JavaScript 22.23.0\n// Quality: 88/100 | Created: 2026-06-24\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Deterministic LCG pseudo-random (seed 42)\nlet _s = 42;\nfunction rand() {\n  _s = (Math.imul(1664525, _s) + 1013904223) >>> 0;\n  return _s / 0x100000000;\n}\n\n// AR(1) time series: y(t) = 0.85 * y(t-1) + noise\nconst N = 300;\nconst PHI = 0.85;\nconst LAG = 1; // configurable: try 7 or 12 for seasonal lag\nconst series = new Array(N);\nseries[0] = 0.0;\nfor (let i = 1; i < N; i++) {\n  series[i] = PHI * series[i - 1] + (rand() - 0.5) * 2.0;\n}\n\n// Lag scatter pairs: x = y(t), y = y(t + LAG)\nconst lagData = [];\nfor (let i = 0; i < N - LAG; i++) {\n  lagData.push({ x: series[i], y: series[i + LAG] });\n}\n\n// Interpolate color along imprint_seq by time index (early: green → late: blue)\nfunction hexRgb(h) {\n  return [parseInt(h.slice(1, 3), 16), parseInt(h.slice(3, 5), 16), parseInt(h.slice(5, 7), 16)];\n}\nfunction lerpColor(c1, c2, frac) {\n  const [r1, g1, b1] = hexRgb(c1);\n  const [r2, g2, b2] = hexRgb(c2);\n  return `rgba(${Math.round(r1 + (r2 - r1) * frac)},${Math.round(g1 + (g2 - g1) * frac)},${Math.round(b1 + (b2 - b1) * frac)},0.65)`;\n}\nconst pointColors = lagData.map((_, i) => lerpColor(t.seq[0], t.seq[1], i / (lagData.length - 1)));\n\n// Axis range: equal scale on both axes so y=x is truly at 45°\nconst allVals = series;\nconst vMin = Math.min(...allVals);\nconst vMax = Math.max(...allVals);\nconst pad = (vMax - vMin) * 0.06;\nconst axisMin = Math.floor((vMin - pad) * 10) / 10;\nconst axisMax = Math.ceil((vMax + pad) * 10) / 10;\n\n// Diagonal reference line (y = x)\nconst diagData = [\n  { x: axisMin, y: axisMin },\n  { x: axisMax, y: axisMax },\n];\n\n// Pearson r at lag-1\nconst xs = lagData.map(d => d.x);\nconst ys = lagData.map(d => d.y);\nconst n = lagData.length;\nconst mX = xs.reduce((a, b) => a + b, 0) / n;\nconst mY = ys.reduce((a, b) => a + b, 0) / n;\nlet num = 0, ssX = 0, ssY = 0;\nfor (let i = 0; i < n; i++) {\n  const dx = xs[i] - mX;\n  const dy = ys[i] - mY;\n  num += dx * dy;\n  ssX += dx * dx;\n  ssY += dy * dy;\n}\nconst rVal = (num / Math.sqrt(ssX * ssY)).toFixed(3);\n\n// Background fill plugin\nconst bgPlugin = {\n  id: \"bg\",\n  beforeDraw(chart) {\n    chart.ctx.fillStyle = t.pageBg;\n    chart.ctx.fillRect(0, 0, chart.width, chart.height);\n  },\n};\n\n// Correlation coefficient annotation drawn in the upper-left of the plot area\nconst rAnnotation = {\n  id: \"rAnnotation\",\n  afterDatasetsDraw(chart) {\n    const { ctx, chartArea: { left, top } } = chart;\n    ctx.save();\n    ctx.font = \"600 15px sans-serif\";\n    ctx.fillStyle = t.ink;\n    ctx.fillText(`r = ${rVal}`, left + 14, top + 22);\n    ctx.restore();\n  },\n};\n\n// Compact horizontal gradient colorbar drawn above the bottom legend\nconst colorbarPlugin = {\n  id: \"colorbar\",\n  afterDraw(chart) {\n    const { ctx, chartArea, width } = chart;\n    const barW = 120;\n    const barH = 10;\n    const barX = (width - barW) / 2;\n    const barY = chartArea.bottom + 28;\n    ctx.save();\n    const grad = ctx.createLinearGradient(barX, 0, barX + barW, 0);\n    grad.addColorStop(0, t.seq[0]);\n    grad.addColorStop(1, t.seq[1]);\n    ctx.fillStyle = grad;\n    ctx.beginPath();\n    ctx.roundRect(barX, barY, barW, barH, 3);\n    ctx.fill();\n    ctx.font = \"12px sans-serif\";\n    ctx.fillStyle = t.inkSoft;\n    ctx.textAlign = \"right\";\n    ctx.fillText(\"early\", barX - 5, barY + barH - 1);\n    ctx.textAlign = \"left\";\n    ctx.fillText(\"late\", barX + barW + 5, barY + barH - 1);\n    ctx.restore();\n  },\n};\n\n// Mount canvas\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// Chart\nnew Chart(canvas, {\n  type: \"scatter\",\n  plugins: [bgPlugin, rAnnotation, colorbarPlugin],\n  data: {\n    datasets: [\n      {\n        label: `y(t) vs y(t+${LAG})`,\n        data: lagData,\n        backgroundColor: pointColors,\n        borderColor: \"transparent\",\n        pointRadius: 4,\n        pointHoverRadius: 6,\n      },\n      {\n        label: \"y = x  (no autocorrelation)\",\n        type: \"line\",\n        data: diagData,\n        borderColor: t.inkSoft,\n        borderWidth: 2,\n        borderDash: [8, 5],\n        pointRadius: 0,\n        fill: false,\n        tension: 0,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: \"AR(1) Process · scatter-lag · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 20, weight: \"600\" },\n        padding: { top: 8, bottom: 4 },\n      },\n      subtitle: {\n        display: true,\n        text: \"Points colored by time index (early: green → late: blue)\",\n        color: t.inkSoft,\n        font: { size: 13 },\n        padding: { bottom: 12 },\n      },\n      legend: {\n        position: \"bottom\",\n        labels: {\n          color: t.ink,\n          font: { size: 14 },\n          padding: 20,\n          filter(item) {\n            return item.datasetIndex === 1;\n          },\n        },\n      },\n      tooltip: {\n        callbacks: {\n          label(ctx) {\n            return `y(t) = ${ctx.parsed.x.toFixed(3)},  y(t+${LAG}) = ${ctx.parsed.y.toFixed(3)}`;\n          },\n        },\n      },\n    },\n    scales: {\n      x: {\n        min: axisMin,\n        max: axisMax,\n        title: {\n          display: true,\n          text: \"y(t)\",\n          color: t.ink,\n          font: { size: 16 },\n          padding: { top: 6 },\n        },\n        border: { display: false },\n        ticks: { color: t.inkSoft, font: { size: 13 } },\n        grid: { color: t.grid },\n      },\n      y: {\n        min: axisMin,\n        max: axisMax,\n        title: {\n          display: true,\n          text: `y(t + ${LAG})`,\n          color: t.ink,\n          font: { size: 16 },\n          padding: { bottom: 6 },\n        },\n        border: { display: false },\n        ticks: { color: t.inkSoft, font: { size: 13 } },\n        grid: { color: t.grid },\n      },\n    },\n  },\n});\n"}