d4 and friendly charting DSL for D3

3,094 views

Published on

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

Published in: Data & Analytics
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,094
On SlideShare
0
From Embeds
0
Number of Embeds
41
Actions
Shares
0
Downloads
28
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

d4 and friendly charting DSL for D3

  1. 1. d4 a friendly charting DSL for D3
  2. 2. What’s a chart? Let’s get our definitions straight
  3. 3. –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.”
  4. 4. Charts are not about displaying data, they are about conveying ideas visually through data.
  5. 5. Successful charts are succinct, self- explanatory visual systems which answer questions about the comparison, composition, distribution or interrelationship in the data they present.
  6. 6. Successful charts are fit for purpose
  7. 7. 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.
  8. 8. Most prebuilt charts are not fit for purpose.
  9. 9. Build Jigs Not Tools First insight
  10. 10. –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.”
  11. 11. 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
  12. 12. 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
  13. 13. 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.
  14. 14. 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
  15. 15. 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); });
  16. 16. Context Over Configuration Second Insight
  17. 17. 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.
  18. 18. 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.
  19. 19. 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().
  20. 20. 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.
  21. 21. 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.
  22. 22. Separation of concerns matters Third Insight
  23. 23. 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.
  24. 24. d4 and CSS d4 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.
  25. 25. 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.
  26. 26. 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.
  27. 27. Proxies in d4 Maximize the work you are not doing
  28. 28. (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.
  29. 29. Thanks! Additional Resources Examples & Documentation Site: http://www.visible.io Source code repository: https://github.com/heavysixer/d4 Mark Daggett 2014 @heavysixer

×