{"spec_id":"line-pca-variance-cumulative","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nline-pca-variance-cumulative: Cumulative Explained Variance for PCA Component Selection\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-29\n\"\"\"\n\nimport sys\n\n\nsys.path = sys.path[1:]  # prevent this file from shadowing the installed plotly package\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\nfrom sklearn.datasets import load_wine\nfrom sklearn.decomposition import PCA\nfrom sklearn.preprocessing import StandardScaler\n\n\n# Theme tokens — Imprint palette chrome\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\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint categorical palette — data colors are theme-independent\nBRAND = \"#009E73\"  # position 1 — cumulative variance line (always first series)\nBAR_COLOR = \"#4467A3\"  # position 3 — individual variance bars\nELBOW_COLOR = \"#C475FD\"  # position 2 — elbow marker accent\nAMBER = \"#DDCC77\"  # semantic anchor — 90% warning threshold\nRED = \"#AE3030\"  # position 5 — 95% critical threshold\n\n# Data — PCA on Wine dataset\nwine = load_wine()\nX_scaled = StandardScaler().fit_transform(wine.data)\npca = PCA().fit(X_scaled)\n\nexplained_variance = pca.explained_variance_ratio_\ncumulative_variance = np.cumsum(explained_variance) * 100\nn_components = np.arange(1, len(explained_variance) + 1)\n\n# Threshold crossings\nthreshold_90_idx = int(np.argmax(cumulative_variance >= 90))\nthreshold_95_idx = int(np.argmax(cumulative_variance >= 95))\nthreshold_90_comp = threshold_90_idx + 1\nthreshold_95_comp = threshold_95_idx + 1\n\n# Elbow: first component where marginal gain drops below mean gain\nindividual_pct = explained_variance * 100\nelbow_idx = int(np.argmax(individual_pct < individual_pct.mean()))\nelbow_comp = elbow_idx + 1\nelbow_var = float(cumulative_variance[elbow_idx])\n\ntitle = \"line-pca-variance-cumulative · python · plotly · anyplot.ai\"\n\n# Plot\nfig = go.Figure()\n\n# Individual variance bars (secondary context)\nbar_fill = \"rgba(68,103,163,0.30)\"\nbar_edge = \"rgba(68,103,163,0.55)\"\nfig.add_trace(\n    go.Bar(\n        x=n_components,\n        y=explained_variance * 100,\n        marker={\"color\": bar_fill, \"line\": {\"width\": 1, \"color\": bar_edge}},\n        name=\"Individual variance\",\n        hovertemplate=\"PC%{x}<br>Individual: %{y:.1f}%<extra></extra>\",\n    )\n)\n\n# Cumulative variance line — primary series (Imprint position 1)\nfig.add_trace(\n    go.Scatter(\n        x=n_components,\n        y=cumulative_variance,\n        mode=\"lines+markers\",\n        line={\"color\": BRAND, \"width\": 4, \"shape\": \"spline\"},\n        marker={\"size\": 12, \"color\": BRAND, \"line\": {\"width\": 2, \"color\": PAGE_BG}},\n        name=\"Cumulative variance\",\n        hovertemplate=\"PC%{x}<br>Cumulative: %{y:.1f}%<extra></extra>\",\n    )\n)\n\n# Dummy traces so threshold lines appear in the legend\nfig.add_trace(\n    go.Scatter(\n        x=[None], y=[None], mode=\"lines\", line={\"color\": AMBER, \"width\": 2.5, \"dash\": \"dash\"}, name=\"90% threshold\"\n    )\n)\nfig.add_trace(\n    go.Scatter(\n        x=[None], y=[None], mode=\"lines\", line={\"color\": RED, \"width\": 2.5, \"dash\": \"dash\"}, name=\"95% threshold\"\n    )\n)\n\n# Horizontal threshold lines — placed on opposite sides to avoid label crowding\nfig.add_hline(\n    y=90,\n    line_dash=\"dash\",\n    line_color=AMBER,\n    line_width=2.5,\n    annotation_text=\"90%\",\n    annotation_position=\"top right\",\n    annotation_font={\"size\": 14, \"color\": AMBER},\n)\nfig.add_hline(\n    y=95,\n    line_dash=\"dash\",\n    line_color=RED,\n    line_width=2.5,\n    annotation_text=\"95%\",\n    annotation_position=\"top left\",\n    annotation_font={\"size\": 14, \"color\": RED},\n)\n\n# Vertical drop lines from threshold crossings to x-axis\nfig.add_shape(\n    type=\"line\",\n    x0=threshold_90_comp,\n    x1=threshold_90_comp,\n    y0=0,\n    y1=90,\n    line={\"color\": AMBER, \"width\": 1.5, \"dash\": \"dot\"},\n)\nfig.add_shape(\n    type=\"line\",\n    x0=threshold_95_comp,\n    x1=threshold_95_comp,\n    y0=0,\n    y1=95,\n    line={\"color\": RED, \"width\": 1.5, \"dash\": \"dot\"},\n)\n\n# Elbow marker — diamond outline for visual distinction\nfig.add_trace(\n    go.Scatter(\n        x=[elbow_comp],\n        y=[elbow_var],\n        mode=\"markers\",\n        marker={\"size\": 22, \"color\": ELEVATED_BG, \"line\": {\"width\": 3, \"color\": ELBOW_COLOR}, \"symbol\": \"diamond\"},\n        showlegend=False,\n        hovertemplate=f\"Elbow: PC{elbow_comp}<br>Cumulative: {elbow_var:.1f}%<extra></extra>\",\n    )\n)\n\n# Elbow annotation with arrow\nfig.add_annotation(\n    x=elbow_comp,\n    y=elbow_var,\n    text=f\"<b>Elbow</b> — PC{elbow_comp}<br>{elbow_var:.0f}% variance\",\n    showarrow=True,\n    arrowhead=2,\n    arrowsize=1.4,\n    arrowwidth=1.8,\n    arrowcolor=ELBOW_COLOR,\n    ax=-65,\n    ay=-55,\n    font={\"size\": 12, \"color\": ELBOW_COLOR},\n    bgcolor=ELEVATED_BG,\n    bordercolor=ELBOW_COLOR,\n    borderwidth=1.5,\n    borderpad=5,\n)\n\n# Layout\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    title={\"text\": title, \"font\": {\"size\": 16, \"color\": INK}, \"x\": 0.5, \"xanchor\": \"center\"},\n    xaxis={\n        \"title\": {\"text\": \"Principal Component\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"tickmode\": \"linear\",\n        \"tick0\": 1,\n        \"dtick\": 1,\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"showline\": True,\n        \"linewidth\": 1,\n        \"linecolor\": INK_SOFT,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Explained Variance (%)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"range\": [0, 105],\n        \"showgrid\": True,\n        \"gridwidth\": 1,\n        \"gridcolor\": GRID,\n        \"zeroline\": False,\n        \"showline\": True,\n        \"linewidth\": 1,\n        \"linecolor\": INK_SOFT,\n    },\n    legend={\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"x\": 0.02,\n        \"y\": 0.40,\n    },\n    font={\"color\": INK},\n    margin={\"t\": 80, \"b\": 60, \"l\": 80, \"r\": 40},\n    bargap=0.4,\n)\n\n# Save\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}