{"spec_id":"elbow-curve","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nelbow-curve: Elbow Curve for K-Means Clustering\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\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\"  # Okabe-Ito position 1\n\n# Data - Simulate K-means inertia values with realistic decay\nnp.random.seed(42)\nk_values = list(range(1, 12))\n\n# Realistic inertia: sharp drop initially, then diminishing returns\nbase_inertia = 5000\ninertia = []\nfor k in k_values:\n    decay = base_inertia * np.exp(-0.35 * (k - 1)) + 200\n    noise = np.random.uniform(-50, 50)\n    inertia.append(max(decay + noise, 150))\n\n# Mark optimal k (elbow point at k=4)\noptimal_k = 4\n\ndf = pd.DataFrame({\"Number of Clusters (k)\": k_values, \"Inertia\": inertia})\n\n# Create base line chart\nline = (\n    alt.Chart(df)\n    .mark_line(color=BRAND, strokeWidth=4)\n    .encode(\n        x=alt.X(\n            \"Number of Clusters (k):Q\",\n            scale=alt.Scale(domain=[0.5, 11.5]),\n            axis=alt.Axis(tickCount=11, values=k_values),\n        ),\n        y=alt.Y(\"Inertia:Q\", scale=alt.Scale(domain=[0, max(inertia) * 1.1])),\n    )\n)\n\n# Add points at each k value\npoints = (\n    alt.Chart(df)\n    .mark_point(size=300, color=BRAND, filled=True)\n    .encode(x=\"Number of Clusters (k):Q\", y=\"Inertia:Q\", tooltip=[\"Number of Clusters (k)\", \"Inertia\"])\n)\n\n# Highlight the elbow point (optimal k)\nelbow_df = df[df[\"Number of Clusters (k)\"] == optimal_k]\nelbow_point = (\n    alt.Chart(elbow_df)\n    .mark_point(size=600, color=INK, filled=True, stroke=BRAND, strokeWidth=3)\n    .encode(x=\"Number of Clusters (k):Q\", y=\"Inertia:Q\")\n)\n\n# Add annotation for elbow point\nelbow_text = (\n    alt.Chart(elbow_df)\n    .mark_text(align=\"left\", baseline=\"bottom\", dx=15, dy=-15, fontSize=20, fontWeight=\"bold\", color=INK)\n    .encode(x=\"Number of Clusters (k):Q\", y=\"Inertia:Q\", text=alt.value(f\"Optimal k = {optimal_k}\"))\n)\n\n# Combine layers\nchart = (\n    (line + points + elbow_point + elbow_text)\n    .properties(\n        width=1600,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(\"elbow-curve · altair · anyplot.ai\", fontSize=28, anchor=\"middle\", color=INK),\n    )\n    .configure_axis(\n        labelFontSize=18,\n        titleFontSize=22,\n        gridColor=INK,\n        gridOpacity=0.1,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n    .configure_title(color=INK)\n)\n\n# Save outputs\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}