{"spec_id":"recurrence-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nrecurrence-basic: Recurrence Plot for Nonlinear Time Series\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 88/100 | Created: 2026-06-10\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\nfrom scipy.spatial.distance import cdist\n\n\nLetsPlot.setup_html()\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nBRAND = \"#009E73\"  # Imprint palette position 1\n\n# Data — logistic map in chaotic regime (r=3.8), transient discarded\nnp.random.seed(42)\nN_total = 350\nr = 3.8\nx_all = np.zeros(N_total)\nx_all[0] = 0.5\nfor i in range(1, N_total):\n    x_all[i] = r * x_all[i - 1] * (1 - x_all[i - 1])\nx_series = x_all[100:]  # 250 steps, transient removed\n\n# Time-delay embedding (Takens' theorem): dimension=2, delay=5\ndim = 2\ndelay = 5\nM = len(x_series) - (dim - 1) * delay  # 245 embedded vectors\nembedded = np.array([[x_series[i], x_series[i + delay]] for i in range(M)])\n\n# Pairwise Euclidean distances (245 × 245)\ndist_matrix = cdist(embedded, embedded, metric=\"euclidean\")\n\n# Threshold: 15th percentile of off-diagonal distances → ~15% recurrent\noff_diag = dist_matrix[np.triu_indices(M, k=1)]\nepsilon = np.percentile(off_diag, 15)\n\n# Binary recurrence: 1 where states recur (distance ≤ ε)\nrecurrence = dist_matrix <= epsilon\nrows, cols = np.where(recurrence)\ndf = pd.DataFrame({\"time_i\": rows, \"time_j\": cols})\n\n# Title (49 chars — under 67-char baseline, no scaling needed)\ntitle = \"recurrence-basic · python · letsplot · anyplot.ai\"\n\n# Theme — color=PAGE_BG on panel_background hides the panel box, yielding an L-shaped frame\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_border=element_blank(),\n    panel_grid_major=element_blank(),\n    panel_grid_minor=element_blank(),\n    axis_title=element_text(color=INK, size=12),\n    axis_text=element_text(color=INK_SOFT, size=10),\n    axis_line=element_line(color=INK_SOFT),\n    plot_title=element_text(color=INK, size=16),\n    legend_position=\"none\",\n)\n\n# Plot — recurrent pairs as tiles; diagonal line marks Line of Identity (i=j)\nplot = (\n    ggplot(df, aes(x=\"time_i\", y=\"time_j\"))\n    + geom_tile(fill=BRAND, alpha=0.9, tooltips=layer_tooltips([\"time_i\", \"time_j\"]))\n    + geom_abline(slope=1, intercept=0, color=ELEVATED_BG, size=0.6, alpha=0.7)\n    + labs(x=\"Time index i\", y=\"Time index j\", title=title)\n    + anyplot_theme\n    + ggsize(600, 600)\n    + coord_fixed(ratio=1.0)\n)\n\n# Save\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}