{"spec_id":"line-pca-variance-cumulative","library":"echarts","language":"javascript","code":"// anyplot.ai\n// line-pca-variance-cumulative: Cumulative Explained Variance for PCA Component Selection\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-08-26\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Synthetic PCA spectrum for a 15-sensor industrial monitoring array: a\n// geometrically decaying eigenvalue sequence, normalized so the individual\n// ratios sum to 1 (mirrors sklearn's PCA.explained_variance_ratio_).\nconst N_COMPONENTS = 15;\nconst DECAY = 0.78;\nconst rawEigenvalues = Array.from({ length: N_COMPONENTS }, (_, i) => 45 * Math.pow(DECAY, i));\nconst eigenSum = rawEigenvalues.reduce((a, b) => a + b, 0);\nconst individualPct = rawEigenvalues.map((v) => (v / eigenSum) * 100);\nconst cumulativePct = individualPct.reduce((acc, v, i) => {\n  acc.push((i === 0 ? 0 : acc[i - 1]) + v);\n  return acc;\n}, []);\nconst components = Array.from({ length: N_COMPONENTS }, (_, i) => String(i + 1));\n\n// Elbow point: index of maximum perpendicular distance from the chord joining\n// the first and last cumulative points (kneedle heuristic for a concave curve).\nfunction findElbowIndex(values) {\n  const n = values.length;\n  const x1 = 1, y1 = values[0];\n  const x2 = n, y2 = values[n - 1];\n  const denom = Math.sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2);\n  let bestIdx = 0, bestDist = -1;\n  values.forEach((y0, i) => {\n    const x0 = i + 1;\n    const dist = Math.abs((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1) / denom;\n    if (dist > bestDist) { bestDist = dist; bestIdx = i; }\n  });\n  return bestIdx;\n}\nconst elbowIdx = findElbowIndex(cumulativePct);\n\n// --- Init --------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option -------------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  color: [t.palette[0]],\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"line-pca-variance-cumulative · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: \"bold\" },\n  },\n  legend: {\n    data: [\"Cumulative variance\", \"Individual variance\"],\n    top: 74,\n    left: \"center\",\n    textStyle: { color: t.inkSoft, fontSize: 15 },\n    itemWidth: 22,\n    itemHeight: 14,\n  },\n  grid: { left: 100, right: 70, top: 140, bottom: 90 },\n  xAxis: {\n    type: \"category\",\n    data: components,\n    name: \"Number of Components\",\n    nameLocation: \"middle\",\n    nameGap: 42,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Explained Variance\",\n    nameLocation: \"middle\",\n    nameGap: 64,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    min: 0,\n    max: 100,\n    axisLabel: { color: t.inkSoft, fontSize: 14, formatter: \"{value}%\" },\n    axisLine: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      name: \"Cumulative variance\",\n      type: \"line\",\n      data: cumulativePct,\n      symbol: \"circle\",\n      symbolSize: 11,\n      lineStyle: { width: 3.5, color: t.palette[0] },\n      itemStyle: { color: t.palette[0] },\n      areaStyle: { color: t.palette[0], opacity: 0.06 },\n      z: 3,\n      markLine: {\n        symbol: \"none\",\n        silent: true,\n        lineStyle: { type: \"dashed\", color: t.inkSoft, width: 1.5 },\n        label: { color: t.inkSoft, fontSize: 15, position: \"insideEndTop\", formatter: \"{b}\" },\n        data: [\n          { yAxis: 90, name: \"90% threshold\" },\n          { yAxis: 95, name: \"95% threshold\" },\n        ],\n      },\n      markPoint: {\n        symbol: \"circle\",\n        symbolSize: 16,\n        symbolOffset: [16, -16],\n        itemStyle: { color: t.pageBg, borderColor: t.ink, borderWidth: 3 },\n        label: {\n          show: true,\n          position: \"top\",\n          distance: 14,\n          color: t.ink,\n          fontSize: 15,\n          fontWeight: \"bold\",\n          formatter: `Elbow (n=${elbowIdx + 1})`,\n        },\n        data: [{ coord: [elbowIdx, cumulativePct[elbowIdx]] }],\n      },\n    },\n    {\n      name: \"Individual variance\",\n      type: \"bar\",\n      data: individualPct,\n      barWidth: \"55%\",\n      itemStyle: { color: t.inkSoft, opacity: 0.35 },\n      z: 1,\n    },\n  ],\n});\n"}