This parallel coordinates visualization of cars from the ‘70s and ‘80s demonstrates one of D3 2.5.0’s new interactive features: the brush component. By clicking and dragging along any axis, you can specify a filter for that dimension. The brush component is also used in the updated scatterplot matrix example.
-
-
Save sfsheath/9814676 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name | year_created | seating_capacity | |
---|---|---|---|
Amphitheater at Arles | 90 | 20000 | |
Amphitheater at Carnuntum | 100 | 12000 | |
Amphitheater at Carthage | 100 | 30000 | |
Amphitheater at Conimbriga | 50 | 4000 | |
Amphitheater at Dura Europos | 216 | 1000 | |
Amphitheater at Empuries | 50 | 3300 | |
Amphitheater at Gemellae | 130 | 3000 | |
Amphitheater at Italica | 140 | 25000 | |
Amphitheater at Leptis Magna | 56 | 16000 | |
Amphitheater at Lyon | 19 | 20000 | |
Amphitheater at Marcianopolis (Devnya) | 200 | 4000 | |
Amphitheater at Mérida (Emerita Augusta) | -8 | 15000 | |
Amphitheater at Pompeii | -70 | 22000 | |
Amphitheater at Pula | 1 | 26000 | |
Amphitheater at Segobriga | 90 | 7500 | |
Amphitheater at Tarragona | 100 | 15000 | |
Amphitheater at Thysdrus | 238 | 35000 | |
Amphitheatre at Nimes | 90 | 24000 | |
Amphitheatre at Paestum | -75 | 4500 | |
Amphitheatre at Sutrium | -50 | 8500 | |
Amphitheatre at Telesia | -75 | 9000 | |
Amphitheatre at Uthina | 120 | 16000 | |
Amphitheatre at Verona | 30 | 30000 | |
Colosseum (Rome) | 70 | 50000 | |
Flavian Amphitheater at Pozzuoli | 70 | 20000 | |
Large amphitheater at Carnuntum | 150 | 15000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
svg { | |
font: 10px sans-serif; | |
} | |
.background path { | |
fill: none; | |
stroke: #ccc; | |
stroke-opacity: .4; | |
shape-rendering: crispEdges; | |
} | |
.foreground path { | |
fill: none; | |
stroke: steelblue; | |
stroke-opacity: .7; | |
} | |
.brush .extent { | |
fill-opacity: .3; | |
stroke: #fff; | |
shape-rendering: crispEdges; | |
} | |
.axis line, | |
.axis path { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.axis text { | |
text-shadow: 0 1px 0 #fff; | |
cursor: move; | |
} | |
</style> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var m = [30, 10, 10, 10], | |
w = 960 - m[1] - m[3], | |
h = 500 - m[0] - m[2]; | |
var x = d3.scale.ordinal().rangePoints([0, w], 1), | |
y = {}, | |
dragging = {}; | |
var line = d3.svg.line(), | |
axis = d3.svg.axis().orient("left"), | |
background, | |
foreground; | |
var svg = d3.select("body").append("svg") | |
.attr("width", w + m[1] + m[3]) | |
.attr("height", h + m[0] + m[2]) | |
.append("g") | |
.attr("transform", "translate(" + m[3] + "," + m[0] + ")"); | |
d3.csv("cars.csv", function(error, cars) { | |
// Extract the list of dimensions and create a scale for each. | |
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) { | |
return d != "name" && (y[d] = d3.scale.linear() | |
.domain(d3.extent(cars, function(p) { return +p[d]; })) | |
.range([h, 0])); | |
})); | |
// Add grey background lines for context. | |
background = svg.append("g") | |
.attr("class", "background") | |
.selectAll("path") | |
.data(cars) | |
.enter().append("path") | |
.attr("d", path); | |
// Add blue foreground lines for focus. | |
foreground = svg.append("g") | |
.attr("class", "foreground") | |
.selectAll("path") | |
.data(cars) | |
.enter().append("path") | |
.attr("d", path); | |
// Add a group element for each dimension. | |
var g = svg.selectAll(".dimension") | |
.data(dimensions) | |
.enter().append("g") | |
.attr("class", "dimension") | |
.attr("transform", function(d) { return "translate(" + x(d) + ")"; }) | |
.call(d3.behavior.drag() | |
.on("dragstart", function(d) { | |
dragging[d] = this.__origin__ = x(d); | |
background.attr("visibility", "hidden"); | |
}) | |
.on("drag", function(d) { | |
dragging[d] = Math.min(w, Math.max(0, this.__origin__ += d3.event.dx)); | |
foreground.attr("d", path); | |
dimensions.sort(function(a, b) { return position(a) - position(b); }); | |
x.domain(dimensions); | |
g.attr("transform", function(d) { return "translate(" + position(d) + ")"; }) | |
}) | |
.on("dragend", function(d) { | |
delete this.__origin__; | |
delete dragging[d]; | |
transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")"); | |
transition(foreground) | |
.attr("d", path); | |
background | |
.attr("d", path) | |
.transition() | |
.delay(500) | |
.duration(0) | |
.attr("visibility", null); | |
})); | |
// Add an axis and title. | |
g.append("g") | |
.attr("class", "axis") | |
.each(function(d) { d3.select(this).call(axis.scale(y[d])); }) | |
.append("text") | |
.attr("text-anchor", "middle") | |
.attr("y", -9) | |
.text(String); | |
// Add and store a brush for each axis. | |
g.append("g") | |
.attr("class", "brush") | |
.each(function(d) { d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brushstart", brushstart).on("brush", brush)); }) | |
.selectAll("rect") | |
.attr("x", -8) | |
.attr("width", 16); | |
}); | |
function position(d) { | |
var v = dragging[d]; | |
return v == null ? x(d) : v; | |
} | |
function transition(g) { | |
return g.transition().duration(500); | |
} | |
// Returns the path for a given data point. | |
function path(d) { | |
return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; })); | |
} | |
// When brushing, don’t trigger axis dragging. | |
function brushstart() { | |
d3.event.sourceEvent.stopPropagation(); | |
} | |
// Handles a brush event, toggling the display of foreground lines. | |
function brush() { | |
var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }), | |
extents = actives.map(function(p) { return y[p].brush.extent(); }); | |
foreground.style("display", function(d) { | |
return actives.every(function(p, i) { | |
return extents[i][0] <= d[p] && d[p] <= extents[i][1]; | |
}) ? null : "none"; | |
}); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment