diff --git a/plots/acf-pacf/implementations/julia/makie.jl b/plots/acf-pacf/implementations/julia/makie.jl new file mode 100644 index 0000000000..db556695bc --- /dev/null +++ b/plots/acf-pacf/implementations/julia/makie.jl @@ -0,0 +1,170 @@ +# anyplot.ai +# acf-pacf: Autocorrelation and Partial Autocorrelation (ACF/PACF) Plot +# Library: makie 0.22.10 | Julia 1.11.9 +# Quality: 86/100 | Created: 2026-06-10 + +using CairoMakie +using Colors +using Random +using Statistics + +Random.seed!(42) + +# Theme tokens +const THEME = get(ENV, "ANYPLOT_THEME", "light") +const PAGE_BG = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17" +const INK = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8" +const INK_SOFT = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0" +const IMPRINT_PALETTE = [ + colorant"#009E73", colorant"#C475FD", colorant"#4467A3", colorant"#BD8233", + colorant"#AE3030", colorant"#2ABCCD", colorant"#954477", colorant"#99B314", +] + +# Data: AR(2) time series — monthly economic indicator +# x[t] = 0.7*x[t-1] + 0.2*x[t-2] + noise (φ₁=0.7, φ₂=0.2) +# PACF cuts off at lag 2; ACF decays as a mixture of exponentials +n_obs = 300 +series = zeros(n_obs) +series[1] = randn() +series[2] = 0.7 * series[1] + randn() +for t in 3:n_obs + series[t] = 0.7 * series[t-1] + 0.2 * series[t-2] + randn() +end + +# ACF (lags 0 through max_lag) +max_lag = 35 +s_mean = mean(series) +s_var = sum((v - s_mean)^2 for v in series) / n_obs +acf_vals = [sum((series[i] - s_mean) * (series[i-k] - s_mean) for i in (k+1):n_obs) / (n_obs * s_var) + for k in 0:max_lag] + +# PACF via Levinson-Durbin recursion +phi = zeros(max_lag, max_lag) +phi[1, 1] = acf_vals[2] +pacf_vals = zeros(max_lag) +pacf_vals[1] = phi[1, 1] +for k in 2:max_lag + numer = acf_vals[k + 1] + denom = 1.0 + for j in 1:(k - 1) + numer -= phi[k-1, j] * acf_vals[k - j + 1] + denom -= phi[k-1, j] * acf_vals[j + 1] + end + phi[k, k] = numer / denom + for j in 1:(k - 1) + phi[k, j] = phi[k-1, j] - phi[k, k] * phi[k-1, k-j] + end + pacf_vals[k] = phi[k, k] +end + +conf_bound = 1.96 / sqrt(n_obs) +acf_lags = Float64.(0:max_lag) + +# Build linesegments! data for ACF: interleaved [x_base, x_tip, ...] pairs per segment +acf_seg_x = vcat([[lag, lag] for lag in acf_lags]...) +acf_seg_y = vcat([[0.0, v] for v in acf_vals]...) + +# PACF: split lags into significant (|r| > conf_bound) vs. within-bound for visual hierarchy +pacf_sig_idx = findall(v -> abs(v) > conf_bound, pacf_vals) +pacf_ns_idx = findall(v -> abs(v) <= conf_bound, pacf_vals) + +# Muted variant for non-significant PACF lags (same hue, lower opacity) +GREEN_FULL = IMPRINT_PALETTE[1] +GREEN_MUTED = RGBAf(GREEN_FULL.r, GREEN_FULL.g, GREEN_FULL.b, 0.38f0) + +function make_segs(idx, vals) + xs = vcat([[Float64(i), Float64(i)] for i in idx]...) + ys = vcat([[0.0, vals[i]] for i in idx]...) + xs, ys +end + +# Figure +fig = Figure( + size = (1600, 900), + fontsize = 14, + backgroundcolor = PAGE_BG, +) + +# ACF axis (top) +ax_acf = Axis( + fig[1, 1]; + title = "AR(2) Process · acf-pacf · julia · makie · anyplot.ai", + titlesize = 20, + titlecolor = INK, + ylabel = "ACF", + ylabelsize = 14, + ylabelcolor = INK, + xticklabelsize = 12, + yticklabelsize = 12, + xticklabelcolor = INK_SOFT, + yticklabelcolor = INK_SOFT, + xtickcolor = INK_SOFT, + ytickcolor = INK_SOFT, + backgroundcolor = PAGE_BG, + topspinevisible = false, + rightspinevisible = false, + leftspinecolor = INK_SOFT, + bottomspinecolor = INK_SOFT, + xgridvisible = false, + ygridvisible = true, + ygridcolor = RGBAf(INK.r, INK.g, INK.b, 0.12f0), +) + +# PACF axis (bottom) +ax_pacf = Axis( + fig[2, 1]; + xlabel = "Lag", + ylabel = "PACF", + xlabelsize = 14, + ylabelsize = 14, + xlabelcolor = INK, + ylabelcolor = INK, + xticklabelsize = 12, + yticklabelsize = 12, + xticklabelcolor = INK_SOFT, + yticklabelcolor = INK_SOFT, + xtickcolor = INK_SOFT, + ytickcolor = INK_SOFT, + backgroundcolor = PAGE_BG, + topspinevisible = false, + rightspinevisible = false, + leftspinecolor = INK_SOFT, + bottomspinecolor = INK_SOFT, + xgridvisible = false, + ygridvisible = true, + ygridcolor = RGBAf(INK.r, INK.g, INK.b, 0.12f0), +) + +linkxaxes!(ax_acf, ax_pacf) +ax_acf.xticklabelsvisible = false +ax_acf.xlabelvisible = false + +# ACF stems, tips, and reference lines +linesegments!(ax_acf, acf_seg_x, acf_seg_y; + color = GREEN_FULL, linewidth = 2.0) +scatter!(ax_acf, acf_lags, acf_vals; + color = GREEN_FULL, markersize = 8, strokewidth = 0) +hlines!(ax_acf, [0.0]; color = INK_SOFT, linewidth = 1.0) +hlines!(ax_acf, [conf_bound, -conf_bound]; + color = INK_SOFT, linestyle = :dash, linewidth = 1.5) + +# PACF: within-bound lags (muted) drawn first, then significant lags on top +if !isempty(pacf_ns_idx) + ns_sx, ns_sy = make_segs(pacf_ns_idx, pacf_vals) + linesegments!(ax_pacf, ns_sx, ns_sy; color = GREEN_MUTED, linewidth = 1.5) + scatter!(ax_pacf, Float64.(pacf_ns_idx), pacf_vals[pacf_ns_idx]; + color = GREEN_MUTED, markersize = 7, strokewidth = 0) +end +if !isempty(pacf_sig_idx) + sig_sx, sig_sy = make_segs(pacf_sig_idx, pacf_vals) + linesegments!(ax_pacf, sig_sx, sig_sy; color = GREEN_FULL, linewidth = 2.5) + scatter!(ax_pacf, Float64.(pacf_sig_idx), pacf_vals[pacf_sig_idx]; + color = GREEN_FULL, markersize = 9, strokewidth = 0) +end +hlines!(ax_pacf, [0.0]; color = INK_SOFT, linewidth = 1.0) +hlines!(ax_pacf, [conf_bound, -conf_bound]; + color = INK_SOFT, linestyle = :dash, linewidth = 1.5) + +rowgap!(fig.layout, 1, 20) + +save("plot-$(THEME).png", fig; px_per_unit = 2) diff --git a/plots/acf-pacf/metadata/julia/makie.yaml b/plots/acf-pacf/metadata/julia/makie.yaml new file mode 100644 index 0000000000..cded70e7e9 --- /dev/null +++ b/plots/acf-pacf/metadata/julia/makie.yaml @@ -0,0 +1,249 @@ +library: makie +language: julia +specification_id: acf-pacf +created: '2026-06-10T02:02:27Z' +updated: '2026-06-10T02:25:19Z' +generated_by: claude-sonnet +workflow_run: 27247698933 +issue: 4663 +language_version: 1.11.9 +library_version: 0.22.10 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/acf-pacf/julia/makie/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/acf-pacf/julia/makie/plot-dark.png +preview_html_light: null +preview_html_dark: null +quality_score: 86 +review: + strengths: + - 'Excellent spec compliance: all required features present including shared x-axis + via linkxaxes!, vertical stems with dot tips, dashed 95% confidence bounds, lag + 0 in ACF but not PACF' + - 'Strong data storytelling: AR(2) process choice beautifully illustrates the textbook + ACF/PACF signatures — slow exponential ACF decay contrasted with sharp PACF cutoff + at lag 2' + - 'Visual hierarchy for PACF: muted alpha (0.38) for non-significant lags vs full + #009E73 for significant lags — immediately guides the eye to the identifying AR(2) + spikes' + - 'Full theme-adaptive chrome: all six Makie color tokens (PAGE_BG, INK, INK_SOFT, + ygridcolor) correctly wired — both renders are clean and readable' + - Levinson-Durbin recursion implemented from scratch — no statsmodels/scipy dependency + needed, keeps implementation self-contained + weaknesses: + - Helper function make_segs() at line 75 violates the KISS no-functions/classes + rule — inline the two-line segment construction directly at each call site + - 'Design Excellence ceiling: plot reads as a well-configured professional chart + but lacks the typographic weight variation (bold title, lighter tick labels) and + deliberate whitespace that would push it toward publication-ready' + image_description: |- + Light render (plot-light.png): + Background: Warm off-white #FAF8F1 — correct, not pure white + Chrome: Title "AR(2) Process · acf-pacf · julia · makie · anyplot.ai" in dark ink, clearly readable. "ACF" and "PACF" y-axis labels in dark ink. "Lag" x-axis label (bottom panel only) in dark ink. Tick labels in INK_SOFT (#4A4A44). All text fully readable. + Data: Single color #009E73 (Imprint brand green) for ACF stems/dots and significant PACF stems/dots. Non-significant PACF lags rendered in same hue at alpha=0.38 — visually muted but legible. Dashed confidence bounds at ~±0.113. Horizontal zero baseline in INK_SOFT. Top and right spines removed, subtle y-axis grid with alpha=0.12. + Legibility verdict: PASS — all text readable at desktop and estimated mobile scale + + Dark render (plot-dark.png): + Background: Warm near-black #1A1A17 — correct, not pure black + Chrome: Title, axis labels, tick labels all in light colors (INK = #F0EFE8 for primary, INK_SOFT = #B8B7B0 for secondary). No dark-on-dark failures observed — text is clearly legible against the dark surface. + Data: #009E73 data stems/dots identical to light render — color identity preserved across themes. Confidence bound dashes and zero baseline appear in theme-appropriate light tones. Y-grid is faint but visible. + Legibility verdict: PASS — no dark-on-dark issues, all text readable + 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 (titlesize=20, labels=14, ticks=12). + Both renders readable. Near-perfect proportions. + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No overlapping elements across 36 lags in two panels + - id: VQ-03 + name: Element Visibility + score: 5 + max: 6 + passed: true + comment: Stems and tips clearly visible. Muted PACF dots (alpha=0.38, markersize=7) + are visible but slightly small. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: 'Single hue #009E73 with alpha differentiation — no red-green issue, + good contrast both backgrounds' + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Two panels fill canvas well, rowgap=20 gives breathing room, no content + cut off. 3200x1800 confirmed. + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: ACF/PACF are standard accepted abbreviations, Lag is clear. Descriptive + title prefix 'AR(2) Process'. + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series #009E73, backgrounds #FAF8F1/#1A1A17, full theme-adaptive + chrome. Data 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 well-configured default: intentional significant/non-significant + visual hierarchy, clean spine removal, professional Imprint palette application. + Not yet FiveThirtyEight-level.' + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Top/right spines removed, subtle y-grid (alpha=0.12), dashed confidence + lines add visual interest. Good but not perfect. + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: AR(2) process choice is pedagogically intentional — slow ACF decay + vs PACF cutoff at lag 2. Visual emphasis on significant lags creates a clear + focal point. + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Two-panel ACF/PACF stem plot — exactly the specified chart type + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Shared x-axis (linkxaxes!), 95% CI dashed lines, lag 0 in ACF only, + stems+tips, 35 lags + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=Lag, Y=correlation values, all data visible + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: 'Title: ''AR(2) Process · acf-pacf · julia · makie · anyplot.ai'' + — correct format with descriptive prefix. No legend needed.' + data_quality: + score: 14 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: AR(2) shows both slow ACF decay and PACF cutoff — demonstrates all + distinguishing features of the plot type + - id: DQ-02 + name: Realistic Context + score: 4 + max: 5 + passed: true + comment: Monthly economic indicator context in comments, realistic AR(2) parameters + (phi1=0.7, phi2=0.2). Good but context only in comments, not axis labels. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 300 observations (within 100-500 spec), correlation values in [-1,1], + confidence bounds at correct 1.96/sqrt(300) + code_quality: + score: 9 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 2 + max: 3 + passed: true + comment: Defines make_segs() helper function at line 75 — violates no-functions + rule. Small and practical but still a function. + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Random.seed!(42) at line 11 + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: CairoMakie (plotting), Colors (colorant), Random (seed), Statistics + (mean) — all used + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean Levinson-Durbin recursion, logical separation of significant + vs non-significant lags, no fake interactivity + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: save("plot-$(THEME).png", fig; px_per_unit = 2) — correct + library_mastery: + score: 7 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: linkxaxes!, linesegments!, scatter!, hlines!, rowgap!, RGBAf — very + good idiomatic Makie usage + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: linkxaxes! for shared axes, Makie layout system (fig[1,1]/fig[2,1]), + rowgap!, RGBAf alpha colors — Makie-specific patterns used well + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - subplots + patterns: + - data-generation + dataprep: + - time-series + styling: + - alpha-blending