{"spec_id":"bar-permutation-importance","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// bar-permutation-importance: Permutation Feature Importance Plot\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-08-26\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// sklearn.inspection.permutation_importance-style output (n_repeats=10) for a\n// flight-delay classifier: mean decrease in ROC-AUC when each feature is\n// shuffled, sorted descending so the most important feature sits on top.\nconst rows = [\n  { feature: \"Departure delay (min)\", mean: 0.182, std: 0.014 },\n  { feature: \"Distance (miles)\", mean: 0.146, std: 0.011 },\n  { feature: \"Scheduled hour\", mean: 0.098, std: 0.009 },\n  { feature: \"Day of week\", mean: 0.071, std: 0.008 },\n  { feature: \"Carrier\", mean: 0.058, std: 0.007 },\n  { feature: \"Origin airport\", mean: 0.045, std: 0.006 },\n  { feature: \"Destination airport\", mean: 0.038, std: 0.006 },\n  { feature: \"Weather severity\", mean: 0.031, std: 0.005 },\n  { feature: \"Aircraft age (yrs)\", mean: 0.019, std: 0.004 },\n  { feature: \"Season\", mean: 0.014, std: 0.004 },\n  { feature: \"Number of stops\", mean: 0.009, std: 0.003 },\n  { feature: \"Passenger count\", mean: -0.002, std: 0.003 },\n  { feature: \"Ticket price\", mean: -0.006, std: 0.004 },\n];\n\nconst labels = rows.map((r) => r.feature);\nconst means = rows.map((r) => r.mean);\nconst stds = rows.map((r) => r.std);\n\n// --- Color: sequential gradient mapped to importance (continuous, not categorical)\nfunction hexToRgb(hex) {\n  const n = parseInt(hex.slice(1), 16);\n  return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\nfunction lerpColor(hexA, hexB, frac) {\n  const [r1, g1, b1] = hexToRgb(hexA);\n  const [r2, g2, b2] = hexToRgb(hexB);\n  const r = Math.round(r1 + (r2 - r1) * frac);\n  const g = Math.round(g1 + (g2 - g1) * frac);\n  const b = Math.round(b1 + (b2 - b1) * frac);\n  return `rgb(${r}, ${g}, ${b})`;\n}\n\nconst [seqLo, seqHi] = t.seq; // imprint_seq: [\"#009E73\", \"#4467A3\"]\nconst maxMean = Math.max(...means);\nconst minMean = Math.min(...means);\n// Highest-importance features get brand green (visual emphasis); least\n// important fade toward blue.\nconst barColors = means.map((m) => lerpColor(seqLo, seqHi, 1 - (m - minMean) / (maxMean - minMean)));\n\n// --- Value-axis range: must fit the mean ± std whiskers, not just the bars\nconst lo = Math.min(...rows.map((r) => r.mean - r.std));\nconst hi = Math.max(...rows.map((r) => r.mean + r.std));\nconst pad = (hi - lo) * 0.12;\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Custom plugins (core Chart.js Plugin API — no external packages) -------\nconst zeroLinePlugin = {\n  id: \"zeroLine\",\n  afterDatasetsDraw(chart) {\n    const {\n      ctx,\n      scales: { x },\n      chartArea: { top, bottom },\n    } = chart;\n    const xZero = x.getPixelForValue(0);\n    ctx.save();\n    ctx.strokeStyle = t.inkSoft;\n    ctx.lineWidth = 1.5;\n    ctx.setLineDash([6, 4]);\n    ctx.beginPath();\n    ctx.moveTo(xZero, top);\n    ctx.lineTo(xZero, bottom);\n    ctx.stroke();\n    ctx.restore();\n  },\n};\n\nconst errorBarsPlugin = {\n  id: \"errorBars\",\n  afterDatasetsDraw(chart) {\n    const {\n      ctx,\n      scales: { x },\n    } = chart;\n    const meta = chart.getDatasetMeta(0);\n    ctx.save();\n    ctx.strokeStyle = t.ink;\n    ctx.lineWidth = 2;\n    const capHalf = 7;\n    meta.data.forEach((bar, i) => {\n      const y = bar.y;\n      const xLo = x.getPixelForValue(means[i] - stds[i]);\n      const xHi = x.getPixelForValue(means[i] + stds[i]);\n      ctx.beginPath();\n      ctx.moveTo(xLo, y);\n      ctx.lineTo(xHi, y);\n      ctx.moveTo(xLo, y - capHalf);\n      ctx.lineTo(xLo, y + capHalf);\n      ctx.moveTo(xHi, y - capHalf);\n      ctx.lineTo(xHi, y + capHalf);\n      ctx.stroke();\n    });\n    ctx.restore();\n  },\n};\n\n// --- Chart ---------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"bar\",\n  data: {\n    labels,\n    datasets: [\n      {\n        data: means,\n        backgroundColor: barColors,\n        borderWidth: 0,\n        barPercentage: 0.85,\n        categoryPercentage: 0.8,\n      },\n    ],\n  },\n  options: {\n    indexAxis: \"y\",\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: { right: 24 } },\n    plugins: {\n      title: {\n        display: true,\n        text: \"bar-permutation-importance · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22 },\n      },\n      legend: { display: false },\n    },\n    scales: {\n      x: {\n        min: lo - pad,\n        max: hi + pad,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        title: { display: true, text: \"Mean decrease in ROC-AUC (permutation importance)\", color: t.ink, font: { size: 16 } },\n      },\n      y: {\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { display: false },\n        title: { display: true, text: \"Feature\", color: t.ink, font: { size: 16 } },\n      },\n    },\n  },\n  plugins: [zeroLinePlugin, errorBarsPlugin],\n});\n"}