{"spec_id":"line-pca-variance-cumulative","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nline-pca-variance-cumulative: Cumulative Explained Variance for PCA Component Selection\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-29\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove the script's own directory from sys.path so this file (matplotlib.py)\n# doesn't shadow the installed matplotlib package.\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if p and os.path.normcase(os.path.abspath(p)) != os.path.normcase(_this_dir)]\n\nimport matplotlib.patheffects as pe\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as mticker\nimport numpy as np\nfrom sklearn.datasets import load_wine\nfrom sklearn.decomposition import PCA\nfrom sklearn.preprocessing import StandardScaler\n\n\n# Theme\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\n# Imprint palette — 8 hues, hybrid-v3 sort order\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\nBRAND = IMPRINT_PALETTE[0]  # cumulative variance line\nLAVENDER = IMPRINT_PALETTE[1]  # 90% threshold\nBLUE = IMPRINT_PALETTE[2]  # 95% threshold\n\n# Data — scikit-learn wine dataset (13 features → 13 PCA components)\nwine = load_wine()\nX = StandardScaler().fit_transform(wine.data)\npca = PCA().fit(X)\n\nvariance = pca.explained_variance_ratio_\ncumulative = np.cumsum(variance)\ncomponents = np.arange(1, len(cumulative) + 1)\n\n# Elbow detection via maximum curvature in second discrete difference\ndiffs = np.diff(cumulative)\ndiffs2 = np.diff(diffs)\nelbow_idx = int(np.argmin(diffs2)) + 1  # shifted by one from double-diff\nelbow_component = elbow_idx + 1  # 1-indexed\n\n# Threshold crossings\nthresholds = [(0.90, \"90%\", LAVENDER), (0.95, \"95%\", BLUE)]\nn_at_threshold = [int(np.argmax(cumulative >= t)) + 1 for t, _, _ in thresholds]\n\n# Canvas — hard rule: 3200 × 1800 px (landscape 16:9)\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Individual variance bars (muted overlay, same brand green at low alpha)\nax.bar(\n    components,\n    variance,\n    color=BRAND,\n    alpha=0.18,\n    width=0.6,\n    edgecolor=BRAND,\n    linewidth=0.4,\n    label=\"Individual variance\",\n    zorder=2,\n)\n\n# Shaded area under cumulative curve\nax.fill_between(components, cumulative, alpha=0.07, color=BRAND, zorder=3)\n\n# Cumulative variance line with open-circle markers\nax.plot(\n    components,\n    cumulative,\n    color=BRAND,\n    linewidth=2.5,\n    marker=\"o\",\n    markersize=5,\n    markerfacecolor=PAGE_BG,\n    markeredgecolor=BRAND,\n    markeredgewidth=1.8,\n    label=\"Cumulative variance\",\n    zorder=5,\n    path_effects=[pe.Stroke(linewidth=4.5, foreground=PAGE_BG, alpha=0.6), pe.Normal()],\n)\n\n# Threshold horizontal lines + crossing annotations\nfor i, ((t, label, color), n_comp) in enumerate(zip(thresholds, n_at_threshold, strict=True)):\n    ax.axhline(y=t, color=color, linestyle=\"--\", linewidth=1.2, alpha=0.75, label=f\"{label} threshold\", zorder=4)\n    ax.plot(n_comp, t, marker=\"D\", color=color, markersize=7, zorder=6, markeredgecolor=PAGE_BG, markeredgewidth=0.9)\n    # Annotation: place to the left of the crossing point\n    text_x = max(n_comp - 3.5, 1.5)\n    text_y = t + (0.045 if i == 1 else -0.07)\n    ax.annotate(\n        f\"{n_comp} components → {label}\",\n        xy=(n_comp, t),\n        xytext=(text_x, text_y),\n        fontsize=7,\n        color=color,\n        arrowprops={\"arrowstyle\": \"-|>\", \"color\": color, \"lw\": 0.9, \"connectionstyle\": \"arc3,rad=0.15\"},\n        bbox={\"boxstyle\": \"round,pad=0.28\", \"facecolor\": ELEVATED_BG, \"edgecolor\": color, \"alpha\": 0.93},\n        zorder=7,\n    )\n\n# Elbow marker — neutral semantic anchor (reference / derived feature)\nax.plot(\n    elbow_component,\n    cumulative[elbow_idx],\n    marker=\"*\",\n    color=INK_SOFT,\n    markersize=11,\n    zorder=6,\n    markeredgecolor=PAGE_BG,\n    markeredgewidth=0.7,\n    label=\"Elbow point\",\n)\nax.annotate(\n    f\"Elbow: PC{elbow_component} ({cumulative[elbow_idx]:.0%})\",\n    xy=(elbow_component, cumulative[elbow_idx]),\n    xytext=(elbow_component + 1.8, cumulative[elbow_idx] - 0.10),\n    fontsize=7,\n    color=INK_SOFT,\n    arrowprops={\"arrowstyle\": \"-|>\", \"color\": INK_SOFT, \"lw\": 0.9},\n    bbox={\"boxstyle\": \"round,pad=0.28\", \"facecolor\": ELEVATED_BG, \"edgecolor\": INK_SOFT, \"alpha\": 0.9},\n    zorder=7,\n)\n\n# Title — scale fontsize to avoid overflow (formula from library prompt)\ntitle = \"line-pca-variance-cumulative · python · matplotlib · anyplot.ai\"\ntitle_fontsize = max(8, round(12 * 67 / len(title))) if len(title) > 67 else 12\nax.set_title(title, fontsize=title_fontsize, fontweight=\"medium\", pad=8, color=INK)\n\n# Axis labels\nax.set_xlabel(\"Number of Components\", fontsize=10, labelpad=5, color=INK)\nax.set_ylabel(\"Explained Variance\", fontsize=10, labelpad=5, color=INK)\n\n# Ticks\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\nax.set_xticks(components)\nax.set_xlim(0.3, len(components) + 0.7)\nax.set_ylim(0, 1.08)\n\n# Percentage formatter on y-axis\nax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda v, _: f\"{v:.0%}\"))\n\n# Spines — L-shaped frame\nfor spine in (\"top\", \"right\"):\n    ax.spines[spine].set_visible(False)\nfor spine in (\"left\", \"bottom\"):\n    ax.spines[spine].set_linewidth(0.5)\n    ax.spines[spine].set_color(INK_SOFT)\n\n# Grid — y-axis only, subtle\nax.yaxis.grid(True, which=\"major\", alpha=0.15, linewidth=0.6, color=INK)\nax.set_axisbelow(True)\n\n# Legend\nhandles, labels_list = ax.get_legend_handles_labels()\nif len(handles) > 1:\n    leg = ax.legend(fontsize=8, loc=\"lower right\", framealpha=0.95, fancybox=True, borderpad=0.7, handlelength=2.0)\n    if leg:\n        leg.get_frame().set_facecolor(ELEVATED_BG)\n        leg.get_frame().set_edgecolor(INK_SOFT)\n        leg.get_frame().set_linewidth(0.5)\n        plt.setp(leg.get_texts(), color=INK_SOFT)\n\nplt.tight_layout(pad=1.0)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}