{"spec_id":"acf-pacf","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nacf-pacf: Autocorrelation and Partial Autocorrelation (ACF/PACF) Plot\nLibrary: bokeh 3.9.1 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-10\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent self-import: this file is named bokeh.py, which shadows the installed\n# bokeh package when its directory sits at the front of sys.path.\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p or \".\") != _this_dir]\n\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.layouts import column\nfrom bokeh.models import BoxAnnotation, ColumnDataSource, HoverTool, Label, Span\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\nfrom statsmodels.tsa.stattools import acf, pacf\n\n\n# Theme-adaptive chrome (Imprint palette)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — first series always #009E73\nBRAND = \"#009E73\"  # significant lags (Imprint position 1)\nCI_COLOR = \"#AE3030\"  # confidence interval (Imprint position 5 — semantic threshold)\nAR_ACCENT = \"#BD8233\"  # AR(2) highlights (Imprint position 4, ochre)\n\n# Data — simulated monthly retail sales with AR(2) structure\n# AR(2): positive lag-1 momentum + negative lag-2 correction\nnp.random.seed(42)\nn_obs = 200\nseries = np.zeros(n_obs)\nfor i in range(2, n_obs):\n    series[i] = 0.6 * series[i - 1] - 0.3 * series[i - 2] + np.random.randn()\n\nn_lags = 35\nacf_values = acf(series, nlags=n_lags, fft=True)\npacf_values = pacf(series, nlags=n_lags, method=\"ywm\")\nconf_bound = 1.96 / np.sqrt(n_obs)\n\nacf_significant = np.abs(acf_values) > conf_bound\npacf_significant = np.abs(pacf_values[1:]) > conf_bound\n\n# Canvas — two stacked subplots; Selenium screenshots viewport at W×H = 3200×1800\nW, H = 3200, 1800\nSUBPLOT_H = 880\n\n# ACF data sources\nacf_lags = np.arange(len(acf_values))\nacf_colors = [BRAND if s else INK_MUTED for s in acf_significant]\nacf_stem_src = ColumnDataSource(\n    {\"x0\": acf_lags, \"y0\": np.zeros(len(acf_lags)), \"x1\": acf_lags, \"y1\": acf_values, \"color\": acf_colors}\n)\nacf_src = ColumnDataSource(\n    {\n        \"x\": acf_lags,\n        \"y\": acf_values,\n        \"color\": acf_colors,\n        \"sig\": [\"Significant\" if s else \"Not significant\" for s in acf_significant],\n        \"val\": [f\"{v:.3f}\" for v in acf_values],\n    }\n)\n\n# PACF data sources\npacf_lags = np.arange(1, len(pacf_values))\npacf_vals = pacf_values[1:]\npacf_colors = [BRAND if s else INK_MUTED for s in pacf_significant]\npacf_stem_src = ColumnDataSource(\n    {\"x0\": pacf_lags, \"y0\": np.zeros(len(pacf_lags)), \"x1\": pacf_lags, \"y1\": pacf_vals, \"color\": pacf_colors}\n)\npacf_src = ColumnDataSource(\n    {\n        \"x\": pacf_lags,\n        \"y\": pacf_vals,\n        \"color\": pacf_colors,\n        \"sig\": [\"Significant\" if s else \"Not significant\" for s in pacf_significant],\n        \"val\": [f\"{v:.3f}\" for v in pacf_vals],\n    }\n)\n\n# --- ACF plot (top) ---\np_acf = figure(\n    title=\"acf-pacf · bokeh · anyplot.ai\",\n    x_axis_label=\"Lag\",\n    y_axis_label=\"ACF\",\n    width=W,\n    height=SUBPLOT_H,\n    background_fill_color=PAGE_BG,\n    border_fill_color=PAGE_BG,\n    toolbar_location=None,\n    min_border_bottom=140,\n    min_border_left=180,\n    min_border_top=110,\n    min_border_right=50,\n)\n\np_acf.segment(\"x0\", \"y0\", \"x1\", \"y1\", source=acf_stem_src, line_width=5, color=\"color\", alpha=0.85)\np_acf.scatter(\"x\", \"y\", source=acf_src, size=12, color=\"color\", alpha=0.9)\n\np_acf.add_layout(BoxAnnotation(bottom=-conf_bound, top=conf_bound, fill_alpha=0.08, fill_color=CI_COLOR, line_alpha=0))\np_acf.add_layout(\n    Span(\n        location=conf_bound, dimension=\"width\", line_dash=\"dashed\", line_width=2.5, line_color=CI_COLOR, line_alpha=0.7\n    )\n)\np_acf.add_layout(\n    Span(\n        location=-conf_bound, dimension=\"width\", line_dash=\"dashed\", line_width=2.5, line_color=CI_COLOR, line_alpha=0.7\n    )\n)\np_acf.add_layout(Span(location=0, dimension=\"width\", line_width=1.5, line_color=INK_SOFT, line_alpha=0.5))\n\np_acf.add_tools(HoverTool(tooltips=[(\"Lag\", \"@x\"), (\"ACF\", \"@val\"), (\"Status\", \"@sig\")], mode=\"vline\"))\n\n# --- PACF plot (bottom) ---\np_pacf = figure(\n    x_axis_label=\"Lag\",\n    y_axis_label=\"PACF\",\n    x_range=p_acf.x_range,\n    width=W,\n    height=SUBPLOT_H,\n    background_fill_color=PAGE_BG,\n    border_fill_color=PAGE_BG,\n    toolbar_location=None,\n    min_border_bottom=160,\n    min_border_left=180,\n    min_border_top=50,\n    min_border_right=50,\n)\n\np_pacf.segment(\"x0\", \"y0\", \"x1\", \"y1\", source=pacf_stem_src, line_width=5, color=\"color\", alpha=0.85)\np_pacf.scatter(\"x\", \"y\", source=pacf_src, size=12, color=\"color\", alpha=0.9)\n\np_pacf.add_layout(BoxAnnotation(bottom=-conf_bound, top=conf_bound, fill_alpha=0.08, fill_color=CI_COLOR, line_alpha=0))\np_pacf.add_layout(\n    Span(\n        location=conf_bound, dimension=\"width\", line_dash=\"dashed\", line_width=2.5, line_color=CI_COLOR, line_alpha=0.7\n    )\n)\np_pacf.add_layout(\n    Span(\n        location=-conf_bound, dimension=\"width\", line_dash=\"dashed\", line_width=2.5, line_color=CI_COLOR, line_alpha=0.7\n    )\n)\np_pacf.add_layout(Span(location=0, dimension=\"width\", line_width=1.5, line_color=INK_SOFT, line_alpha=0.5))\n\n# AR(2) structural lags highlighted in ochre; annotation placed edge-right to avoid data overlap\nar_lags = [1, 2]\nar_vals = [pacf_values[lag] for lag in ar_lags]\np_pacf.scatter(ar_lags, ar_vals, size=22, color=AR_ACCENT, alpha=0.95, line_color=INK, line_width=2)\n\np_pacf.add_layout(\n    Label(\n        x=3,\n        y=float(pacf_values[1]),\n        text=\"AR(2) identified\",\n        text_font_size=\"28pt\",\n        text_color=AR_ACCENT,\n        text_font_style=\"bold\",\n        x_offset=5,\n        y_offset=-5,\n    )\n)\n\np_pacf.add_tools(HoverTool(tooltips=[(\"Lag\", \"@x\"), (\"PACF\", \"@val\"), (\"Status\", \"@sig\")], mode=\"vline\"))\n\n# Apply canonical bokeh font sizes and theme-adaptive chrome to both subplots\nfor p in [p_acf, p_pacf]:\n    p.title.text_font_size = \"50pt\"\n    p.title.text_color = INK\n    p.xaxis.axis_label_text_font_size = \"42pt\"\n    p.yaxis.axis_label_text_font_size = \"42pt\"\n    p.xaxis.major_label_text_font_size = \"34pt\"\n    p.yaxis.major_label_text_font_size = \"34pt\"\n    p.xaxis.axis_label_text_color = INK\n    p.yaxis.axis_label_text_color = INK\n    p.xaxis.major_label_text_color = INK_SOFT\n    p.yaxis.major_label_text_color = INK_SOFT\n    p.xaxis.axis_line_color = INK_SOFT\n    p.yaxis.axis_line_color = INK_SOFT\n    p.xaxis.major_tick_line_color = INK_SOFT\n    p.yaxis.major_tick_line_color = INK_SOFT\n    p.xaxis.minor_tick_line_color = None\n    p.yaxis.minor_tick_line_color = None\n    p.xgrid.grid_line_color = INK\n    p.xgrid.grid_line_alpha = 0\n    p.ygrid.grid_line_color = INK\n    p.ygrid.grid_line_alpha = 0.15\n    p.outline_line_color = INK_SOFT\n\n# Layout — two stacked subplots with minimal gap\nlayout = column(p_acf, p_pacf, spacing=5)\n\n# Save interactive HTML\noutput_file(f\"plot-{THEME}.html\")\nsave(layout)\n\n# Screenshot via Selenium headless Chrome — matches bokeh.md pattern\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\n# CDP override forces an exact W×H viewport regardless of outer window chrome\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}