{"spec_id":"elbow-curve","library":"muix","language":"javascript","code":"// anyplot.ai\n// elbow-curve: Elbow Curve for K-Means Clustering\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 94/100 | Created: 2026-09-05\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\n\n// --- Data (in-memory, deterministic): K-means inertia for customer segmentation ---\nconst kValues = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\nconst inertia = [1250, 690, 410, 260, 195, 165, 145, 130, 118, 108];\nconst optimalK = 4;\nconst optimalKIndex = kValues.indexOf(optimalK);\n// A second series holding a single non-null point (the rest are `null`, with\n// connectNulls off) so only the elbow marker renders — recolored via `sx`\n// below to emphasize the elbow point beyond the reference line alone.\nconst elbowMarkerData = kValues.map((_, i) =>\n  i === optimalKIndex ? inertia[optimalKIndex] : null,\n);\n\nconst TITLE = \"elbow-curve · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 56;\nconst Y_LABEL_WIDTH = 40;\n\n// MUI X's built-in yAxis `label` offsets from a fixed (tickFontSize + tickSize\n// + 10) constant rather than the tick labels' measured width, so it collides\n// with wide tick numbers (e.g. \"1,300\") at this fontSize — render the axis\n// title as its own rotated element instead, with guaranteed dedicated space.\nconst yAxisLabelStyle = {\n  writingMode: \"vertical-rl\" as const,\n  transform: \"rotate(180deg)\",\n  color: t.ink,\n  fontSize: 16,\n  width: Y_LABEL_WIDTH,\n  display: \"flex\",\n  alignItems: \"center\",\n  justifyContent: \"center\",\n};\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  return (\n    <div\n      style={{\n        width,\n        height,\n        display: \"flex\",\n        flexDirection: \"column\",\n        backgroundColor: t.pageBg,\n      }}\n    >\n      <div\n        style={{\n          height: TITLE_HEIGHT,\n          display: \"flex\",\n          alignItems: \"center\",\n          paddingLeft: 32,\n        }}\n      >\n        <span style={{ color: t.ink, fontSize: 22, fontWeight: 500 }}>\n          {TITLE}\n        </span>\n      </div>\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"row\",\n          height: height - TITLE_HEIGHT,\n        }}\n      >\n        <span style={yAxisLabelStyle}>Inertia (WCSS)</span>\n        <LineChart\n          width={width - Y_LABEL_WIDTH}\n          height={height - TITLE_HEIGHT}\n          skipAnimation\n          margin={{ top: 24, right: 48, bottom: 64, left: 80 }}\n          xAxis={[\n            {\n              data: kValues,\n              scaleType: \"linear\",\n              label: \"Number of Clusters (k)\",\n              tickMinStep: 1,\n              labelStyle: { fontSize: 16, fill: t.ink },\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n            },\n          ]}\n          yAxis={[\n            {\n              tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n            },\n          ]}\n          series={[\n            {\n              id: \"inertia\",\n              data: inertia,\n              label: \"Within-cluster sum of squares\",\n              color: t.palette[0],\n              showMark: true,\n              curve: \"monotoneX\",\n              area: true,\n            },\n            {\n              id: \"elbowMarker\",\n              data: elbowMarkerData,\n              color: t.amber,\n              showMark: true,\n              connectNulls: false,\n              disableHighlight: true,\n            },\n          ]}\n          grid={{ horizontal: true }}\n          slotProps={{ legend: { hidden: true } }}\n          sx={{\n            // Subtle brand-green fill under the curve — a deliberate design\n            // touch beyond a bare line, kept low-opacity so it stays a wash\n            // rather than competing with the line/markers.\n            \"& .MuiAreaElement-series-inertia\": {\n              fill: t.palette[0],\n              opacity: 0.12,\n            },\n            // Solid amber-filled marker at k=4 (vs. the hollow green marks\n            // elsewhere) so the elbow point reads as emphasized, not just\n            // co-located with the dashed reference line.\n            \"& .MuiMarkElement-series-elbowMarker\": {\n              fill: t.amber,\n              stroke: t.pageBg,\n              strokeWidth: 2.5,\n            },\n          }}\n        >\n          <ChartsReferenceLine\n            x={optimalK}\n            label={`Elbow · k=${optimalK}`}\n            labelAlign=\"middle\"\n            lineStyle={{\n              stroke: t.amber,\n              strokeDasharray: \"6 6\",\n              strokeWidth: 2,\n            }}\n            labelStyle={{\n              fill: t.inkSoft,\n              fontSize: 14,\n              fontWeight: 600,\n              // Text-stroke halo acts as a background chip so the label\n              // stays crisp where it crosses gridlines/the dashed line.\n              paintOrder: \"stroke\",\n              stroke: t.pageBg,\n              strokeWidth: 6,\n              strokeLinejoin: \"round\",\n            }}\n          />\n        </LineChart>\n      </div>\n    </div>\n  );\n}\n"}