Recurrence Plot for Nonlinear Time Series — lets-plot

A recurrence plot is a binary or distance-based matrix visualization that reveals when states in a dynamical system recur over time. Both axes represent time indices, and a point is plotted at position (i, j) when the system state at time i is sufficiently similar to the state at time j (distance below a threshold). The resulting symmetric matrix exposes hidden structure in complex, nonlinear dynamics — diagonal lines indicate determinism, vertical/horizontal lines reveal laminar states, and block structures signal regime changes.

Recurrence Plot for Nonlinear Time Series rendered with lets-plot

Renders

Python source (lets-plot)

""" anyplot.ai
recurrence-basic: Recurrence Plot for Nonlinear Time Series
Library: letsplot 4.10.1 | Python 3.13.13
Quality: 88/100 | Created: 2026-06-10
"""

import os

import numpy as np
import pandas as pd
from lets_plot import *
from scipy.spatial.distance import cdist


LetsPlot.setup_html()

# Theme tokens
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"
BRAND = "#009E73"  # Imprint palette position 1

# Data — logistic map in chaotic regime (r=3.8), transient discarded
np.random.seed(42)
N_total = 350
r = 3.8
x_all = np.zeros(N_total)
x_all[0] = 0.5
for i in range(1, N_total):
    x_all[i] = r * x_all[i - 1] * (1 - x_all[i - 1])
x_series = x_all[100:]  # 250 steps, transient removed

# Time-delay embedding (Takens' theorem): dimension=2, delay=5
dim = 2
delay = 5
M = len(x_series) - (dim - 1) * delay  # 245 embedded vectors
embedded = np.array([[x_series[i], x_series[i + delay]] for i in range(M)])

# Pairwise Euclidean distances (245 × 245)
dist_matrix = cdist(embedded, embedded, metric="euclidean")

# Threshold: 15th percentile of off-diagonal distances → ~15% recurrent
off_diag = dist_matrix[np.triu_indices(M, k=1)]
epsilon = np.percentile(off_diag, 15)

# Binary recurrence: 1 where states recur (distance ≤ ε)
recurrence = dist_matrix <= epsilon
rows, cols = np.where(recurrence)
df = pd.DataFrame({"time_i": rows, "time_j": cols})

# Title (49 chars — under 67-char baseline, no scaling needed)
title = "recurrence-basic · python · letsplot · anyplot.ai"

# Theme — color=PAGE_BG on panel_background hides the panel box, yielding an L-shaped frame
anyplot_theme = 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(),
    panel_grid_major=element_blank(),
    panel_grid_minor=element_blank(),
    axis_title=element_text(color=INK, size=12),
    axis_text=element_text(color=INK_SOFT, size=10),
    axis_line=element_line(color=INK_SOFT),
    plot_title=element_text(color=INK, size=16),
    legend_position="none",
)

# Plot — recurrent pairs as tiles; diagonal line marks Line of Identity (i=j)
plot = (
    ggplot(df, aes(x="time_i", y="time_j"))
    + geom_tile(fill=BRAND, alpha=0.9, tooltips=layer_tooltips(["time_i", "time_j"]))
    + geom_abline(slope=1, intercept=0, color=ELEVATED_BG, size=0.6, alpha=0.7)
    + labs(x="Time index i", y="Time index j", title=title)
    + anyplot_theme
    + ggsize(600, 600)
    + coord_fixed(ratio=1.0)
)

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

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/recurrence-basic/letsplot/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "recurrence-basic",
  "language": "python",
  "library": "letsplot",
  "page": "https://anyplot.ai/recurrence-basic/python/letsplot",
  "hub": "https://anyplot.ai/recurrence-basic",
  "code_json": "https://api.anyplot.ai/specs/recurrence-basic/letsplot/code",
  "spec_json": "https://api.anyplot.ai/specs/recurrence-basic",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/recurrence-basic/python/letsplot/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/recurrence-basic/python/letsplot/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/recurrence-basic/python/letsplot/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/recurrence-basic/python/letsplot/plot-dark.html",
  "quality_score": 88.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Recurrence Plot for Nonlinear Time Series on anyplot.ai.

Other implementations