Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
189 changes: 93 additions & 96 deletions plots/acf-pacf/implementations/python/letsplot.py
Original file line number Diff line number Diff line change
@@ -1,119 +1,116 @@
""" pyplots.ai
""" anyplot.ai
acf-pacf: Autocorrelation and Partial Autocorrelation (ACF/PACF) Plot
Library: letsplot 4.9.0 | Python 3.14.3
Quality: 90/100 | Created: 2026-03-14
Library: letsplot 4.10.1 | Python 3.13.13
Quality: 89/100 | Updated: 2026-06-10
"""

import os

import numpy as np
import pandas as pd
from lets_plot import * # noqa: F403
from lets_plot.export import ggsave as export_ggsave
from lets_plot import (
LetsPlot,
aes,
element_blank,
element_line,
element_rect,
element_text,
facet_wrap,
geom_hline,
geom_point,
geom_ribbon,
geom_segment,
ggplot,
ggsave,
ggsize,
labs,
scale_color_manual,
scale_x_continuous,
theme,
theme_minimal,
)
from statsmodels.tsa.stattools import acf, pacf


LetsPlot.setup_html() # noqa: F405
LetsPlot.setup_html()

# Theme tokens — Imprint palette
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
GRID = "rgba(26,26,23,0.15)" if THEME == "light" else "rgba(240,239,232,0.15)"
BRAND = "#009E73" # Imprint position 1 — always first series

# Data - Simulate monthly airline-style passenger data with trend and seasonality
# Data monthly airline-style passenger series with trend and seasonality
np.random.seed(42)
n = 200
time = np.arange(n)
trend = 0.05 * time
seasonal = 10 * np.sin(2 * np.pi * time / 12)
noise = np.random.normal(0, 2, n)
passengers = 100 + trend + seasonal + noise
t = np.arange(n)
series = 100 + 0.05 * t + 10 * np.sin(2 * np.pi * t / 12) + np.random.normal(0, 2, n)

# Compute ACF and PACF
n_lags = 36
acf_values = acf(passengers, nlags=n_lags)
pacf_values = pacf(passengers, nlags=n_lags)
acf_vals = acf(series, nlags=n_lags)
pacf_vals = pacf(series, nlags=n_lags)
ci = 1.96 / np.sqrt(n)

# 95% confidence interval
ci_bound = 1.96 / np.sqrt(n)
acf_df = pd.DataFrame({"lag": np.arange(n_lags + 1), "value": acf_vals, "zero": 0.0, "panel": "ACF"})
acf_df["sig"] = (acf_df["lag"] > 0) & (acf_df["value"].abs() > ci)

# Prepare ACF data (include lag 0)
acf_lags = list(range(0, n_lags + 1))
acf_df = pd.DataFrame(
{
"lag": acf_lags,
"value": acf_values.tolist(),
"zero": 0.0,
"significant": [abs(v) > ci_bound and i > 0 for i, v in enumerate(acf_values)],
}
)
acf_df["color"] = acf_df["significant"].map({True: "Significant", False: "Non-significant"})
pacf_df = pd.DataFrame({"lag": np.arange(1, n_lags + 1), "value": pacf_vals[1:], "zero": 0.0, "panel": "PACF"})
pacf_df["sig"] = pacf_df["value"].abs() > ci

# Prepare PACF data (start from lag 1)
pacf_vals = pacf_values[1:].tolist()
pacf_lags = list(range(1, n_lags + 1))
pacf_df = pd.DataFrame(
{"lag": pacf_lags, "value": pacf_vals, "zero": 0.0, "significant": [abs(v) > ci_bound for v in pacf_vals]}
)
pacf_df["color"] = pacf_df["significant"].map({True: "Significant", False: "Non-significant"})
df = pd.concat([acf_df, pacf_df], ignore_index=True)
df["label"] = df["sig"].map({True: "Significant", False: "Non-significant"})

# Color palette
sig_colors = ["#306998", "#C0392B"] # Non-significant (blue), Significant (red)
sig_labels = ["Non-significant", "Significant"]
# CI band columns — semi-transparent shaded confidence zone behind the stems
df["ci_ymin"] = -ci
df["ci_ymax"] = ci

# Common theme
x_breaks = list(range(0, n_lags + 1, 6))
common_theme = (
flavor_high_contrast_light() # noqa: F405
+ theme( # noqa: F405
axis_text=element_text(size=16), # noqa: F405
axis_title=element_text(size=20), # noqa: F405
panel_grid_major_x=element_blank(), # noqa: F405
panel_grid_minor=element_blank(), # noqa: F405
panel_grid_major_y=element_line(color="#E0E0E0", size=0.4), # noqa: F405
legend_text=element_text(size=16), # noqa: F405
legend_title=element_blank(), # noqa: F405
legend_position=[0.85, 0.85], # noqa: F405
legend_background=element_rect(fill="white", color="#CCCCCC", size=0.5), # noqa: F405
)
)
# Separate datasets for visual hierarchy — significant stems are drawn thicker
sig_df = df[df["sig"]].copy()
nonsig_df = df[~df["sig"]].copy()

# ACF plot
acf_plot = (
ggplot(acf_df, aes(x="lag", y="value", color="color")) # noqa: F405
+ geom_segment( # noqa: F405
aes(x="lag", y="zero", xend="lag", yend="value", color="color"), # noqa: F405
size=2.5,
)
+ geom_point(aes(x="lag", y="value", color="color"), size=4.5) # noqa: F405
+ geom_hline(yintercept=0, color="#333333", size=0.5) # noqa: F405
+ geom_hline(yintercept=ci_bound, color="#999999", size=0.7, linetype="dashed") # noqa: F405
+ geom_hline(yintercept=-ci_bound, color="#999999", size=0.7, linetype="dashed") # noqa: F405
+ scale_color_manual(values=sig_colors, limits=sig_labels) # noqa: F405
+ scale_x_continuous(breaks=x_breaks) # noqa: F405
+ labs(x="", y="ACF", title="acf-pacf · letsplot · pyplots.ai") # noqa: F405
+ theme(plot_title=element_text(size=24, hjust=0.5, face="bold")) # noqa: F405
+ common_theme
+ ggsize(1600, 500) # noqa: F405
)
color_order = ["Significant", "Non-significant"]
color_values = [BRAND, INK_MUTED]

# PACF plot (no legend - shared with ACF)
pacf_plot = (
ggplot(pacf_df, aes(x="lag", y="value", color="color")) # noqa: F405
+ geom_segment( # noqa: F405
aes(x="lag", y="zero", xend="lag", yend="value", color="color"), # noqa: F405
size=2.5,
# Plot — faceted ACF / PACF panels; theme_minimal() as lets-plot built-in base preset
plot = (
ggplot(df, aes(x="lag", y="value"))
+ geom_ribbon(aes(x="lag", ymin="ci_ymin", ymax="ci_ymax"), fill=INK_SOFT, alpha=0.1)
+ geom_hline(yintercept=0, color=INK_SOFT, size=0.5)
+ geom_hline(yintercept=ci, color=INK_MUTED, size=0.7, linetype="dashed")
+ geom_hline(yintercept=-ci, color=INK_MUTED, size=0.7, linetype="dashed")
+ geom_segment(aes(x="lag", y="zero", xend="lag", yend="value", color="label"), data=nonsig_df, size=0.8)
+ geom_segment(aes(x="lag", y="zero", xend="lag", yend="value", color="label"), data=sig_df, size=2.0)
+ geom_point(aes(x="lag", y="value", color="label"), data=nonsig_df, size=1.5)
+ geom_point(aes(x="lag", y="value", color="label"), data=sig_df, size=3.5)
+ scale_color_manual(values=color_values, limits=color_order, name="")
+ scale_x_continuous(breaks=list(range(0, n_lags + 1, 6)))
+ facet_wrap("panel", ncol=1, scales="free_y")
+ labs(x="Lag", y="Correlation", title="acf-pacf · python · letsplot · anyplot.ai")
+ theme_minimal()
+ theme(
plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
panel_border=element_blank(),
strip_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT, size=0.5),
strip_text=element_text(color=INK, size=14, face="bold"),
panel_grid_major_x=element_blank(),
panel_grid_minor=element_blank(),
panel_grid_major_y=element_line(color=GRID, size=0.5),
axis_title=element_text(color=INK, size=12),
axis_text=element_text(color=INK_SOFT, size=10),
axis_line=element_line(color=INK_SOFT, size=0.5),
plot_title=element_text(color=INK, size=16, hjust=0.5),
legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT, size=0.5),
legend_text=element_text(color=INK_SOFT, size=10),
legend_title=element_blank(),
legend_position="bottom",
)
+ geom_point(aes(x="lag", y="value", color="color"), size=4.5) # noqa: F405
+ geom_hline(yintercept=0, color="#333333", size=0.5) # noqa: F405
+ geom_hline(yintercept=ci_bound, color="#999999", size=0.7, linetype="dashed") # noqa: F405
+ geom_hline(yintercept=-ci_bound, color="#999999", size=0.7, linetype="dashed") # noqa: F405
+ scale_color_manual(values=sig_colors, limits=sig_labels) # noqa: F405
+ scale_x_continuous(breaks=x_breaks) # noqa: F405
+ labs(x="Lag", y="PACF") # noqa: F405
+ common_theme
+ theme(legend_position="none") # noqa: F405
+ ggsize(1600, 470) # noqa: F405
)

# Combine vertically
combined = ggbunch( # noqa: F405
plots=[acf_plot, pacf_plot], regions=[(0, 0, 1, 0.54, 0, 0), (0, 0.48, 1, 0.54, 0, 0)]
+ ggsize(800, 450)
)

# Save
export_ggsave(combined, filename="plot.png", path=".", scale=3)
export_ggsave(combined, filename="plot.html", path=".")
ggsave(plot, f"plot-{THEME}.png", path=".", scale=4)
ggsave(plot, f"plot-{THEME}.html", path=".")
Loading
Loading