히트맵

Heatmap

두 범주(행·열)를 격자로 교차시키고 값의 크기를 색으로 채우는 차트. 큰 표를 한눈에 훑을 때 씁니다.

다른 이름: Matrix heatmap히트 매트릭스
···
html
<div class="viz">
  <svg id="svg"></svg>
  <div class="scale minor" id="scale"><span>0</span><i></i><span>100</span></div>
</div>
css
body{display:block}
.viz{display:flex;flex-direction:column;width:100%;height:100%}
svg{flex:1;min-height:0;display:block;width:100%;overflow:visible}
.scale{flex:none;display:flex;align-items:center;justify-content:center;gap:6px;padding:2px 4px 4px;font:600 9px/1 system-ui,sans-serif;color:var(--muted)}
.scale i{width:60px;height:7px;border-radius:4px;background:linear-gradient(90deg, color-mix(in srgb, var(--accent) 12%, var(--surface)), var(--accent))}
.cell{transition:fill .6s ease}
js
var svg = document.getElementById('svg');
var NS = 'http://www.w3.org/2000/svg';
function se(t){ return document.createElementNS(NS,t); }
function sa(e,o){ for (var k in o) e.setAttribute(k,o[k]); }
function R(a,b){ return a + Math.random()*(b-a); }

var COLS = 8, ROWS = 4;
var cells = [];
for (var r=0;r<ROWS;r++) for (var c=0;c<COLS;c++) cells.push({ r:r, c:c, v:R(10,90) });
var W=300, H=200;
var rects = [];

function measure(){
  var rc = svg.getBoundingClientRect();
  W = Math.max(rc.width,10); H = Math.max(rc.height,10);
  sa(svg,{ viewBox: '0 0 ' + W + ' ' + H });
}

function build(){
  svg.innerHTML = '';
  rects = [];
  var padL=8, padR=8, padT=8, padB=8;
  var pw=W-padL-padR, ph=H-padT-padB;
  var gap=2;
  var cw=(pw-gap*(COLS-1))/COLS, ch=(ph-gap*(ROWS-1))/ROWS;
  cells.forEach(function(cell){
    var x = padL+cell.c*(cw+gap), y = padT+cell.r*(ch+gap);
    var rect = se('rect');
    sa(rect,{x:x,y:y,width:cw,height:ch,rx:2,class:'cell'});
    svg.appendChild(rect);
    rects.push(rect);
  });
  paint();
}

function paint(){
  cells.forEach(function(cell,i){
    var pct = Math.max(6,Math.min(96, cell.v));
    rects[i].setAttribute('fill', 'color-mix(in srgb, var(--accent) ' + pct + '%, var(--surface))');
  });
}

new ResizeObserver(function(){ measure(); build(); }).observe(svg);
measure();
build();
setInterval(function(){
  cells.forEach(function(cell){ cell.v = R(8,92); });
  paint();
}, 2800);

색은 반드시 하나의 색상(hue)을 명도만 바꿔가며 씁니다 — 값이 클수록 진하게. 무지개색 그라데이션은 절대 쓰지 마세요. 색이 여러 번 바뀌면 어디가 "중간값"인지 뇌가 순서를 못 만들고, 실제로는 없는 경계가 생긴 것처럼 보입니다.

색만으로는 정확한 값을 읽을 수 없습니다(사람은 색의 미세한 명도 차이를 절대적으로 구분하지 못합니다) — 그래서 옆에 최소~최대를 보여주는 범례 막대가 항상 필요하고, 값이 중요한 표라면 칸 안에 숫자를 함께 적는 편이 안전합니다.

행·열의 정렬 순서가 패턴을 만듭니다. 비슷한 값끼리 군집되도록 정렬(군집화)하면 무작위로 보이던 표에서 숨은 구조가 드러납니다 — 반대로 순서를 대충 두면 실제로 있는 패턴도 안 보일 수 있습니다.

언제 쓰나

행×열 조합이 많아 표로는 한눈에 안 들어올 때, 전체적인 패턴·이상치를 빠르게 훑고 싶을 때.