Lag Plot for Time Series Autocorrelation Diagnosis — MUI X Charts

A lag plot is a scatter plot of a time series against a lagged version of itself, plotting y(t) on the x-axis versus y(t+k) on the y-axis for a given lag order k. If the data is purely random, points scatter uniformly with no visible structure; if autocorrelation is present, distinctive patterns emerge — linear clusters for autoregressive processes, elliptical shapes for seasonal data. This provides a quick visual diagnostic for time series dependence, complementing numerical tools like ACF/PACF.

Lag Plot for Time Series Autocorrelation Diagnosis rendered with MUI X Charts

Renders

JavaScript source (MUI X Charts)

// anyplot.ai
// scatter-lag: Lag Plot for Time Series Autocorrelation Diagnosis
// Library: muix 7.29.1 | JavaScript 22.23.0
// Quality: 90/100 | Created: 2026-06-24
//# anyplot-orientation: landscape
// anyplot.ai
// scatter-lag: Lag Plot for Time Series Autocorrelation Diagnosis
// Library: MUI X Charts | React | Node 22
// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.
// Quality: pending | Created: 2026-06-24

import { ChartContainer } from "@mui/x-charts/ChartContainer";
import { ScatterPlot } from "@mui/x-charts/ScatterChart";
import { ChartsXAxis } from "@mui/x-charts/ChartsXAxis";
import { ChartsYAxis } from "@mui/x-charts/ChartsYAxis";
import { ChartsGrid } from "@mui/x-charts/ChartsGrid";
import { ChartsLegend } from "@mui/x-charts/ChartsLegend";
import { useXScale, useYScale } from "@mui/x-charts/hooks";

const t = window.ANYPLOT_TOKENS;
const { width, height } = window.ANYPLOT_SIZE;

// Deterministic LCG (seed 42) — no Math.random() in browser harness
let _seed = 42;
function lcg() {
  _seed = (1664525 * _seed + 1013904223) >>> 0;
  return _seed / 4294967296;
}
function randn() {
  const u = lcg(), v = lcg();
  return Math.sqrt(-2 * Math.log(u + 1e-15)) * Math.cos(2 * Math.PI * v);
}

// AR(1) hourly temperature process: ρ=0.82, μ=20°C, σ≈5°C
// Strong positive autocorrelation → tight ellipse along diagonal in lag plot
const N = 200;
const RHO = 0.82;
const rawAR = [0.0];
for (let i = 1; i < N; i++) {
  rawAR.push(RHO * rawAR[i - 1] + Math.sqrt(1 - RHO * RHO) * randn());
}
const temps = rawAR.map(v => +(20 + v * 5).toFixed(2));

// Lag-1 pairs: (temp(t), temp(t+1))
const lagPairs = temps.slice(0, N - 1).map((v, i) => ({ x: v, y: temps[i + 1] }));

// Symmetric axis range with padding
const gMin = Math.min(...temps);
const gMax = Math.max(...temps);
const axPad = (gMax - gMin) * 0.07;
const axMin = gMin - axPad;
const axMax = gMax + axPad;

// Pearson r(lag=1)
const nPairs = lagPairs.length;
const xMean = lagPairs.reduce((s, p) => s + p.x, 0) / nPairs;
const yMean = lagPairs.reduce((s, p) => s + p.y, 0) / nPairs;
const cov = lagPairs.reduce((s, p) => s + (p.x - xMean) * (p.y - yMean), 0);
const sX = Math.sqrt(lagPairs.reduce((s, p) => s + (p.x - xMean) ** 2, 0));
const sY = Math.sqrt(lagPairs.reduce((s, p) => s + (p.y - yMean) ** 2, 0));
const pearsonR = (cov / (sX * sY)).toFixed(3);

// 4 time-quartile series — color encodes temporal order (earliest → latest)
const QN = Math.floor(nPairs / 4);
const QLABELS = ["t = 0–49", "t = 50–99", "t = 100–149", "t = 150–198"];
const series = [0, 1, 2, 3].map(q => ({
  type: "scatter",
  id: `q${q}`,
  label: QLABELS[q],
  color: t.palette[q],
  markerSize: 7,
  data: lagPairs
    .slice(q * QN, q < 3 ? (q + 1) * QN : nPairs)
    .map((p, i) => ({ x: p.x, y: p.y, id: `q${q}-${i}` })),
}));

// Diagonal reference line y=x — rendered via MUI X axis scale hooks
function DiagonalLine() {
  const xScale = useXScale();
  const yScale = useYScale();
  if (!xScale || !yScale) return null;
  return (
    <line
      x1={xScale(axMin)} y1={yScale(axMin)}
      x2={xScale(axMax)} y2={yScale(axMax)}
      stroke={t.inkSoft}
      strokeWidth={2.5}
      strokeDasharray="10,8"
      opacity={0.5}
    />
  );
}

const TITLE = "scatter-lag · javascript · muix · anyplot.ai";
const MARGIN = { top: 80, right: 60, bottom: 96, left: 110 };

export default function Chart() {
  return (
    <ChartContainer
      width={width}
      height={height}
      margin={MARGIN}
      series={series}
      xAxis={[{
        scaleType: "linear",
        min: axMin,
        max: axMax,
        label: "y(t)  — hourly temperature (°C)",
        tickLabelStyle: { fontSize: 14, fill: t.inkSoft },
        labelStyle: { fontSize: 16, fill: t.ink },
      }]}
      yAxis={[{
        scaleType: "linear",
        min: axMin,
        max: axMax,
        label: "y(t+1)  — next-hour temperature (°C)",
        tickLabelStyle: { fontSize: 14, fill: t.inkSoft },
        labelStyle: { fontSize: 16, fill: t.ink },
      }]}
    >
      <ChartsGrid horizontal vertical />
      <DiagonalLine />
      <ScatterPlot skipAnimation />
      <ChartsXAxis />
      <ChartsYAxis />
      <ChartsLegend
        direction="row"
        position={{ vertical: "bottom", horizontal: "middle" }}
        slotProps={{
          legend: {
            itemMarkWidth: 12,
            itemMarkHeight: 12,
            markGap: 8,
            itemGap: 28,
            labelStyle: { fontSize: 14, fill: t.ink },
          },
        }}
      />
      {/* L-frame: mask top and right spines with background color */}
      <line
        x1={MARGIN.left - 1} y1={MARGIN.top}
        x2={width - MARGIN.right + 1} y2={MARGIN.top}
        stroke={t.pageBg}
        strokeWidth={3}
      />
      <line
        x1={width - MARGIN.right} y1={MARGIN.top - 1}
        x2={width - MARGIN.right} y2={height - MARGIN.bottom + 1}
        stroke={t.pageBg}
        strokeWidth={3}
      />
      {/* Title */}
      <text
        x={width / 2}
        y={36}
        textAnchor="middle"
        fontSize={22}
        fontWeight={600}
        fill={t.ink}
      >
        {TITLE}
      </text>
      {/* Subtitle */}
      <text
        x={width / 2}
        y={58}
        textAnchor="middle"
        fontSize={14}
        fill={t.inkSoft}
      >
        Hourly temperature station readings · lag = 1 · AR(1) process (ρ = 0.82)
      </text>
      {/* Pearson r annotation — top-left corner (sparse for positive autocorrelation) */}
      <text
        x={MARGIN.left + 18}
        y={MARGIN.top + 28}
        textAnchor="start"
        fontSize={16}
        fontWeight={600}
        fill={t.ink}
      >
        {`r(lag = 1) = ${pearsonR}`}
      </text>
    </ChartContainer>
  );
}

Part of Lag Plot for Time Series Autocorrelation Diagnosis on anyplot.ai.

Other implementations