{"spec_id":"scatter-lag","library":"muix","language":"javascript","code":"// anyplot.ai\n// scatter-lag: Lag Plot for Time Series Autocorrelation Diagnosis\n// Library: muix 7.29.1 | JavaScript 22.23.0\n// Quality: 90/100 | Created: 2026-06-24\n//# anyplot-orientation: landscape\n// anyplot.ai\n// scatter-lag: Lag Plot for Time Series Autocorrelation Diagnosis\n// Library: MUI X Charts | React | Node 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-06-24\n\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ScatterPlot } from \"@mui/x-charts/ScatterChart\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { ChartsGrid } from \"@mui/x-charts/ChartsGrid\";\nimport { ChartsLegend } from \"@mui/x-charts/ChartsLegend\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\n\n// Deterministic LCG (seed 42) — no Math.random() in browser harness\nlet _seed = 42;\nfunction lcg() {\n  _seed = (1664525 * _seed + 1013904223) >>> 0;\n  return _seed / 4294967296;\n}\nfunction randn() {\n  const u = lcg(), v = lcg();\n  return Math.sqrt(-2 * Math.log(u + 1e-15)) * Math.cos(2 * Math.PI * v);\n}\n\n// AR(1) hourly temperature process: ρ=0.82, μ=20°C, σ≈5°C\n// Strong positive autocorrelation → tight ellipse along diagonal in lag plot\nconst N = 200;\nconst RHO = 0.82;\nconst rawAR = [0.0];\nfor (let i = 1; i < N; i++) {\n  rawAR.push(RHO * rawAR[i - 1] + Math.sqrt(1 - RHO * RHO) * randn());\n}\nconst temps = rawAR.map(v => +(20 + v * 5).toFixed(2));\n\n// Lag-1 pairs: (temp(t), temp(t+1))\nconst lagPairs = temps.slice(0, N - 1).map((v, i) => ({ x: v, y: temps[i + 1] }));\n\n// Symmetric axis range with padding\nconst gMin = Math.min(...temps);\nconst gMax = Math.max(...temps);\nconst axPad = (gMax - gMin) * 0.07;\nconst axMin = gMin - axPad;\nconst axMax = gMax + axPad;\n\n// Pearson r(lag=1)\nconst nPairs = lagPairs.length;\nconst xMean = lagPairs.reduce((s, p) => s + p.x, 0) / nPairs;\nconst yMean = lagPairs.reduce((s, p) => s + p.y, 0) / nPairs;\nconst cov = lagPairs.reduce((s, p) => s + (p.x - xMean) * (p.y - yMean), 0);\nconst sX = Math.sqrt(lagPairs.reduce((s, p) => s + (p.x - xMean) ** 2, 0));\nconst sY = Math.sqrt(lagPairs.reduce((s, p) => s + (p.y - yMean) ** 2, 0));\nconst pearsonR = (cov / (sX * sY)).toFixed(3);\n\n// 4 time-quartile series — color encodes temporal order (earliest → latest)\nconst QN = Math.floor(nPairs / 4);\nconst QLABELS = [\"t = 0–49\", \"t = 50–99\", \"t = 100–149\", \"t = 150–198\"];\nconst series = [0, 1, 2, 3].map(q => ({\n  type: \"scatter\",\n  id: `q${q}`,\n  label: QLABELS[q],\n  color: t.palette[q],\n  markerSize: 7,\n  data: lagPairs\n    .slice(q * QN, q < 3 ? (q + 1) * QN : nPairs)\n    .map((p, i) => ({ x: p.x, y: p.y, id: `q${q}-${i}` })),\n}));\n\n// Diagonal reference line y=x — rendered via MUI X axis scale hooks\nfunction DiagonalLine() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  if (!xScale || !yScale) return null;\n  return (\n    <line\n      x1={xScale(axMin)} y1={yScale(axMin)}\n      x2={xScale(axMax)} y2={yScale(axMax)}\n      stroke={t.inkSoft}\n      strokeWidth={2.5}\n      strokeDasharray=\"10,8\"\n      opacity={0.5}\n    />\n  );\n}\n\nconst TITLE = \"scatter-lag · javascript · muix · anyplot.ai\";\nconst MARGIN = { top: 80, right: 60, bottom: 96, left: 110 };\n\nexport default function Chart() {\n  return (\n    <ChartContainer\n      width={width}\n      height={height}\n      margin={MARGIN}\n      series={series}\n      xAxis={[{\n        scaleType: \"linear\",\n        min: axMin,\n        max: axMax,\n        label: \"y(t)  — hourly temperature (°C)\",\n        tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n        labelStyle: { fontSize: 16, fill: t.ink },\n      }]}\n      yAxis={[{\n        scaleType: \"linear\",\n        min: axMin,\n        max: axMax,\n        label: \"y(t+1)  — next-hour temperature (°C)\",\n        tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n        labelStyle: { fontSize: 16, fill: t.ink },\n      }]}\n    >\n      <ChartsGrid horizontal vertical />\n      <DiagonalLine />\n      <ScatterPlot skipAnimation />\n      <ChartsXAxis />\n      <ChartsYAxis />\n      <ChartsLegend\n        direction=\"row\"\n        position={{ vertical: \"bottom\", horizontal: \"middle\" }}\n        slotProps={{\n          legend: {\n            itemMarkWidth: 12,\n            itemMarkHeight: 12,\n            markGap: 8,\n            itemGap: 28,\n            labelStyle: { fontSize: 14, fill: t.ink },\n          },\n        }}\n      />\n      {/* L-frame: mask top and right spines with background color */}\n      <line\n        x1={MARGIN.left - 1} y1={MARGIN.top}\n        x2={width - MARGIN.right + 1} y2={MARGIN.top}\n        stroke={t.pageBg}\n        strokeWidth={3}\n      />\n      <line\n        x1={width - MARGIN.right} y1={MARGIN.top - 1}\n        x2={width - MARGIN.right} y2={height - MARGIN.bottom + 1}\n        stroke={t.pageBg}\n        strokeWidth={3}\n      />\n      {/* Title */}\n      <text\n        x={width / 2}\n        y={36}\n        textAnchor=\"middle\"\n        fontSize={22}\n        fontWeight={600}\n        fill={t.ink}\n      >\n        {TITLE}\n      </text>\n      {/* Subtitle */}\n      <text\n        x={width / 2}\n        y={58}\n        textAnchor=\"middle\"\n        fontSize={14}\n        fill={t.inkSoft}\n      >\n        Hourly temperature station readings · lag = 1 · AR(1) process (ρ = 0.82)\n      </text>\n      {/* Pearson r annotation — top-left corner (sparse for positive autocorrelation) */}\n      <text\n        x={MARGIN.left + 18}\n        y={MARGIN.top + 28}\n        textAnchor=\"start\"\n        fontSize={16}\n        fontWeight={600}\n        fill={t.ink}\n      >\n        {`r(lag = 1) = ${pearsonR}`}\n      </text>\n    </ChartContainer>\n  );\n}\n"}