Parallel coordinates

평행 좌표

Lines up many variables as parallel vertical axes and draws one item’s values across all of them as a single line.

Also known as: Parallel coordinates plot
···
html
<div class="viz">
  <svg id="svg"></svg>
</div>
css
body{display:block}
.viz{position:relative;width:100%;height:100%}
svg{display:block;width:100%;height:100%;overflow:visible}
.axisline{stroke:var(--line);stroke-width:1}
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 AXES = ['가격','성능','디자인','내구성','A/S'];
var NITEM = 6;
var cur = [];
for (var i=0;i<NITEM;i++) cur.push(AXES.map(function(){ return 0.5; }));
var W=300, H=200;

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

function draw(){
  svg.innerHTML = '';
  var padL=14,padR=14,padT=14,padB=20;
  var pw=W-padL-padR, ph=H-padT-padB;
  var n=AXES.length;
  var fs = Math.max(7,Math.min(10,W/48));
  var xs = AXES.map(function(_,k){ return padL + pw*k/(n-1); });
  xs.forEach(function(x,k){
    var line = se('line');
    sa(line,{ x1:x,y1:padT,x2:x,y2:padT+ph,class:'axisline' });
    svg.appendChild(line);
    var lab = se('text');
    sa(lab,{ x:x,y:padT+ph+13,'text-anchor':'middle',fill:'var(--muted)','font-size':fs,'font-weight':600 });
    lab.textContent = AXES[k];
    svg.appendChild(lab);
  });
  for (var i=NITEM-1;i>=0;i--){
    var pts = cur[i].map(function(v,k){ return xs[k].toFixed(1)+','+(padT+(1-v)*ph).toFixed(1); });
    var d = 'M'+pts.join(' L');
    var isMain = i===0;
    var pl = se('path');
    sa(pl,{ d:d, fill:'none', stroke: isMain?'var(--accent)':'var(--muted)', 'stroke-width': isMain?2.6:1.1, 'stroke-opacity': isMain?1:0.32, 'stroke-linejoin':'round' });
    svg.appendChild(pl);
  }
}

function animate(target){
  var from = cur.map(function(r){ return r.slice(); });
  var t0 = performance.now();
  function step(t){
    var p = Math.min(1,(t-t0)/850);
    var e = 1-Math.pow(1-p,3);
    cur = from.map(function(row,i){ return row.map(function(v,k){ return v+(target[i][k]-v)*e; }); });
    draw();
    if (p<1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

function nextTarget(){
  var t=[];
  for (var i=0;i<NITEM;i++) t.push(AXES.map(function(){ return R(0.08,0.94); }));
  return t;
}

new ResizeObserver(function(){ measure(); draw(); }).observe(svg);
measure();
draw();
animate(nextTarget());
setInterval(function(){ animate(nextTarget()); }, 3800);

A scatter plot tops out at two variables on x/y, but parallel coordinates keeps adding axes side by side — five, ten variables fit on one screen. Each item draws one point per axis and those points connect into a single line, so "this item is high on axis A, low on axis B" shows up directly as the line's slope.

Axis order is a choice, not a property of the data — put correlated axes next to each other and the segment between them bunches into near-parallel lines, a visible signature of correlation; an arbitrary order turns the same data into spaghetti. Each axis also needs its own min/max normalization, since units differ axis to axis — one global scale flattens whichever axis has the smallest range into a thin band.

With dozens or hundreds of lines, overlap turns the chart into a gray smear; the fix is to pick one or two items to highlight in full color and fade the rest into a gray background. Rather than deleting axes because the first look is cluttered, the real remedy is interaction — brushing a range on one axis to filter every line through it. A static auto-play demo like this one only shows half of what the chart is for.

When to use

Use it with four or more variables to explore multidimensional patterns or outliers across items. With only two variables, a scatter plot reads more precisely.