Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
634e47d
[Autoloop: build-tsb-pandas-typescript-migration] Iteration 387: Add …
github-actions[bot] Jun 29, 2026
4bc79cc
[Autoloop: build-tsb-pandas-typescript-migration] Iteration 388: Add …
github-actions[bot] Jun 30, 2026
ae5012c
Fix TypeScript errors and convert signal/filters playgrounds to runtime
github-actions[bot] Jul 2, 2026
7506dd9
ci: trigger checks
github-actions[bot] Jul 2, 2026
d1cd215
[Autoloop: build-tsb-pandas-typescript-migration] Iteration 393: Add …
github-actions[bot] Jul 2, 2026
1507434
ci: trigger checks
github-actions[bot] Jul 2, 2026
b56f92f
[Autoloop: build-tsb-pandas-typescript-migration] Iteration 395: Add …
github-actions[bot] Jul 4, 2026
5a11c61
ci: trigger checks
github-actions[bot] Jul 4, 2026
758d4d5
[Autoloop: build-tsb-pandas-typescript-migration] Iteration 396: Add …
github-actions[bot] Jul 5, 2026
b9aa964
ci: trigger checks
github-actions[bot] Jul 5, 2026
cc88491
[Autoloop: build-tsb-pandas-typescript-migration] Iteration 397: Add …
github-actions[bot] Jul 8, 2026
2f28193
ci: trigger checks
github-actions[bot] Jul 8, 2026
476eeaf
fix: correct MutMat type annotations and scipy guards in playground
github-actions[bot] Jul 10, 2026
1b59a11
ci: trigger checks
github-actions[bot] Jul 10, 2026
e6c6e74
[Autoloop: build-tsb-pandas-typescript-migration] Iteration 407: Add …
github-actions[bot] Jul 12, 2026
61987a9
ci: trigger checks
github-actions[bot] Jul 12, 2026
42f98c9
[Autoloop: build-tsb-pandas-typescript-migration] Iteration 410: Add …
github-actions[bot] Jul 14, 2026
085a98a
ci: trigger checks
github-actions[bot] Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
393 changes: 393 additions & 0 deletions playground/acf_pacf.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,393 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>tsb — ACF / PACF / Portmanteau Tests</title>
<style>
:root {
--bg: #0d1117;
--surface: #161b22;
--border: #30363d;
--text: #e6edf3;
--accent: #58a6ff;
--green: #3fb950;
--orange: #d29922;
--red: #f85149;
--font-mono: "Cascadia Code", "Fira Code", "JetBrains Mono", monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
padding: 2rem;
max-width: 900px;
margin: 0 auto;
}
a { color: var(--accent); }
h1 { color: var(--accent); margin-bottom: 0.5rem; }
h2 { margin-top: 0; margin-bottom: 0.5rem; font-size: 1.25rem; }
p { color: #8b949e; margin-bottom: 1rem; }
code {
font-family: var(--font-mono);
font-size: 0.875em;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 0.3rem;
padding: 0.1rem 0.4rem;
}
.back { margin-bottom: 2rem; display: inline-block; }
.subtitle { margin-bottom: 1.5rem; }

#playground-loading {
position: fixed; inset: 0;
background: rgba(13, 17, 23, 0.92);
display: flex; flex-direction: column;
align-items: center; justify-content: center;
z-index: 1000; gap: 1rem;
}
.spinner {
width: 40px; height: 40px;
border: 3px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
#playground-status { color: #8b949e; font-size: 0.95rem; }

.section {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.section p { margin-bottom: 0.75rem; }

.playground-block { margin-top: 0.75rem; }
.playground-header {
display: flex; align-items: center; justify-content: space-between;
background: #1c2128;
border: 1px solid var(--border);
border-bottom: none;
border-radius: 0.5rem 0.5rem 0 0;
padding: 0.4rem 0.75rem;
}
.playground-label {
font-size: 0.75rem; color: #8b949e;
text-transform: uppercase; letter-spacing: 0.05em;
}
.playground-actions { display: flex; gap: 0.5rem; }
.playground-actions button {
background: transparent; color: var(--accent);
border: 1px solid var(--border);
border-radius: 0.35rem;
padding: 0.25rem 0.7rem;
font-size: 0.8rem; cursor: pointer;
font-family: system-ui, sans-serif;
transition: background 0.15s, border-color 0.15s;
}
.playground-actions button:hover:not(:disabled) {
background: rgba(88, 166, 255, 0.1);
border-color: var(--accent);
}
.playground-actions button:disabled { opacity: 0.4; cursor: not-allowed; }
.playground-run { font-weight: 600; }

.playground-editor {
display: block; width: 100%; min-height: 80px;
background: #0d1117; color: var(--text);
border: 1px solid var(--border);
border-top: none; border-bottom: none;
padding: 1rem;
font-family: var(--font-mono);
font-size: 0.875rem; line-height: 1.55;
resize: vertical; outline: none;
tab-size: 2; white-space: pre; overflow-x: auto;
}
.playground-editor:focus {
border-color: var(--accent);
box-shadow: inset 0 0 0 1px var(--accent);
}

.playground-output {
background: #1c2333;
border: 1px solid var(--border);
border-radius: 0 0 0.5rem 0.5rem;
padding: 0.75rem 1rem;
font-family: var(--font-mono);
font-size: 0.85rem; color: #8b949e;
white-space: pre-wrap; min-height: 2rem;
word-break: break-word;
}
.playground-output.active { color: var(--green); border-color: var(--green); }
.playground-output.error { color: var(--red); border-color: var(--red); }
.playground-hint {
font-size: 0.75rem; color: #484f58;
margin-top: 0.35rem; text-align: right;
}

footer {
text-align: center;
padding: 2rem 0;
color: #8b949e;
font-size: 0.85rem;
border-top: 1px solid var(--border);
margin-top: 2rem;
}
</style>
</head>
<body>

<div id="playground-loading">
<div class="spinner"></div>
<div id="playground-status">Initializing playground…</div>
</div>

<a class="back" href="index.html">← Back to roadmap</a>
<h1>ACF / PACF &amp; Portmanteau Tests</h1>
<p class="subtitle">
Autocorrelation (<code>acf</code>), partial autocorrelation (<code>pacf</code>),
cross-correlation (<code>ccf</code>), Durbin-Watson, Ljung-Box, and Box-Pierce tests —
mirrors <code>statsmodels.tsa.stattools</code> and <code>pd.Series.autocorr</code>.
</p>

<div class="section">
<h2>1 — Single-lag autocorrelation (pandas-style)</h2>
<p>
<code>autocorr(x, lag)</code> computes the Pearson correlation between
<code>x[0..n−lag−1]</code> and <code>x[lag..n−1]</code>, exactly like
<code>pd.Series.autocorr(lag)</code>.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">JavaScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { autocorr } from "tsb";

// Linear trend — strong positive autocorrelation at all lags
const trend = Array.from({ length: 20 }, (_, i) => i + 1);
console.log("Trend autocorr lag=1:", autocorr(trend, 1).toFixed(4));
console.log("Trend autocorr lag=5:", autocorr(trend, 5).toFixed(4));

// Alternating ±1 — perfect negative autocorrelation at lag 1
const alt = Array.from({ length: 10 }, (_, i) => i % 2 === 0 ? 1 : -1);
console.log("Alternating autocorr lag=1:", autocorr(alt, 1).toFixed(4));

// Seasonal: period 4
const seasonal = [1, 2, 3, 2, 1, 2, 3, 2, 1, 2, 3, 2];
for (let k = 0; k <= 4; k++) {
console.log(`Seasonal autocorr lag=${k}:`, autocorr(seasonal, k).toFixed(4));
}</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<div class="section">
<h2>2 — Full ACF with Bartlett confidence intervals</h2>
<p>
<code>acf(x, { nlags, alpha })</code> returns all autocorrelations at lags 0…nlags.
With <code>alpha=0.05</code>, Bartlett confidence intervals are returned: lags whose
CI excludes zero are statistically significant.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">JavaScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { acf } from "tsb";

// AR(1)-like series with φ ≈ 0.8
const x = [1];
for (let i = 1; i < 60; i++) x.push(0.8 * x[i-1] + (Math.random() - 0.5) * 0.3);

const result = acf(x, { nlags: 8, alpha: 0.05 });
console.log("Lag | ACF | 95% CI");
console.log("----+---------+------------------");
for (let k = 0; k <= 8; k++) {
const r = result.acf[k].toFixed(4);
const ci = result.confint[k];
const lo = ci[0].toFixed(4);
const hi = ci[1].toFixed(4);
const sig = (result.acf[k] < ci[0] || result.acf[k] > ci[1]) ? " *" : "";
console.log(` ${k} | ${r} | [${lo}, ${hi}]${sig}`);
}</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<div class="section">
<h2>3 — Partial ACF (Levinson-Durbin)</h2>
<p>
<code>pacf(x, { nlags, alpha })</code> uses the Levinson-Durbin recursion to compute
partial autocorrelations. For a true AR(p) process, only the first p PACF values are
significantly non-zero — this is how you identify the AR order.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">JavaScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { pacf } from "tsb";

// AR(2): x[t] = 0.6*x[t-1] - 0.3*x[t-2] + ε
// → PACF should show non-zero values at lags 1 and 2, near-zero beyond
const x = [0, 0];
for (let i = 2; i < 100; i++) {
x.push(0.6 * x[i-1] - 0.3 * x[i-2] + (Math.random() - 0.5) * 0.2);
}

const result = pacf(x, { nlags: 6, alpha: 0.05 });
console.log("PACF for AR(2) process");
console.log("Lag | PACF | 95% CI");
console.log("----+---------+--------------------------");
for (let k = 0; k <= 6; k++) {
const r = result.pacf[k].toFixed(4);
const ci = result.confint[k];
const lo = ci[0].toFixed(4);
const hi = ci[1].toFixed(4);
console.log(` ${k} | ${r} | [${lo}, ${hi}]`);
}
console.log("\nLags 1-2 should be significant; higher lags should be near zero.");</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<div class="section">
<h2>4 — Cross-Correlation Function (CCF)</h2>
<p>
<code>ccf(x, y, { nlags, alpha })</code> measures the linear relationship between
<code>x[t]</code> and <code>y[t+k]</code> at each lag k. Peaks in the CCF reveal
lead/lag relationships between two series.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">JavaScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { ccf } from "tsb";

// Create x = sine wave; y = x shifted by 3 steps
const n = 40;
const x = Array.from({ length: n }, (_, i) => Math.sin(2 * Math.PI * i / 8));
const y = Array.from({ length: n }, (_, i) => Math.sin(2 * Math.PI * (i - 3) / 8));

const result = ccf(x, y, { nlags: 6, positiveOnly: false });
console.log("Lag | CCF");
console.log("-----+--------");
for (let i = 0; i < result.lags.length; i++) {
const bar = "#".repeat(Math.round(Math.abs(result.acf[i]) * 20));
console.log(`${String(result.lags[i]).padStart(4)} | ${result.acf[i].toFixed(4)} ${bar}`);
}
console.log("\nPeak at lag 3 indicates y leads x by 3 steps.");</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<div class="section">
<h2>5 — Durbin-Watson statistic</h2>
<p>
<code>durbinWatson(residuals)</code> tests for first-order autocorrelation in OLS residuals.
Values near 2 indicate no autocorrelation; values near 0 indicate positive autocorrelation;
values near 4 indicate negative autocorrelation.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">JavaScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { durbinWatson } from "tsb";

// White noise residuals — DW ≈ 2
const noise = Array.from({ length: 50 }, () => Math.random() - 0.5);
console.log("White noise DW:", durbinWatson(noise).toFixed(4), "(should be ≈ 2)");

// Positively autocorrelated — DW < 2 (near 0)
const posAC = noise.map((_, i) => 1 + i * 0.01); // slow upward drift
console.log("Positive AC DW:", durbinWatson(posAC).toFixed(4), "(should be < 1)");

// Alternating residuals — DW ≈ 4 (negative autocorrelation)
const altResid = Array.from({ length: 20 }, (_, i) => i % 2 === 0 ? 1 : -1);
console.log("Alternating DW:", durbinWatson(altResid).toFixed(4), "(should be ≈ 4)");

// Rule of thumb: 1.5 < DW < 2.5 → no autocorrelation concern
const isOk = (dw) => dw > 1.5 && dw < 2.5;
console.log("\nWhite noise is OK:", isOk(durbinWatson(noise)));
console.log("Positive AC is OK:", isOk(durbinWatson(posAC)));</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<div class="section">
<h2>6 — Ljung-Box &amp; Box-Pierce portmanteau tests</h2>
<p>
<code>ljungBox(x, { lags })</code> and <code>boxPierce(x, { lags })</code> test the null
hypothesis that no autocorrelation exists up to a given lag. Small p-values reject the
white-noise hypothesis. Ljung-Box has better finite-sample properties.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">JavaScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { ljungBox, boxPierce } from "tsb";

// White noise: p-values should be large (fail to reject H0)
const wn = Array.from({ length: 100 }, () => Math.random() - 0.5);
const lb_wn = ljungBox(wn, { lags: [5, 10, 15] });
console.log("=== White Noise ===");
lb_wn.lags.forEach((h, i) => {
console.log(`Lag ${h}: Q=${lb_wn.statistic[i].toFixed(3)}, p=${lb_wn.pvalue[i].toFixed(3)}`);
});

// AR(1) process: p-values should be very small (reject H0)
const ar1 = [1.0];
for (let i = 1; i < 100; i++) ar1.push(0.85 * ar1[i-1] + (Math.random() - 0.5) * 0.2);
const lb_ar = ljungBox(ar1, { lags: [5, 10] });
const bp_ar = boxPierce(ar1, { lags: [5, 10] });
console.log("\n=== AR(1) φ=0.85 ===");
lb_ar.lags.forEach((h, i) => {
console.log(`Lag ${h}: LB Q=${lb_ar.statistic[i].toFixed(2)}, p=${lb_ar.pvalue[i].toExponential(2)}; BP Q=${bp_ar.statistic[i].toFixed(2)}, p=${bp_ar.pvalue[i].toExponential(2)}`);
});</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<footer>
<p>
<a href="index.html">tsb playground</a> ·
Built by <a href="https://github.com/githubnext/autoloop">Autoloop</a>
</p>
</footer>

<script type="module" src="playground-runtime.js"></script>
</body>
</html>
Loading
Loading