Parallel Coordinates
An implementation of parallel coordinates in d3 as a reusable chart
This library is under only sporadic development by contributors using an outdated version of d3. Consider using this d3.v5 ES6 port by BigFatDog for a more modern approach.
Contributing
In order to obtain a linear history, please adhere to the workflow outlined by @bbroeksema.
Resources
Tutorials
- Wikipedia
- Parallel Coordinates, Robert Kosara
- Better Know a Visualization: Parallel Coordinates, Zach Gemignani
- Multivariate Analysis Using Parallel Coordinates, Stephen Few
- Edward Tufte's "Slopegraphs", Charlie Park
- Data Visualization's Final Frontier, J. Kevin Byrne
- D3JS Parallel Lines and Football, Patrick Martin
- Parallel Coordinates, Julian Heinrich
Papers
- Parallel Coordinates: a tool for visualizing mult-dimensional geometry, Alfred Inselberg
- Hyperdimensional Data Analysis Using Parallel Coordinates, Edward Wegman 2
- On Some Mathametics for Visualizing High Dimensional Data, Edward Wegman, Jeffrey Solka
- The State of the Art of Parallel Coordinates, Julian Heinrich & Daniel Weiskopf
Books
- Parallel Coordinates: Visual Multidimensional Geometry and Its Applications, Alfred Inselberg.
Videos
- Graph theory applied to data visualization playlist, Wayne Oldford
Software
- Protovis Parallel Coordinates, Mike Bostock
- d3.js Parallel Coordinates, Mike Bostock, Jason Davies
- GGobi, Debby Swayne, Di Cook, Duncan Temple Lang, Andres Buja, Nicholas Lewin-Koh, Heike Hofmann, Michael Lawrence, Hadley Wickham
- VizApp, Jee Vang
- XDAT
- Mondrian, Martin Theus
- Parvis, flo ledermann
- Parallel Coordinate Graphics using MATLAB, Bruce R. Land
- Parallel Coordinates in pandas, Python, Vytautas Janฤauskas
- PointCloudXplore
API
# d3.parcoords(config)
Setup a new parallel coordinates chart.
# parcoords(selector)
Create the chart within a container. The selector can also be a d3 selection.
# parcoords.animationTime(milliseconds = 1100)
Allows you to set the time it takes for flipping an axis on double click.
// Flipping an axis will take half a second
parcoords.animationTime(500);
# parcoords.data([values])
Add data to the chart by passing in an array of values.
A single value may be either an object or an array. All values should be the same format.
// objects
var foods = [
{name: "Asparagus", protein: 2.2, calcium: 0.024, sodium: 0.002},
{name: "Butter", protein: 0.85, calcium: 0.024, sodium: 0.714},
{name: "Coffeecake", protein: 6.8, calcium: 0.054, sodium: 0.351},
{name: "Pork", protein: 28.5, calcium: 0.016, sodium: 0.056},
{name: "Provolone", protein: 25.58, calcium: 0.756, sodium: 0.876}
];
// arrays
var cube = [
[0, 0, 0],
[1, 0, 0],
[0, 1, 0],
[1, 1, 0],
[0, 0, 1],
[1, 0, 1],
[0, 1, 1],
[1, 1, 1]
];
# parcoords.render()
Renders the polylines.
If no dimensions have been specified, it will attempt to detect quantitative dimensions based on the first data entry. If scales haven't been set, it will autoscale based on the extent for each dimension.
# parcoords.rotateLabels(enabled = false)
Whether scrolling on top of a label should result in the labels rotating.
# parcoords.dimensions(dimensions)
If dimensions is specified, sets the quantitative dimensions to be visualized and custom formatting. The format is an object of dimension objects. This will update the xscale domain, but will not trigger re-rendering of lines or axes.
var dimensions = {
"name":
{
orient: 'right',
type: 'string',
tickPadding: 0,
innerTickSize: 8
},
"protein": {type:"number"},
"calcium": {type:"number"}};
If no dimensions are specified, then it returns the currently set dimensions.
Dimension attributes include:
"title": String label for dimension
"type": Possible values include: string, date and number. Detected types are automatically populated by detectDimensions using d3.parcoords.detectDimensionTypes.
"ticks": Number of horizontal ticks to include on y axis
"tickValues": Array of values to display for tick labels
"orient": Orientation of ticks and tickValues(left or right of axis)
"innerTickSize": Length of the horizontal ticks in between the top and bottom
"outerTickSize": Length of the horizontal ticks at the top and bottom
"tickPadding": Pixels to pad the tick title from the innerTickSize
"yscale": Type of scale to use for the axis(log, linear, ordinal). Reference D3 Scales
"index": Integer position for ordering dimensions on the x axis
# parcoords.smoothness(double)
If double exists, polylines will be rendered with specified amount of curvature. NOTE: sylvester.js is a necessary dependency for this feature.
parcoords.smoothness(.2);
# parcoords.color(color)
If a color is a string, polylines will be rendered as that color. If color is a function, that function will be run for each data element and the polyline color will be the return value.
To set all lines to a transparent green:
parcoords.color("rgba(0,200,0,0.3)");
Function example
parcoords.color(function(d) {
// d corresponds to the individual data object
if (d.x < 100)
return "red";
else
return "green";
});
If no color is specified, then it returns the currently set color.
# parcoords.flipAxes()
Allows you to flip axes without animation.
parcoords.flipAxes(["x", "y"]);
# parcoords.state()
Returns an object which contains the state of the chart. This is particularly useful for debugging with a JavaScript console.
# parcoords.state
Exposes the public state of parallel coordinates. Useful for debugging in a JavaScript console. Avoid modifying values directly, instead use methods such as parcoords.data() to update the state.
The design of this object is experimental and contributed by Ziggy Jonsson. Read more at this d3-js mailing list discussion.
When the public state is updated through a method, an event will fire.
# parcoords.createAxes()
Create static SVG axes with dimension names, ticks, and labels.
# parcoords.removeAxes()
Remove SVG axes.
# parcoords.updateAxes()
Update SVG axes. Call this after updating the dimension order.
# parcoords.brushMode(mode)
1D-axes
1D-axes-multi
2D-strums
angular
# parcoords.brushed()
For brushable plots, returns the selected data.
# parcoords.brushReset()
Reset all brushes.
# parcoords brushedColor()
Change coloring of brushed items. The default behavior is that brushed items get the original coloring. The example below will make all brushed items black.
parcoords.brushedColor("#000");
# parcoords alphaOnBrushed()
Change the alpha of the layer between the foreground and brushed items. This value defaults to 0, making the foreground invisible when items are brushed. Increasing the alpha value will result in a shadows effect, where the foreground items are increasingly more visible when alpha increases. Combined with brushedColor various highlight effects can be achieved on brushing.
// default behavior: brushed items are colored the same as foreground items,
// forground items are invisible.
// Add shadows: Brushed items are colored the same as foreground items, forground
// items are vaguely visible. Same effect is achieved by parcoords.shadows()
parcoords.alphaOnBrushed(0.1);
// Highlight brushed items with a different color. Foreground items are fully
// visibible, except those who are covered by brushed items.
parcoords
.alphaOnBrushed(1)
.brushedColor("#000");
# parcoords.reorderable()
Enable reordering of axes. Automatically creates axes if they don't exist.
The behavior is identical to that of the original reorderable d3.js parallel coordinates.
# parcoords.axisDots(size = 0.1)
Mark the points where polylines meet an axis with dots of radius size.
# parcoords.shadows()
Active greyed-out background shadows. See brushedColor and alphaOnBrushed
# parcoords.width()
parcoords.width(300)
# parcoords.height()
parcoords.height(300)
# parcoords.margin()
parcoords.margin({
top: 100,
left: 0,
right: 0,
bottom: 20
})
# parcoords.composite()
Change foreground context's globalCompositeOperation
- source-over
- source-in
- source-out
- source-atop
- destination-over
- destination-in
- destination-out
- destination-atop
- lighter
- darker
- xor
- copy
# parcoords.alpha()
Change the opacity of the polylines, also the foreground context's globalAlpha.
# parcoords.autoscale()
Set the xscale, yscale, and canvas sizes. Usually this is called automatically, such as on render() or resize() events
# parcoords.mode(type)
Toggle the rendering mode. Currently there are two options:
- "default" renders instantaneously, but is less responsive with more than ~2000 rows of data
- "queue" puts the data in a queue, and renders progressively. This way the user can interact with the chart during rendering.
# parcoords.rate(integer)
Change the number of lines drawn each frame when the rendering mode is set to "queue". Some suggested values for different machines are:
- Mobile phone or iPad: 12-30
- Normal PC with Firefox: 20-60
- Normal PC with Chrome/Safari: 30-100
- Fast PC with Chrome/Safari: 100-300
In the future, an automatic rate adjuster will be included to optimize this number on-the-fly.
# parcoords.detectDimensions()
Set the quantative dimensions using based on the first row of data.
# parcoords.highlight([values])
Pass an array of data to overlay the data on the chart, masking the foreground.
# parcoords.unhighlight([values])
Clear the highlighting layer. This is equivalent to calling parcoords.clear("highlight").
# parcoords.interactive()
Activate interactive mode for use with a JavaScript console. The concept is that for each method that modifies a chart, everything that needs to happen to update the rendered chart will run automatically.
Currently this only affects adding/removing/reordering dimensions. Normally the chart must be re-rendered and axes updated:
parcoords.dimensions([3,1,2])
.render()
.updateAxes()
In interactive mode, just specify the new dimensions array.
parcoords.dimensions([3,1,2])
# parcoords.clear(layer)
Clear the layer, which could be "foreground", "shadows", "marks", "extents" or "highlight".
# parcoords.canvas
An object containing the canvas elements.
# parcoords.ctx
An object containing the canvas 2d rendering contexts. Use this to modify canvas rendering styles, except for the foreground stroke which is controlled by parcoords.color().
# parcoords.on(event, listener)
Trigger a listener when an event fires. The value of this in the listener refers to parcoords. The data passed into the listener depends on type of event:
- "render" returns no data
- "resize" returns an object containing the width, height and margin
- "highlight" returns the highlighted data
- "brush" returns the brushed data
When values in the state are updated through methods, an event of the same name fires (except height, width and margin which fire resize). The data passed into the listener is an object containing the new value, value, and the old value, previous.
Custom values must be passed into the original chart config to register events.
- "dimensions"
- "data"
- "color"
- "composite"
- "alpha"
Axes
Todo Axis configuration
Developing
The 'examples' directory contain a number of examples that demonstrate the various function of d3.parcoords.js. If you make any chances make sure to verify that these examples still work as expected.
Build
make
runs the Makefile to concatenate d3.parcoords.js. This needs to be done
after each modification to files in the src/ directory.
Test framework
Recently (as of Oct. 2014), we started to implement a vows-based test suite to more rigorously and in a more automated way, test the functionality of d3.parcoords.js. The test suite itself can be found under tests/.
To run the tests you'll need some initial setup. First, you'll need to have nodejs and npm installed. Next, to run the tests, first run:
$ npm install
** NOTE: *** The node canvas package, which is a requirement, might need some additional software to be installed. Please refer to this page for further instructions.
Now you should be able to run the test suite:
$ make test
Credits
This library created with examples, suggestions and contributions from Mike Bostock, Jason Davies, Mary Becica, Stephen Boak, Ziggy Jonsson, Ger Hobbelt, Johan Sundstrรถm, Raffael Marty, Hugo Lopez, Bob Monteverde, Vadim Ogievetsky, Chris Rich, Patrick Martin, Sean Parmelee, Alfred Inselberg, Scott Markwell, Julian Heinrich, and Bertjan Broeksema.