{"spec_id":"confusion-matrix","library":"echarts","language":"javascript","code":"// anyplot.ai\n// confusion-matrix: Confusion Matrix Heatmap\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-04\n\n//# anyplot-orientation: square\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// A pet-photo classifier's confusion matrix on a 500-image held-out test set.\n// Rows = true label, columns = predicted label.\nconst classNames = [\"Cat\", \"Dog\", \"Bird\", \"Rabbit\", \"Fish\"];\nconst matrix = [\n  [104, 8, 4, 3, 1], // Cat\n  [10, 112, 3, 4, 1], // Dog\n  [5, 4, 76, 3, 2], // Bird\n  [4, 5, 2, 57, 2], // Rabbit\n  [2, 1, 3, 2, 82], // Fish\n];\n\nconst heatmapData = [];\nmatrix.forEach((row, rowIdx) => {\n  const rowTotal = row.reduce((sum, v) => sum + v, 0);\n  row.forEach((value, colIdx) => {\n    const pct = (value / rowTotal) * 100;\n    heatmapData.push({\n      value: [colIdx, rowIdx, value],\n      label: { formatter: `${value}\\n${pct.toFixed(0)}%` },\n      itemStyle:\n        colIdx === rowIdx\n          ? { borderColor: t.ink, borderWidth: 3 }\n          : { borderColor: t.pageBg, borderWidth: 2 },\n    });\n  });\n});\n\n// --- Title (length-scaled fontsize) -----------------------------------------\nconst title = \"Pet Photo Classifier · confusion-matrix · javascript · echarts · anyplot.ai\";\nconst titleFontSize = Math.max(15, Math.round(22 * Math.min(1, 67 / title.length)));\n\n// --- Init ---------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option -------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: title,\n    left: \"center\",\n    top: 20,\n    textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 500 },\n  },\n  grid: { left: 140, right: 60, top: 130, bottom: 190 },\n  xAxis: {\n    type: \"category\",\n    data: classNames,\n    name: \"Predicted Label\",\n    nameLocation: \"middle\",\n    nameGap: 40,\n    nameTextStyle: { color: t.ink, fontSize: 18 },\n    position: \"top\",\n    axisLabel: { color: t.inkSoft, fontSize: 16 },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitArea: { show: false },\n  },\n  yAxis: {\n    type: \"category\",\n    data: classNames,\n    inverse: true,\n    name: \"True Label\",\n    nameLocation: \"middle\",\n    nameGap: 90,\n    nameTextStyle: { color: t.ink, fontSize: 18 },\n    axisLabel: { color: t.inkSoft, fontSize: 16 },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitArea: { show: false },\n  },\n  visualMap: {\n    min: 0,\n    max: 112,\n    calculable: true,\n    orient: \"horizontal\",\n    left: \"center\",\n    bottom: 20,\n    inRange: { color: t.seq },\n    textStyle: { color: t.inkSoft, fontSize: 14 },\n  },\n  series: [\n    {\n      type: \"heatmap\",\n      data: heatmapData,\n      label: {\n        show: true,\n        color: \"#FFFDF6\",\n        textBorderColor: \"#1A1A17\",\n        textBorderWidth: 3,\n        fontSize: 15,\n        fontWeight: 600,\n        lineHeight: 20,\n      },\n      emphasis: {\n        itemStyle: { shadowBlur: 0 },\n      },\n    },\n  ],\n});\n"}