Thursday, March 26, 2009

Protovis. A graphical toolkit for visualization

Protovis is a visualization toolkit for JavaScript using the canvas element. It takes a graphical approach to data visualization, composing custom views of data with simple graphical primitives like bars and dots. These primitives are called marks, and each mark encodes data visually through dynamic properties such as color and position. For example, this simple bar chart visually encodes an array of numbers with height:
new pv.Panel().width(150).height(150)
.add(pv.Bar)
.data([1, 1.2, 1.7, 1.5, .7, .3])
.bottom(0)
.width(20)
.height(function(d) d * 80)
.left(function() this.index * 25)
.root.render();

Although marks are simple by themselves, you can combine them in interesting ways to make rich, interactive visualizations. To facilitate this, Protovis supports panels and inheritance. A panel is a container for marks; the contained marks are replicated for each data point on the panel. You can vary the panel position to create small multiple displays, or you can overlay panels, such as in this stacked area chart of sine waves:

new pv.Panel().width(150).height(150)
.add(pv.Panel)
.data([[Math.sin(x / y)
for (x in pv.range(50))]
for (y in pv.range(3, 9))])
.add(pv.Area)
.data(function(d) d)
.fillStyle(pv.Colors.category19.unique)
.bottom(function() let (c = this.cousin())
c ? (c.bottom + c.height) : 0)
.height(function(d) (d + 1) * 13)
.left(function() this.index * 3)
.root.render();

Inheritance lets you derive new marks from existing ones, while sharing some or all of the same properties. This is the same principle as cascading in CSS, or prototypal inheritance in JavaScript. For example, here we derive labels for a rule and a bar:

new pv.Panel().width(150).height(150)
.bottom(9.5).left(20)
.add(pv.Rule)
.data(function() pv.range(0, 2, .5))
.bottom(function(d) d * 70)
.anchor("left").add(pv.Label)
.root.add(pv.Bar)
.data([1, 1.2, 1.7, 1.5, .7])
.height(function(d) d * 70).width(20)
.bottom(0).left(function() this.index * 25 + 4)
.anchor("bottom").add(pv.Label)
.root.render();
http://vis.stanford.edu/protovis/

No comments: