{"spec_id":"acf-pacf","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nacf-pacf: Autocorrelation and Partial Autocorrelation (ACF/PACF) Plot\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-10\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent local files (matplotlib.py, etc.) from shadowing installed packages\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nif _script_dir in sys.path:\n    sys.path.remove(_script_dir)\nif \"\" in sys.path:\n    sys.path.remove(\"\")\nif \".\" in sys.path:\n    sys.path.remove(\".\")\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens (Imprint chrome — see prompts/default-style-guide.md)\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nBRAND = \"#009E73\"  # Imprint palette position 1 — always first series\nANYPLOT_AMBER = \"#DDCC77\"  # caution/threshold marker for CI bounds\n\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data: ARMA(1,1) process with seasonal component (airline passenger residuals)\nnp.random.seed(42)\nn_obs = 200\nar1_coeff = 0.7\nma1_coeff = 0.4\nseasonal_period = 12\nseasonal_strength = 0.3\nnoise = np.random.randn(n_obs)\nseries = np.zeros(n_obs)\nseries[0] = noise[0]\nfor t in range(1, n_obs):\n    seasonal = seasonal_strength * np.sin(2 * np.pi * t / seasonal_period)\n    series[t] = ar1_coeff * series[t - 1] + noise[t] + ma1_coeff * noise[t - 1] + seasonal\n\n# Compute ACF\nn_lags = 35\nmean = np.mean(series)\nvar = np.sum((series - mean) ** 2)\nacf_values = np.array([np.sum((series[: n_obs - k] - mean) * (series[k:] - mean)) / var for k in range(n_lags + 1)])\n\n# Compute PACF via Durbin-Levinson recursion\npacf_values = np.zeros(n_lags + 1)\npacf_values[0] = 1.0\npacf_values[1] = acf_values[1]\nphi = np.zeros((n_lags + 1, n_lags + 1))\nphi[1, 1] = acf_values[1]\nfor k in range(2, n_lags + 1):\n    num = acf_values[k] - np.sum(phi[k - 1, 1:k] * acf_values[k - 1 : 0 : -1])\n    den = 1.0 - np.sum(phi[k - 1, 1:k] * acf_values[1:k])\n    phi[k, k] = num / den if den != 0 else 0\n    for j in range(1, k):\n        phi[k, j] = phi[k - 1, j] - phi[k, k] * phi[k - 1, k - j]\n    pacf_values[k] = phi[k, k]\n\nlags_acf = np.arange(0, n_lags + 1)\nlags_pacf = np.arange(1, n_lags + 1)\nconf_bound = 1.96 / np.sqrt(n_obs)\n\n# DataFrames with significance classification for seaborn hue encoding\nacf_df = pd.DataFrame(\n    {\n        \"Lag\": lags_acf,\n        \"Correlation\": acf_values,\n        \"Significance\": np.where((np.abs(acf_values) > conf_bound) | (lags_acf == 0), \"Significant\", \"Within CI\"),\n    }\n)\npacf_df = pd.DataFrame(\n    {\n        \"Lag\": lags_pacf,\n        \"Correlation\": pacf_values[1:],\n        \"Significance\": np.where(np.abs(pacf_values[1:]) > conf_bound, \"Significant\", \"Within CI\"),\n    }\n)\n\nsig_palette = {\"Significant\": BRAND, \"Within CI\": INK_MUTED}\n\n\ndef make_stem_df(df):\n    # Paired-row format required by sns.lineplot(units='Lag') to draw each stem as an\n    # individual vertical segment without cross-lag interpolation.\n    rows = []\n    for _, row in df.iterrows():\n        rows.append({\"Lag\": row[\"Lag\"], \"y\": 0.0, \"Significance\": row[\"Significance\"]})\n        rows.append({\"Lag\": row[\"Lag\"], \"y\": row[\"Correlation\"], \"Significance\": row[\"Significance\"]})\n    return pd.DataFrame(rows)\n\n\nacf_stem_df = make_stem_df(acf_df)\npacf_stem_df = make_stem_df(pacf_df)\n\n# Canvas: figsize=(8, 4.5) @ dpi=400 → exactly 3200×1800 px (landscape 16:9)\nfig, (ax_acf, ax_pacf) = plt.subplots(2, 1, figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG, sharex=True)\nax_acf.set_facecolor(PAGE_BG)\nax_pacf.set_facecolor(PAGE_BG)\n\n# ACF: sns.lineplot with units='Lag' draws each significance-colored stem as a\n# discrete vertical segment — idiomatic seaborn statistical unit rendering\nsns.lineplot(\n    data=acf_stem_df,\n    x=\"Lag\",\n    y=\"y\",\n    hue=\"Significance\",\n    palette=sig_palette,\n    units=\"Lag\",\n    estimator=None,\n    linewidth=1.8,\n    ax=ax_acf,\n    legend=False,\n)\nsns.scatterplot(\n    data=acf_df,\n    x=\"Lag\",\n    y=\"Correlation\",\n    hue=\"Significance\",\n    hue_order=[\"Significant\", \"Within CI\"],\n    palette=sig_palette,\n    s=55,\n    zorder=5,\n    edgecolor=PAGE_BG,\n    linewidth=0.5,\n    ax=ax_acf,\n    legend=True,\n)\n\n# PACF: same seaborn approach from lag 1\nsns.lineplot(\n    data=pacf_stem_df,\n    x=\"Lag\",\n    y=\"y\",\n    hue=\"Significance\",\n    palette=sig_palette,\n    units=\"Lag\",\n    estimator=None,\n    linewidth=1.8,\n    ax=ax_pacf,\n    legend=False,\n)\nsns.scatterplot(\n    data=pacf_df,\n    x=\"Lag\",\n    y=\"Correlation\",\n    hue=\"Significance\",\n    palette=sig_palette,\n    s=55,\n    zorder=5,\n    edgecolor=PAGE_BG,\n    linewidth=0.5,\n    ax=ax_pacf,\n    legend=False,\n)\n\n# CI bounds, baseline, and grid for both panels\nfor ax in (ax_acf, ax_pacf):\n    ax.axhline(y=0, color=INK_SOFT, linewidth=0.8)\n    ax.axhline(y=conf_bound, color=ANYPLOT_AMBER, linestyle=\"--\", linewidth=1.5, alpha=0.9)\n    ax.axhline(y=-conf_bound, color=ANYPLOT_AMBER, linestyle=\"--\", linewidth=1.5, alpha=0.9)\n    ax.fill_between([-0.5, n_lags + 0.5], -conf_bound, conf_bound, color=ANYPLOT_AMBER, alpha=0.07)\n    ax.set_xlim(-0.5, n_lags + 0.5)\n    ax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK)\n    ax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\n\n# Spine styling\nsns.despine(fig=fig)\nfor ax in (ax_acf, ax_pacf):\n    ax.spines[\"left\"].set_color(INK_SOFT)\n    ax.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Axis labels\nax_acf.set_ylabel(\"ACF\", fontsize=10, color=INK)\nax_pacf.set_ylabel(\"PACF\", fontsize=10, color=INK)\nax_pacf.set_xlabel(\"Lag\", fontsize=10, color=INK)\n\n# X-ticks every 5 lags (shared axis — set once on either panel)\nax_pacf.set_xticks(np.arange(0, n_lags + 1, 5))\n\n# Legend in ACF panel: seaborn auto-generates Significant/Within CI handles from\n# scatterplot hue; extend with the amber CI dashed-line handle\nhandles, labels = ax_acf.get_legend_handles_labels()\nci_handle = plt.Line2D([0], [0], linestyle=\"--\", color=ANYPLOT_AMBER, linewidth=1.5)\nhandles.append(ci_handle)\nlabels.append(\"95% CI\")\nax_acf.legend(handles=handles, labels=labels, loc=\"upper right\", fontsize=8, facecolor=ELEVATED_BG, edgecolor=INK_SOFT)\n\n# Data storytelling annotations — AR(1) signature visible in both panels\nax_acf.annotate(\n    \"Gradual decay → AR process\",\n    xy=(4, acf_values[4]),\n    xytext=(13, 0.58),\n    fontsize=7,\n    color=INK_MUTED,\n    arrowprops={\"arrowstyle\": \"->\", \"color\": INK_MUTED, \"lw\": 0.7},\n)\nax_pacf.annotate(\n    \"Spike at lag 1 → AR(1) order\",\n    xy=(1, pacf_values[1]),\n    xytext=(7, 0.63),\n    fontsize=7,\n    color=INK_MUTED,\n    arrowprops={\"arrowstyle\": \"->\", \"color\": INK_MUTED, \"lw\": 0.7},\n)\n\n# Title — \"acf-pacf · python · seaborn · anyplot.ai\" is 40 chars (< 67 baseline → fontsize=12)\ntitle = \"acf-pacf · python · seaborn · anyplot.ai\"\nfig.suptitle(title, fontsize=12, fontweight=\"medium\", color=INK, y=0.99)\nfig.subplots_adjust(top=0.92, bottom=0.13, hspace=0.3)\n\n# Save — no bbox_inches to preserve exact 3200×1800 canvas\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}