d4 and friendly charting DSL for D3

29
d4 a friendly charting DSL for D3

description

This presentation gives a basic overview of d4 the charting DSL for D3.

Transcript of d4 and friendly charting DSL for D3

Page 1: d4 and friendly charting DSL for D3

d4a friendly charting DSL for D3

Page 2: d4 and friendly charting DSL for D3

What’s a chart?Let’s get our definitions straight

Page 3: d4 and friendly charting DSL for D3

–Garry Klass

“A graphical chart provides a visual display of data that otherwise would be presented in a table; a table, one that would otherwise be presented in text. Ideally, a chart should convey ideas

about the data that would not be readily apparent if they were displayed in a table

or as text.”

Page 4: d4 and friendly charting DSL for D3

Charts are not about displaying data, they are about conveying

ideas visually through data.

Page 5: d4 and friendly charting DSL for D3

Successful charts are succinct, self-explanatory visual systems which answer

questions about the comparison, composition, distribution or interrelationship

in the data they present.

Page 6: d4 and friendly charting DSL for D3

Successful charts are fit for purpose

Page 7: d4 and friendly charting DSL for D3

Being fit for purpose is hard to do using reusable charts because visual

expressiveness is often the first quality scraped away when trying to wedge an idea into the mold of a pre-made chart.

Page 8: d4 and friendly charting DSL for D3

Most prebuilt charts are not fit for purpose.

Page 9: d4 and friendly charting DSL for D3

Build Jigs Not ToolsFirst insight

Page 10: d4 and friendly charting DSL for D3

–Hunt & Thomas “The Pragmatic Programmer: From Journeyman to Master”

“When woodworkers are faced with the task of producing the same thing over and over, they

cheat. They build themselves a jig or a template. If they get the jig right once, they can reproduce a piece of work time after time. The

jig takes away complexity and reduces the chances of making mistakes, leaving the craftsman free to concentrate on quality.”

Page 11: d4 and friendly charting DSL for D3

Data is your raw material

D3 is a tool for visually shaping !your data

d4 guides your data !through D3

Image from: www.finewoodworking.com

Page 12: d4 and friendly charting DSL for D3

A chart is a jig• Used to control the process our output of another

tool

• Provides repeatability, accuracy and interchangeability in the production process

• Simplifies a complexities of a task

• Acts as a template or a guide

Page 13: d4 and friendly charting DSL for D3

Elements of a d4 jig• Chart - A collection of features, and a builder which renders

data using d3 into a graphical representation.

• Builder - A routine to apply sensible defaults to the features.

• Feature - A visual component of a chart, which helps convey meaning in the data.

• Dimension - A segment of the data described by the chart.

• Parser - A routine to prepare incoming data into a format appropriate for a given chart.

Page 14: d4 and friendly charting DSL for D3

A Basic Chart

// Let’s create a reusable column chart !d4.chart('column', function column() { return d4.baseChart() .mixin([{ 'name': 'bars', 'feature': d4.features.rectSeries }, { 'name': 'barLabels', 'feature': d4.features.stackedLabels }, { 'name': 'xAxis', 'feature': d4.features.xAxis }, { 'name': 'yAxis', 'feature': d4.features.yAxis }]); });

Chart Definition

Chart Feature

Chart Feature

Chart Feature

Chart Feature

Page 15: d4 and friendly charting DSL for D3

A Basic Chart

'use strict'; !$(document).ready(function(){ var data = [ { x: '2010', y:-10 }, { x: '2011', y:20 }, { x: '2012', y:30 }, { x: '2013', y:40 }, { x: '2014', y:50 }, ]; ! // now we create our chart instance. var chart = d4.charts.column(); ! // then we use our chart like a jig to // get d3 to render our data. d3.select('#example') .datum(data) .call(chart); });

Page 16: d4 and friendly charting DSL for D3

Context Over Configuration

Second Insight

Page 17: d4 and friendly charting DSL for D3

There is a software design concept called “convention over configuration,” which states that

software should specify a collection of opinionated defaults for the developer.

!The goal of this approach is to lessen the number

of obvious choices a developer must make before they are able to use the software.

Page 18: d4 and friendly charting DSL for D3

d4 extends this concept a bit and suggests that configuration should also be highly contextual to

the object the developer needs changed. !

Instead of making choices in some abstract config file, developers instead use a highly

declarative API to make changes directly to the object they want augment.

Page 19: d4 and friendly charting DSL for D3

A Basic Chart

'use strict'; !$(document).ready(function(){ var data = [ { x: '2010', y:-10 }, { x: '2011', y:20 }, { x: '2012', y:30 }, { x: '2013', y:40 }, { x: '2014', y:50 }, ]; ! var chart = d4.charts.column() ! // lets get rid of the yaxis .mixout(‘yAxis'); ! d3.select('#example') .datum(data) .call(chart); });

#mixout()

d4’s base charts make assumptions on what features are wanted. It is easy to remove an unwanted feature using mixout().

Page 20: d4 and friendly charting DSL for D3

A Basic Chart'use strict'; !$(document).ready(function(){ var data = [ { x: '2010', y:-10 }, { x: '2011', y:20 }, { x: '2012', y:30 }, { x: '2013', y:40 }, { x: '2014', y:50 }, ]; ! var chart = d4.charts.column() .mixin({ 'name' : 'zeroLine', 'feature' : d4.features.referenceLine }); ! d3.select('#example') .datum(data) .call(chart); });

#mixin()zeroLine

You can add features by mixing them in. However, some features do not look that great using their default settings, as is the case with our reference line.

Page 21: d4 and friendly charting DSL for D3

A Basic Chart'use strict'; !$(document).ready(function(){ var data = [ { x: '2010', y:-10 }, { x: '2011', y:20 }, { x: '2012', y:30 }, { x: '2013', y:40 }, { x: '2014', y:50 }, ]; ! var chart = d4.charts.column() .mixin({ 'name' : 'zeroLine', 'feature' : d4.features.referenceLine }) .using('zeroLine', function(zeroLine){ zeroLine .x1(0) .y1(function(){ return this.y(0); }) .x2(chart.outerWidth() - chart.marginLeft() - chart.margin().right) .y2(function(){ return this.y(0); }); }); d3.select('#example') .datum(data) .call(chart); });

#using()

You can control a feature’s attributes with the using() function.

Page 22: d4 and friendly charting DSL for D3

Separation of concerns matters

Third Insight

Page 23: d4 and friendly charting DSL for D3

Separation of Responsibilities

• Designers should not need to know JavaScript to style the chart and vice versa

• The chart does not own the data, and any transformations should be made on a copy of the data

• Don’t add needless abstractions, where possible leverage or proxy existing functionality in d3 or svg.

Page 24: d4 and friendly charting DSL for D3

d4 and CSSd4 adds several generated CSS classes to various elements of the chart as it renders. !The ability to control the series colors changes the story your charts are telling, allowing you to choose the correct visual representation of your series data to support the point you are trying to make.

These is the same chart, using the same data. The only thing that has changed is which CSS classes are being accessed.

Page 25: d4 and friendly charting DSL for D3

A Basic Chart'use strict'; !$(document).ready(function(){ var data = [ { year: '2010', unitsSold: 200, salesman : 'Bob' }, { year: '2011', unitsSold: 200, salesman : 'Bob' }, { year: '2012', unitsSold: 300, salesman : 'Bob' }, { year: '2013', unitsSold: -400, salesman : 'Bob' }, { year: '2014', unitsSold: -500, salesman : 'Bob' }, { year: '2010', unitsSold: 100, salesman : 'Gina' }, { year: '2011', unitsSold: 100, salesman : 'Gina' }, { year: '2012', unitsSold: 200, salesman : 'Gina' }, { year: '2013', unitsSold: -500, salesman : 'Gina' }, { year: '2014', unitsSold: -600, salesman : 'Gina' }, { year: '2010', unitsSold: 400, salesman : 'Average' }, { year: '2011', unitsSold: 100, salesman : 'Average' }, { year: '2012', unitsSold: 400, salesman : 'Average' }, { year: '2013', unitsSold: -400, salesman : 'Average' }, { year: '2014', unitsSold: -400, salesman : 'Average' } ]; ! var parsedData = d4.parsers.nestedStack() .x(function(){ return 'year'; }) .y(function(){ return 'salesman'; }) .value(function(){ return 'unitsSold'; })(data); ! var chart = d4.charts.stackedColumn() .outerWidth($('#example').width()) .x(function(x){ x.key('year'); }) .y(function(y){ y.key('unitsSold'); }) d3.select('#example') .datum(parsedData.data) .call(chart); });

using parsers

Notice: using the parser we define which values correspond to which dimension. Then you link those dimensions in the chart to the appropriate key.

Page 26: d4 and friendly charting DSL for D3

A Basic Chart$(document).ready(function(){ var data = [ { year: '2010', unitsSold:-100, salesman : 'Bob' }, { year: '2011', unitsSold:200, salesman : 'Bob' }, { year: '2012', unitsSold:300, salesman : 'Bob' }, { year: '2013', unitsSold:400, salesman : 'Bob' }, { year: '2014', unitsSold:500, salesman : 'Bob' }, { year: '2010', unitsSold:100, salesman : 'Gina' }, { year: '2011', unitsSold:100, salesman : 'Gina' }, { year: '2012', unitsSold:-100, salesman : 'Gina' }, { year: '2013', unitsSold:500, salesman : 'Gina' }, { year: '2014', unitsSold:600, salesman : 'Gina' }, { year: '2010', unitsSold:400, salesman : 'Average' }, { year: '2011', unitsSold:0, salesman : 'Average' }, { year: '2012', unitsSold:400, salesman : 'Average' }, { year: '2013', unitsSold:400, salesman : 'Average' }, { year: '2014', unitsSold:400, salesman : 'Average' } ]; ! var parsedData = d4.parsers.nestedGroup() .x('year') .y('unitsSold') .value('unitsSold')(data); ! var chart = d4.charts.groupedColumn(); chart .outerWidth($('#example').width()) .x(function(x){ x.key('year'); ! // Just for fun lets reduce the outer padding of the column chart. // This also shows how d4 transparently passes the appropriate calls to d3. x.rangeRoundBands([0, chart.width()], 0.2, 0.1); }) .y(function(y){ y.key('unitsSold'); }) .groupsOf(parsedData.data[0].values.length); ! d3.select('#example') .datum(parsedData.data) .call(chart); });

Same data different parser

The data is the same as the previous example, and simply by using a different data parser you can easily restructure the relationship for d4.

Page 27: d4 and friendly charting DSL for D3

Proxies in d4Maximize the work you are not doing

Page 28: d4 and friendly charting DSL for D3

(function() { 'use strict'; d4.feature('lineSeries', function(name) { var line = d3.svg.line(); line.interpolate('basis'); return { accessors: { x: function(d) { return this.x(d[this.x.$key]); }, ! y: function(d) { return this.y(d[this.y.$key]); }, ! classes: function(d, n) { return 'line stroke series' + n; } }, proxies: [line], render: function(scope, data, selection) { selection.append('g').attr('class', name); line .x(d4.functor(scope.accessors.x).bind(this)) .y(d4.functor(scope.accessors.y).bind(this)); ! var group = this.svg.select('.' + name).selectAll('g') .data(data); group.exit().remove(); group.enter().append('g') .attr('data-key', function(d) { return d.key; }) .attr('class', d4.functor(scope.accessors.classes).bind(this)) .append('path') .attr('d', function(d) { return line(d.values); }); } }; }); }).call(this);

var chart = d4.charts.line() .outerWidth($('#example').width()) .x(function(x){ x.key('year'); }) .y(function(y){ y.key('unitsSold'); }) .using('lineSeries', function(line) { line.interpolate('cardinal'); }) .mixin({ 'name' : 'dotSeries', 'feature' : d4.features.circleSeries, 'index' : 2 }) .using('dotSeries', function(dot) { dot .r(function() { return 5; }) .cx(function(d) { return this.x(d[this.x.$key]); }) .cy(function(d) { return this.y(d[this.y.$key]); }); });

Defining a proxy in a feature Using a proxy attribute

The interpolate() function is a defined feature accessor. Instead the feature transparently proxies it to d3.

Page 29: d4 and friendly charting DSL for D3

Thanks!Additional Resources

Examples & Documentation Site: http://www.visible.io Source code repository: https://github.com/heavysixer/d4

Mark Daggett 2014 @heavysixer