diff --git a/plots/acf-pacf/implementations/javascript/echarts.js b/plots/acf-pacf/implementations/javascript/echarts.js new file mode 100644 index 0000000000..bb57ad59f9 --- /dev/null +++ b/plots/acf-pacf/implementations/javascript/echarts.js @@ -0,0 +1,194 @@ +// anyplot.ai +// acf-pacf: Autocorrelation and Partial Autocorrelation (ACF/PACF) Plot +// Library: echarts 5.5.1 | JavaScript 22.22.3 +// Quality: 88/100 | Created: 2026-06-10 + +//# anyplot-orientation: landscape +const t = window.ANYPLOT_TOKENS; + +// Seeded LCG for deterministic data generation +let _seed = 42; +function _lcg() { + _seed = (Math.imul(_seed, 1664525) + 1013904223) >>> 0; + return _seed / 0x100000000; +} +function _randn() { + const u1 = _lcg() + 1e-10, u2 = _lcg(); + return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2); +} + +// Generate AR(2) time series: x_t = 0.6*x_{t-1} + 0.25*x_{t-2} + ε_t +// Represents daily temperature anomaly residuals (°C) +const N = 300; +const series = new Array(N).fill(0); +for (let i = 2; i < N; i++) { + series[i] = 0.6 * series[i - 1] + 0.25 * series[i - 2] + _randn(); +} + +const MAX_LAG = 35; +const ci = 1.96 / Math.sqrt(N); + +// Sample ACF at lags 0..MAX_LAG +const acfMean = series.reduce((s, v) => s + v, 0) / N; +const acfDenom = series.reduce((s, v) => s + (v - acfMean) ** 2, 0); +const acf = Array.from({ length: MAX_LAG + 1 }, (_, k) => { + let num = 0; + for (let i = k; i < N; i++) num += (series[i] - acfMean) * (series[i - k] - acfMean); + return num / acfDenom; +}); + +// Sample PACF at lags 1..MAX_LAG via Durbin-Levinson recursion +const pacf = [acf[1]]; +let phi = [acf[1]]; +for (let k = 2; k <= MAX_LAG; k++) { + let num = acf[k], den = 1; + for (let j = 0; j < k - 1; j++) { + num -= phi[j] * acf[k - 1 - j]; + den -= phi[j] * acf[j + 1]; + } + const pkk = num / den; + const next = new Array(k); + for (let j = 0; j < k - 1; j++) next[j] = phi[j] - pkk * phi[k - 2 - j]; + next[k - 1] = pkk; + phi = next; + pacf.push(pkk); +} + +// Color tokens: palette[0] = ACF green #009E73, palette[1] = PACF lavender #C475FD +const SIG_ACF = t.palette[0]; +const INSIG_ACF = "rgba(0,158,115,0.28)"; +const SIG_PACF = t.palette[1]; +const INSIG_PACF = "rgba(196,117,253,0.28)"; + +// Shared 36-slot category axis (lags 0–35) for both panels so bars align vertically +const lagLabels = Array.from({ length: MAX_LAG + 1 }, (_, i) => String(i)); + +const acfBarData = acf.map((v, i) => ({ + value: +v.toFixed(4), + itemStyle: { color: (i === 0 || Math.abs(v) > ci) ? SIG_ACF : INSIG_ACF } +})); + +// Prepend null at lag-0 slot so PACF panel columns align with ACF panel +const pacfBarData = [{ value: null, itemStyle: { color: "transparent" } }].concat( + pacf.map((v) => ({ + value: +v.toFixed(4), + itemStyle: { color: Math.abs(v) > ci ? SIG_PACF : INSIG_PACF } + })) +); + +// Init chart +const chart = echarts.init(document.getElementById("container")); + +chart.setOption({ + animation: false, + color: t.palette, + backgroundColor: "transparent", + + title: { + text: "acf-pacf · javascript · echarts · anyplot.ai", + subtext: "AR(2) daily temperature anomalies · N = 300 · 95% CI shown", + left: "center", + top: 16, + textStyle: { color: t.ink, fontSize: 22, fontWeight: "600" }, + subtextStyle: { color: t.inkSoft, fontSize: 14 } + }, + + // Two stacked grids (1600 × 900 CSS canvas) + grid: [ + { left: 85, right: 50, top: 78, bottom: 472 }, // ACF + { left: 85, right: 50, top: 470, bottom: 62 } // PACF + ], + + xAxis: [ + { + gridIndex: 0, + type: "category", + data: lagLabels, + axisLabel: { show: false }, + axisLine: { lineStyle: { color: t.inkSoft } }, + axisTick: { show: false }, + splitLine: { show: false } + }, + { + gridIndex: 1, + type: "category", + data: lagLabels, + name: "Lag", + nameLocation: "middle", + nameGap: 38, + nameTextStyle: { color: t.ink, fontSize: 15, fontWeight: "500" }, + axisLabel: { color: t.inkSoft, fontSize: 13 }, + axisLine: { lineStyle: { color: t.inkSoft } }, + axisTick: { show: false }, + splitLine: { show: false } + } + ], + + yAxis: [ + { + gridIndex: 0, + type: "value", + name: "ACF", + nameLocation: "middle", + nameGap: 52, + nameTextStyle: { color: t.ink, fontSize: 15, fontWeight: "500" }, + min: -0.35, + max: 1.05, + interval: 0.25, + axisLabel: { color: t.inkSoft, fontSize: 13, formatter: v => v.toFixed(2) }, + axisLine: { show: true, lineStyle: { color: t.inkSoft } }, + splitLine: { lineStyle: { color: t.grid } } + }, + { + gridIndex: 1, + type: "value", + name: "PACF", + nameLocation: "middle", + nameGap: 52, + nameTextStyle: { color: t.ink, fontSize: 15, fontWeight: "500" }, + min: -0.40, + max: 0.85, + interval: 0.25, + axisLabel: { color: t.inkSoft, fontSize: 13, formatter: v => v.toFixed(2) }, + axisLine: { show: true, lineStyle: { color: t.inkSoft } }, + splitLine: { lineStyle: { color: t.grid } } + } + ], + + series: [ + { + type: "bar", + xAxisIndex: 0, + yAxisIndex: 0, + data: acfBarData, + barWidth: 3, + markLine: { + silent: true, + symbol: "none", + label: { show: false }, + data: [ + { yAxis: 0, lineStyle: { color: t.inkSoft, type: "solid", width: 1.5 } }, + { yAxis: ci, lineStyle: { color: t.amber, type: "dashed", width: 2 } }, + { yAxis: -ci, lineStyle: { color: t.amber, type: "dashed", width: 2 } } + ] + } + }, + { + type: "bar", + xAxisIndex: 1, + yAxisIndex: 1, + data: pacfBarData, + barWidth: 3, + markLine: { + silent: true, + symbol: "none", + label: { show: false }, + data: [ + { yAxis: 0, lineStyle: { color: t.inkSoft, type: "solid", width: 1.5 } }, + { yAxis: ci, lineStyle: { color: t.amber, type: "dashed", width: 2 } }, + { yAxis: -ci, lineStyle: { color: t.amber, type: "dashed", width: 2 } } + ] + } + } + ] +}); diff --git a/plots/acf-pacf/metadata/javascript/echarts.yaml b/plots/acf-pacf/metadata/javascript/echarts.yaml new file mode 100644 index 0000000000..412d62110d --- /dev/null +++ b/plots/acf-pacf/metadata/javascript/echarts.yaml @@ -0,0 +1,241 @@ +library: echarts +language: javascript +specification_id: acf-pacf +created: '2026-06-10T02:06:24Z' +updated: '2026-06-10T02:27:14Z' +generated_by: claude-sonnet +workflow_run: 27247913178 +issue: 4663 +language_version: 22.22.3 +library_version: 5.5.1 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/acf-pacf/javascript/echarts/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/acf-pacf/javascript/echarts/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/acf-pacf/javascript/echarts/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/acf-pacf/javascript/echarts/plot-dark.html +quality_score: 88 +review: + strengths: + - Correct PACF computation via Durbin-Levinson recursion — mathematically rigorous + - Significant vs. insignificant bar alpha encoding adds an analytical layer true + to ACF/PACF best practices + - Full Imprint palette compliance with amber CI lines as semantic anchors (threshold/caution + role) + - 'Complete theme adaptation: all chrome tokens thread through both panels in both + renders' + - Subtitle (AR(2) daily temperature anomalies · N = 300 · 95% CI shown) provides + rich interpretive context + - 'Clean shared-axis design: upper panel x-axis labels hidden, tick marks removed + throughout' + weaknesses: + - Tick label font size is 13px CSS (one point below the 14px echarts guide default) + — nudge up to 14px for full compliance + - Muted insignificant PACF bars (rgba alpha ~0.28) are slightly faint on dark background + — consider bumping alpha to 0.35–0.40 for dark theme + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct + Chrome: Title "acf-pacf · javascript · echarts · anyplot.ai" (dark, 22px, centered), subtitle "AR(2) daily temperature anomalies · N = 300 · 95% CI shown" (secondary text, 14px). ACF y-axis label "ACF" and PACF y-axis label "PACF" (15px), x-axis label "Lag" (15px). Tick labels at 13px — all clearly readable. + Data: ACF panel (top) uses brand green #009E73 for significant bars, semi-transparent green rgba(0,158,115,0.28) for insignificant. Characteristic exponential decay from lag 0 (~1.0) through lag ~20. PACF panel (bottom) uses lavender #C475FD for significant bars (prominent spikes at lags 1 and 2), semi-transparent lavender for insignificant. Amber dashed CI lines at ±0.113. Zero baseline solid. + Legibility verdict: PASS — all text readable against warm off-white background, no light-on-light issues. + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct + Chrome: Title, subtitle, axis labels, tick labels all rendered in light-colored text (ink tokens flipped). Clearly readable against dark background. Grid lines are subtle light-colored rules. No dark-on-dark failures observed. + Data: ACF bars remain identical green #009E73; PACF bars remain identical lavender #C475FD; CI lines remain amber #DDCC77. Data colors exactly match light render — only chrome flipped. + Legibility verdict: PASS — all text readable against warm near-black background, no dark-on-dark issues. + criteria_checklist: + visual_quality: + score: 28 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: All font sizes explicitly set; tick labels at 13px CSS are 1px below + guide default of 14px + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: Clean two-panel layout with no text collisions + - id: VQ-03 + name: Element Visibility + score: 5 + max: 6 + passed: true + comment: 3px bars appropriate for 36 lags; insignificant bars visible but + slightly faint on dark background + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Green/lavender distinct hue families; amber CI lines; CVD-safe Imprint + palette + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Canvas gate passed; two-panel layout fills canvas well with balanced + margins + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Lag, ACF, PACF per spec; title format correct + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'ACF=palette[0] #009E73, PACF=palette[1] #C475FD, CI=amber #DDCC77; + transparent bg shows page tokens; colors identical across themes' + design_excellence: + score: 13 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: 'Above defaults: per-item alpha encoding for significant/insignificant + bars, informative subtitle, semantic CI lines' + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Tick marks removed; upper panel x-axis labels hidden; grid only on + y-axis; good whitespace + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: AR(2) signature immediately readable; alpha encoding creates visual + hierarchy; subtitle anchors interpretation + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct dual-panel ACF/PACF layout with narrow stem-style bars + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: ACF top + PACF bottom; shared lag x-axis; 95% CI dashed; lag 0 in + ACF; PACF from lag 1; 35 lags; zero baseline + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Lag on x-axis; correlation values on y; axes correctly ranged + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title matches spec format; panels labeled via y-axis names + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Both ACF and PACF; significant/insignificant coding; Durbin-Levinson + PACF; CI bounds; lag-0 in ACF + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: AR(2) daily temperature anomaly residuals — neutral scientific domain, + N=300 + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Correlation values in [-1,1]; y-axis ranges match data; AR(2) signature + factually consistent + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Minimal LCG helpers, flat data → config → setOption + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fixed-seed LCG (seed=42); fully deterministic + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: No imports; echarts is global per harness contract + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean, well-organized; idiomatic per-item itemStyle; no fake UI + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Harness generates plot-{theme}.png + plot-{theme}.html; ECharts 5.5.1 + API + library_mastery: + score: 7 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Multiple grids, xAxisIndex/yAxisIndex association, markLine within + series, per-item itemStyle object format + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: markLine for CI reference lines (ECharts-specific), multiple grid + instances for panel alignment, per-item data object format + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - subplots + - html-export + - manual-ticks + patterns: + - data-generation + dataprep: [] + styling: + - alpha-blending