{"spec_id":"acf-pacf","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// acf-pacf: Autocorrelation and Partial Autocorrelation (ACF/PACF) Plot\n// Library: chartjs 4.4.7 | JavaScript 22.22.3\n// Quality: 84/100 | Created: 2026-06-10\n\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Deterministic LCG for reproducible pseudo-random numbers (no seeded RNG in browser)\nlet _seed = 42;\nfunction lcgRand() {\n  _seed = (Math.imul(1664525, _seed) + 1013904223) >>> 0;\n  return _seed / 4294967296;\n}\nfunction stdNormal() {\n  const u1 = lcgRand() || 1e-10;\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * lcgRand());\n}\n\n// AR(2) process: x_t = 0.7*x_{t-1} + 0.2*x_{t-2} + ε_t\n// Classic diagnostic: ACF decays exponentially; PACF cuts off exactly after lag 2\nconst N = 240;\nconst series = [stdNormal(), stdNormal()];\nfor (let i = 2; i < N; i++) {\n  series.push(0.7 * series[i - 1] + 0.2 * series[i - 2] + stdNormal());\n}\n\n// Sample ACF at lags 0..maxLag (biased estimator, consistent with standard practice)\nfunction computeACF(s, maxLag) {\n  const n = s.length;\n  const mean = s.reduce((a, b) => a + b, 0) / n;\n  const variance = s.reduce((a, b) => a + (b - mean) ** 2, 0) / n;\n  return Array.from({ length: maxLag + 1 }, (_, k) => {\n    const cov = s\n      .slice(0, n - k)\n      .reduce((sum, v, i) => sum + (v - mean) * (s[i + k] - mean), 0);\n    return cov / (n * variance);\n  });\n}\n\n// PACF at lags 1..maxLag via Levinson-Durbin recursion\nfunction computePACF(acfVals, maxLag) {\n  const result = [acfVals[1]];\n  let phi = [acfVals[1]];\n  for (let k = 2; k <= maxLag; k++) {\n    let num = acfVals[k],\n      den = 1;\n    for (let j = 0; j < k - 1; j++) {\n      num -= phi[j] * acfVals[k - j - 1];\n      den -= phi[j] * acfVals[j + 1];\n    }\n    const phikk = Math.abs(den) < 1e-12 ? 0 : num / den;\n    result.push(phikk);\n    phi = Array.from({ length: k }, (_, j) =>\n      j === k - 1 ? phikk : phi[j] - phikk * phi[k - 2 - j]\n    );\n  }\n  return result;\n}\n\nconst MAX_LAG = 30;\nconst acfData = computeACF(series, MAX_LAG); // lags 0..30 (lag 0 = 1.0)\nconst pacfData = computePACF(acfData, MAX_LAG); // lags 1..30\nconst CI = 1.96 / Math.sqrt(N); // 95% confidence bound: ±1.96/√N\n\n// Brand green for positive correlations, matte red for negative\nconst stemColor = (v) => (v >= 0 ? t.palette[0] : t.palette[4]);\n\n// DOM: flex column with overall title + two equal-height chart panes\nconst container = document.getElementById(\"container\");\ncontainer.style.cssText = `\n  display: flex; flex-direction: column; gap: 8px;\n  background: ${t.pageBg}; padding: 30px 64px 26px;\n  box-sizing: border-box;\n`;\n\nconst titleEl = document.createElement(\"div\");\ntitleEl.textContent = \"acf-pacf · javascript · chartjs · anyplot.ai\";\ntitleEl.style.cssText = `\n  color: ${t.ink}; font: 700 22px/1.3 system-ui, -apple-system, sans-serif;\n  text-align: center; flex-shrink: 0;\n`;\ncontainer.appendChild(titleEl);\n\nfunction makePane() {\n  const wrap = document.createElement(\"div\");\n  wrap.style.cssText = \"flex: 1; position: relative; min-height: 0;\";\n  const canvas = document.createElement(\"canvas\");\n  wrap.appendChild(canvas);\n  container.appendChild(wrap);\n  return canvas;\n}\nconst acfCanvas = makePane();\nconst pacfCanvas = makePane();\n\n// Horizontal dashed CI line dataset (same constant value across all lags)\nfunction ciLine(n, val, label) {\n  return {\n    type: \"line\",\n    label,\n    data: new Array(n).fill(val),\n    borderColor: t.inkSoft,\n    borderDash: [8, 5],\n    borderWidth: 1.5,\n    pointRadius: 0,\n    fill: false,\n    tension: 0,\n  };\n}\n\n// Highlight zero baseline gridline for clear significance reference\nconst zeroGridColor = (ctx) => (ctx.tick?.value === 0 ? t.inkSoft : t.grid);\n\n// Shared y-axis config for both subplots\nfunction yAxis(label) {\n  return {\n    ticks: { color: t.inkSoft, font: { size: 14 }, maxTicksLimit: 7 },\n    grid: { color: zeroGridColor },\n    border: { display: false },\n    title: {\n      display: true,\n      text: label,\n      color: t.ink,\n      font: { size: 14, weight: \"600\" },\n    },\n    suggestedMin: -1.1,\n    suggestedMax: 1.15,\n  };\n}\n\n// Legend: show only the \"95% CI\" dashed line entry\nconst legendConfig = {\n  labels: {\n    color: t.inkSoft,\n    font: { size: 13 },\n    filter: (item) => item.text === \"95% CI\",\n    boxWidth: 24,\n  },\n};\n\n// ACF chart — lags 0..30 (lag 0 = 1.0 always included per spec)\nnew Chart(acfCanvas, {\n  type: \"bar\",\n  data: {\n    labels: Array.from({ length: MAX_LAG + 1 }, (_, i) => i),\n    datasets: [\n      {\n        label: \"ACF\",\n        data: acfData,\n        backgroundColor: acfData.map(stemColor),\n        borderWidth: 0,\n        barThickness: 7,\n      },\n      ciLine(MAX_LAG + 1, CI, \"95% CI\"),\n      ciLine(MAX_LAG + 1, -CI, \"\"),\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: { display: false },\n      legend: { display: false },\n    },\n    scales: {\n      x: {\n        ticks: { color: t.inkSoft, font: { size: 14 }, maxTicksLimit: 16 },\n        grid: { display: false },\n        border: { display: false },\n        title: { display: false }, // \"Lag\" label shown on bottom chart only\n      },\n      y: yAxis(\"ACF\"),\n    },\n  },\n});\n\n// PACF chart — lags 1..30 (no lag 0 per spec)\nnew Chart(pacfCanvas, {\n  type: \"bar\",\n  data: {\n    labels: Array.from({ length: MAX_LAG }, (_, i) => i + 1),\n    datasets: [\n      {\n        label: \"PACF\",\n        data: pacfData,\n        backgroundColor: pacfData.map(stemColor),\n        borderWidth: 0,\n        barThickness: 7,\n      },\n      ciLine(MAX_LAG, CI, \"95% CI\"),\n      ciLine(MAX_LAG, -CI, \"\"),\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: { display: false },\n      legend: legendConfig,\n    },\n    scales: {\n      x: {\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { display: false },\n        border: { display: false },\n        title: {\n          display: true,\n          text: \"Lag\",\n          color: t.ink,\n          font: { size: 14, weight: \"600\" },\n        },\n      },\n      y: yAxis(\"PACF\"),\n    },\n  },\n});\n"}