Parallel coordinates

A parallel coordinates (or parcoords) plot visualizes multivariate data as lines traversing parallel axes. This approach facilitates comparison across variables, and identification of relationships and patterns. These plots are easier to use with interactive filtering; see the brushable version.

// Specify the chart’s dimensions.
const width = 928;
const height = keys.length * 120;
const marginTop = 20;
const marginRight = 10;
const marginBottom = 20;
const marginLeft = 10;

// Create an horizontal (x) scale for each key.
const x = new Map(Array.from(keys, (k) => [k, d3.scaleLinear(d3.extent(data, (d) => d[k]), [marginLeft, width - marginRight])]));

// Create the vertical (y) scale.
const y = d3.scalePoint(keys, [marginTop, height - marginBottom]);

// Create the color scale.
const color = d3.scaleSequential(x.get(keyz).domain(), (t) => d3.interpolateBrBG(1 - t));

// Create the SVG container.
const svg = d3.create("svg")
    .attr("viewBox", [0, 0, width, height])
    .attr("width", width)
    .attr("height", height)
    .attr("style", "max-width: 100%; height: auto;");

// Append the lines.
const line = d3.line()
  .defined(([, value]) => value != null)
  .x(([key, value]) => x.get(key)(value))
  .y(([key]) => y(key));

svg.append("g")
    .attr("fill", "none")
    .attr("stroke-width", 1.5)
    .attr("stroke-opacity", 0.4)
  .selectAll("path")
  .data(data.slice().sort((a, b) => d3.ascending(a[keyz], b[keyz])))
  .join("path")
    .attr("stroke", (d) => color(d[keyz]))
    .attr("d", (d) => line(d3.cross(keys, [d], (key, d) => [key, d[key]])))
  .append("title")
    .text((d) => d.name);

// Append the axis for each key.
svg.append("g")
  .selectAll("g")
  .data(keys)
  .join("g")
    .attr("transform", (d) => `translate(0,${y(d)})`)
    .each(function(d) { d3.select(this).call(d3.axisBottom(x.get(d))); })
    .call((g) => g.append("text")
      .attr("x", marginLeft)
      .attr("y", -6)
      .attr("text-anchor", "start")
      .attr("fill", "currentColor")
      .text((d) => d))
    .call((g) => g.selectAll("text")
      .clone(true).lower()
      .attr("fill", "none")
      .attr("stroke-width", 5)
      .attr("stroke-linejoin", "round")
      .attr("stroke", "white"));

display(svg.node());
const data = FileAttachment("data/cars.csv").csv({typed: true});
const keys = data.then((data) => data.columns.slice(1, -1));
✎ Suggest changes to this page