Lag Plot for Time Series Autocorrelation Diagnosis — lets-plot

A lag plot is a scatter plot of a time series against a lagged version of itself, plotting y(t) on the x-axis versus y(t+k) on the y-axis for a given lag order k. If the data is purely random, points scatter uniformly with no visible structure; if autocorrelation is present, distinctive patterns emerge — linear clusters for autoregressive processes, elliptical shapes for seasonal data. This provides a quick visual diagnostic for time series dependence, complementing numerical tools like ACF/PACF.

Lag Plot for Time Series Autocorrelation Diagnosis rendered with lets-plot

Python source (lets-plot)

""" anyplot.ai
scatter-lag: Lag Plot for Time Series Autocorrelation Diagnosis
Library: letsplot 4.10.1 | Python 3.13.14
Quality: 89/100 | Updated: 2026-06-24
"""

import os

import numpy as np
import pandas as pd
from lets_plot import *


LetsPlot.setup_html()

# Theme-adaptive chrome — 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)"

# Imprint sequential colormap: brand-green → blue (single-polarity continuous)
SEQ_LOW = "#009E73"  # position 1
SEQ_HIGH = "#4467A3"  # position 3

# Data: AR(1) temperature process, phi=0.85 → strong positive autocorrelation
np.random.seed(42)
n = 400
lag = 1
phi = 0.85
innovations = np.random.randn(n) * 2.0

temperature = np.zeros(n)
temperature[0] = 20.0
for i in range(1, n):
    temperature[i] = phi * temperature[i - 1] + (1 - phi) * 20.0 + innovations[i]

# Lag plot data: y(t) vs y(t+lag)
value_t = temperature[:-lag]
value_t_lag = temperature[lag:]
time_index = np.arange(len(value_t))
df = pd.DataFrame({"value_t": value_t, "value_t_lag": value_t_lag, "day": time_index})

# Autocorrelation at lag 1
r = np.corrcoef(value_t, value_t_lag)[0, 1]

# Diagonal reference line (y = x)
ref_min = min(value_t.min(), value_t_lag.min()) - 1
ref_max = max(value_t.max(), value_t_lag.max()) + 1
ref_df = pd.DataFrame({"x": [ref_min, ref_max], "y": [ref_min, ref_max]})

# Correlation annotation: placed at bottom-right where point density is low
anno_df = pd.DataFrame({"x": [ref_max - 1.5], "y": [ref_min + 1.5], "label": [f"r = {r:.2f}"]})

plot = (
    ggplot(df, aes(x="value_t", y="value_t_lag", color="day"))
    # Reference diagonal: y = x (perfect lag-1 autocorrelation)
    + geom_line(
        aes(x="x", y="y"),
        data=ref_df,
        color=INK_SOFT,
        size=0.8,
        linetype="dashed",
        inherit_aes=False,
    )
    # OLS regression line — letsplot-native geom_smooth with confidence band
    + geom_smooth(
        aes(x="value_t", y="value_t_lag"),
        data=df,
        method="lm",
        color="#C475FD",
        fill="#C475FD",
        size=1.2,
        alpha=0.12,
        inherit_aes=False,
    )
    # Data points colored by temporal order (Imprint sequential: green → blue)
    + geom_point(
        size=2.5,
        alpha=0.45,
        shape=16,
        tooltips=layer_tooltips()
        .line("Day|@day")
        .line("y(t)|@{value_t}{.2f}")
        .line("y(t+1)|@{value_t_lag}{.2f}"),
    )
    # Correlation coefficient annotation
    + geom_text(
        aes(x="x", y="y", label="label"),
        data=anno_df,
        size=5,
        color=INK,
        family="monospace",
        hjust=1.0,
        inherit_aes=False,
    )
    + scale_color_gradient(
        low=SEQ_LOW, high=SEQ_HIGH, name="Day"
    )
    + labs(
        x="y(t)",
        y=f"y(t + {lag})",
        title="scatter-lag · python · letsplot · anyplot.ai",
        caption="AR(1) simulated daily temperature · dashed = y = x, purple = OLS fit ± 95% CI",
    )
    + ggsize(800, 450)
    + theme_minimal()
    + theme(
        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
        panel_background=element_rect(fill=PAGE_BG),
        panel_grid_major=element_line(color=GRID, size=0.3),
        panel_grid_minor=element_blank(),
        axis_text=element_text(size=10, color=INK_SOFT),
        axis_title=element_text(size=12, color=INK),
        axis_line=element_line(color=INK_SOFT),
        axis_ticks=element_line(color=INK_SOFT, size=0.3),
        plot_title=element_text(size=16, color=INK, face="bold"),
        plot_caption=element_text(size=8, color=INK_MUTED, face="italic"),
        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
        legend_title=element_text(size=10, color=INK),
        legend_text=element_text(size=10, color=INK_SOFT),
        plot_margin=[30, 40, 20, 20],
    )
)

# Save PNG (3200×1800) and interactive HTML
ggsave(plot, f"plot-{THEME}.png", path=".", scale=4)
ggsave(plot, f"plot-{THEME}.html", path=".")

Part of Lag Plot for Time Series Autocorrelation Diagnosis on anyplot.ai.

Other implementations