Intro to D3

Created by Victor Mejia

about me

Freelance UI Engineer and Instructor

I ♥ Coding and Teaching

I ♥ JavaScript (Angular, Ionic, D3, Firebase)


Tweeting @_victormejia

Coding @victormejia

Blogging @ victormejia.me

what is D3?

Data

Driven

Documents

Created by Mike Bostock

efficient manipulation of documents based on data

not a just a charting library, it's so much more

let's focus on the word visualization

check out what

D3 can do

Awesome Visualizations

D3 Gallery

D3 out in the wild:

Emmet Sublime Package

California Drought

Simpson's Paradox

setosa.io

the plan

  • svg intro
  • selections
  • scales
  • axis
  • transitions
  • basic tooltips

svg intro

svg


            
          
(500, 300) (0, 0)

rect


            
          
(500, 300) (0, 0)

circle


            
          
(500, 300) (0, 0)

ellipse


            
          
(500, 300) (0, 0)

text


            Hello SVG!
          
Hello SVG! (500, 300) (0, 0)

line


            
          
(500, 300) (0, 0)

polyline


            
          
(500, 300) (0, 0)

path


            
          
(500, 300) (0, 0)

check this out too

https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial

and this

Leaving Pixels Behind

let's build a bar chart

Top GitHub Languages for 2014

var data =[{"lang":"JavaScript","value":"791535"}
{"lang":"Java","value":"561366"}
{"lang":"Ruby","value":"479525"}
{"lang":"CSS","value":"354354"}
{"lang":"PHP","value":"346761"}
{"lang":"Python","value":"317085"}
{"lang":"C","value":"289880"}
{"lang":"C++","value":"164722"}
{"lang":"C#","value":"123509"}
{"lang":"Objective-C","value":"119345"}
{"lang":"Shell","value":"99555"}
{"lang":"Perl","value":"73431"}
{"lang":"CoffeeScript","value":"43541"}
{"lang":"R","value":"42070"}
{"lang":"Go","value":"38130"}
];
          

Data source: GitHub Archive

Setup

you only need d3.js

+

some styling

Margin Convention

// (1) Select element
var el = d3.select('#chart'),

// (2) Grab element's dimensions
elWidth = parseInt(el.style('width'), 10),
elHeight = parseInt(el.style('height'), 10),

// (3) Declare margins for axis
margin = {top: 20, right: 10, bottom: 80, left: 40},

// (4) calculate width and height used for scaling
width = elWidth - margin.right - margin.left,
height = elHeight - margin.top - margin.bottom;

// (5) append svg element with added margins and group element inside
var svg = el.append("svg")
  .attr("width", elWidth)
  .attr("height", elHeight)
.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

// now in our code we can just reference width and height
          
http://bl.ocks.org/mbostock/3019563

Selections

d3.select


var el = d3.select('#chart');

// append an svg element
var svg = el.append('svg');

// append returns a new selection
svg.attr(...)
          

One element selected, one appended

d3.selectAll


// with many selected, can change all elements
var rect = svg.selectAll('rect')
                .attr(..);
          

Let's talk about data

Data are arrays


var data = [1, 3, 5, 7, 9];

var meetups = [
  { name: 'AngularJSOC', members: 286 },
  { name: 'OCMongoDB'  , members: 326 }
];
          

Data are mapped to elements


var selection = svg.selectAll('rect')
                  .data(data); // this creates empty placeholders

// appending to the enter selection creates new elements
selection
  .enter()
  .append('rect')
    .attr(/*...*/)
          

We can chain

// create viz
var selection = svg.selectAll('rect')
  .data(data)
  .enter()
  .append('rect')
    .attr({
      x: function (d, i) { ... },
      y: function (d, i) { ... },
      height: function (d) { ... },
      width: ...,
      fill: ...
    });
          

Enter, Update & Exit

Three Little Circles

Thinking with Joins

General Pattern


//create visualization
var rect = svg.selectAll('rect').data(data);

// transition new data
rect.enter()
  .append('rect')
    .attr(...)

// remove any data not needed
rect.exit().remove();

          
        

Enter

Update

Exit

Things to remember

  1. If the new dataset is smaller than the old one, the surplus elements end up in the exit selection and get removed.
  2. If the new dataset is larger, the surplus data ends up in the enter selection and new nodes are added.
  3. If the new dataset is exactly the same size, then all the elements are simply updated with new positions, and no elements are added or removed.

source: Thining With Joins

Some useful
D3
functions

d3.max


var values = [2, 10, 3];
var max = d3.max(values); // 10

// if array of objects...
var maxRepoCount = d3.max(data, function (d) {
  return d.value;
});
          

d3.extent


var values = [3, 1, 5, 8, 9, 2];

var max = d3.extent(values); // [1, 9]
          

Scales

d3.scale


var heightScale = d3.scale.linear()
  .domain([0, d3.max(data, function (d) { return d.value})])
  .range([height, 0]);

// ordinal scales for discrete data
var xScale = d3.scale.ordinal()
  .domain(data.map(function (d) { return d.category; }))
  .rangeRoundBands([0, width], 0.1); // useful for barcharts
          

Use scales for rect's attr

               
selection
  .attr({
    x: function (d, i) {
      return xScale(d.lang)
    },
    y: function (d, i) {
      return yScale(d.value);
    },
    height: function (d) {
      return height - yScale(d.value);
    },
    width: xScale.rangeBand(),
    fill: fillColor
  })
                
             

demo

Punchcard code along: scales

Axis

d3.svg.axis


// create axis
var xAxis = d3.svg.axis()
  .scale(xScale)
  .orient("bottom") // place label below tick
  .tickPadding(10); // padding b/n tick and label

var xAxisGroup = svg.append("g")
  .attr({
    class : 'axis',
    transform: 'translate(' + [0, height] + ')'
  }).call(xAxis);

          

axis demo

Punchcard code along: axis

Punchcard code along: display data

Transisitions

General Pattern

// (1) initial attributes
var r = svg.selectAll('rect')
  .data(data)
  .enter()
  .append('rect')
    .attr(initialAttrs);

// (2) transition to final state
r.transition()
  .delay(function (d, i){
    return d * 25; // delay each element
  })
  .ease("linear") // also "elastic", "bounce", etc.
  .duration(700) // entire transition
  .attr(finalAttrs);
          

transition demo

transition demo 2

Punchcard code along: transitions

Custom Tooltips

selection.on(type, listener)


var r = svg.selectAll('rect')
  .data(data)
  .enter()
  .append('rect')
    .attr(...)
    .on('mouseover', handleMouseover);

function handleMouseover(d, i) {
  // this: element moused over
  // d: datum
  // i: index
}
          

tooltip.css


.tooltip {
  visibility: hidden;
  background-color: #39393d;
}

.tooltip text {
  fill: #fff;
  font-size: 12px;
  shape-rendering: crispEdges;
}
          

add tooltip to svg


var tooltip = svg.append('g').attr({class: 'tooltip'});

tooltip.append('rect').attr({ height: '30', width: '100' });

tooltip.append('text').attr({ x: 10, y: 20 }); // relative to group element
          

event handlers


function handleMouseover(d, i) {
  // calculate x, y (add code to check for boundaries)
  var pos = { x: xScale(d.lang), y: yScale(d.value) - 35};

  var tooltip = svg.select('.tooltip')
    .attr('transform': 'translate(' + [pos.x, pos.y + ')');

  tooltip.select('text').text('Repos: ' + d.value);

  tooltip.style('visibility', 'visible');
}

function handleMouseout(d, i) {
  svg.select('.tooltip').style('visibility', 'hidden')
}
          

tooltip demo

tooltip demo 2

Some extra Resources

the end :)

© 2014 PEANUTS Worldwide LLC