D3 – What D3 is – How D3 works



D3 – What D3 is – How D3 works

0 0


d3-talk


On Github anthonysimone / d3-talk

D3

Data Driven Documents

What D3 is

  • A Javascript data visualization library
  • Create by Mike Bostock d3js.org
  • FAOS

What D3 is not

  • A charting library
  • ie - Highcharts and Google charts
  • There are no prebuilt .chart methods or functions that you pass data to, for example

What D3 does

  • Binds arbitrary data to the DOM
  • Allows you to apply data driven transformation to the document
  • Uses transitions, CSS, and SVG
  • Provides a set of tools designed to work with data and handle their manipulation
  • Has no dependencies

How D3 works

  • D3 is built the functional pattern
  • Similar to jQuery, most functions return the object they operate on
  • Data structures and visualizations in D3 are generated by chaining many functions together
  • D3 has no dependencies and uses its own selector syntax

D3 Selectors and the DOM

  • Take any simple or complex selectors as arguments, pretty much the same as D3
    d3.select('div');
    d3.selectAll('div');
  • DOM modification
    d3.selectAll('.some-class').html()    // Get inner html
        .text('Some text')                // Set text
        .classed('.a-class')              // Check if has class
        .classed('.set-class', true)      // Set class
        .classed('.remove-class', false)  // Remove class
        .attr('href', 'google.com')
        .style('font-size', '20px')
        .append('h1').text(function() {   // Pass a function
            return getH1Text();
        });

Data Binding

  • This is the main functionality D3 offers
  • It creates a visual representation of your data
  • Updates the visual elements when the data changes
  • Removes them when data is no longer in the dataset

D3 Patterns

  • D3 is simply a set of tools without strict rules
  • Readable, maintainable, and reusable must have a structure
  • Most commonly, an enter-update-exit pattern is used

enter-update-exit

  • Three Steps: Enter, Update, and Exit
  • Generally, the following occurs at each step:
    • Enter - Create new data points in the data binding model
    • Update - Change data and styles to represent the current visualization
    • Exit - Remove old data points that are no longer represented
  • When visualizing a single set of static data, each step will occur once

enter-update-exit Visually

  • Enter - Returns data selection not represented in the DOM
  • Update - Returns selection that currently exists in both the dataset AND the DOM
  • Exit - Returns selection that exists in the DOM but is no longer represented in the data

Enter

  • The first time you run enter, no div.h-bar element exist, so you select nothing
  • .data(data) binds the data to the d3 structure, and enter compares the two selections
  • EVERY element in the data is returned, then it creates one for each point
// Enter
d3.select("#bar-chart").selectAll("div.h-bar")
  .data(data)
  .enter()
  .append("div")
    .attr("class", "h-bar")
      .append("span");

Update

  • There is no ".update()" method, it's just a conceptual step
  • Take all items and update their data bound properties
// Update
d3.select("#bar-chart").selectAll("div.h-bar")
  .data(data)
    .style("width", function (d) {
      return (d * 3) + "px";
    })
    .select("span")
      .text(function (d) {
        return d;
      });

Exit

  • Returns items that are in the DOM but no longer in the dataset
  • Most commonly, you just .remove() them
// Exit
d3.select("#bar-chart").selectAll("div.h-bar")
  .data(data)
  .exit()
    .remove();

Putting it together

  • Putting all the parts together to make a simple visualization using data in an array
  • For the first example, we'll use simple data as an array
// The data
var data = [10, 15, 30, 50, 80, 65, 55, 30, 20, 10, 8];
// The render function
function render(data) {
  // Enter...
  // Update...
  // Exit...
}
// Call render
render(data);

Example

Toggle Data Stream
10
15
30
50
80
65
55
30
20
10
8

D3 <3s Arrays

  • D3 spends lots of time manipulating arrays
  • .min, .max, .extent, .sum, .median, .mean, .quantile, .bisect
  • Also provides simple interface to sort and filter data

Filtering

  • Take the following objects as our data
var data = [
  {expense: 10, category: "Retail"},
  {expense: 15, category: "Gas"},
  {expense: 30, category: "Retail"},
  {expense: 50, category: "Dining"},
  {expense: 80, category: "Gas"},
  {expense: 65, category: "Retail"},
  {expense: 55, category: "Gas"},
  {expense: 30, category: "Dining"},
  {expense: 20, category: "Retail"},
  {expense: 10, category: "Dining"},
  {expense: 8, category: "Gas"}
];

Updating with Objects & Filters

function render(data, category) {
// Enter (add h-bar and a span)
// Exit (remove)
// Update
d3.select("#example2 .chart").selectAll("div.h-bar")
    .data(data)
  .attr("class", "h-bar")
  .style("width", function (d) {
    return (d.expense * 5) + "px";}
  )
  .select("span")
    .text(function (d) {
      return d.category;
    });
// Filter
d3.select("#example2 .chart").selectAll("div.h-bar")
  .filter(function (d, i) {
    return d.category == category;
  })
  .classed("selected", true);
}

Filter on click

  • The render and render with filter
render(data);

function select(category) {
  render(data, category);
}
  • A filter button example
<button onclick="select('Retail')">Retail</button>

The Filterable Chart

Retail
Gas
Retail
Dining
Gas
Retail
Gas
Dining
Retail
Dining
Gas
Retail Gas Dining Clear

Sorting

  • Comparator takes two items, makes comparison, and orders based on return value
// Replace filter snippet with the following
if(comparator) {
  d3.select("body")
    .selectAll("div.h-bar")
    .sort(comparator);
}
// Comparator functions
var compareByExpense = function (a, b) {
  return a.expense < b.expense?-1:1;
};
var compareByCategory = function (a, b) {
  return a.category < b.category?-1:1;
};

Sort on Click

  • The render and render with filter
render(data);

function sort(comparator) {
  render(data, comparator);
}
  • A sort button example
<button onclick="sort(compareByExpense)">Sort by Expense</button>

The Sortable Chart

Retail
Gas
Retail
Dining
Gas
Retail
Gas
Dining
Retail
Dining
Gas
Compare by Expense Compare by Category Reset

Scale

  • Translates data to an understandable visual representation
  • Domain - input
  • Range - translated output
  • Can translate a number range to a color range
  • Are used when setting up axes
  • linear, power, log, time, ordinal
  • Interpolation
// Simple linear scale mapping numbers 0 through 100
// to colors #add8e6 through blue
var colorScale = d3.scale.linear()
  .domain([0,100])
  .range(["#add8e6", "blue"]);

Using a scale in Update

// Data as objects
var data = [{width: 10, color: 23},{width: 15, color: 33}...];
// Update with scale
d3.select("#example4 .chart").selectAll("div.h-bar")
  .data(data)
    .attr("class", "h-bar")
      .style("width", function(d) {
        return (d.width * 5) + "px";
      })
      .style("background-color", function(d) {
        return colorScale(d.color);
      })
    .select("span")
      .text(function(d) {
        return d.width;
      });

Color Scale Example

Toggle Data Stream
10
15
30
50
80
65
55
30
20
10
8

Axes!

  • .axis method helps to construct axes
  • Customize with .scale, .ticks, .orient
function renderAxis(scale, i, orient) {
  var axis = d3.svg.axis()
    .scale(scale)
    .orient(orient)
    .ticks(5);

  svg.append("g")
    .attr("transform", function () {
      if (["top", "bottom"].indexOf(orient) >= 0) {
        return "translate(" + margin + "," + i * offset + ")";
      } else {
        return "translate(" + i * offset + ", " + margin + ")";
      }
    })
    .call(axis);
}

Some Axes

Horizontal bottom horizontal top Vertical left Vertical right

Get some grid lines

  • Render flexible gridlines by drawing lines at each tick
  • D3 decides where ticks go with input from you
function renderXGridlines() {
  var lines = d3.selectAll("g.x-axis g.tick")
    .select("line.grid-line")
    .remove();

  lines = d3.selectAll("g.x-axis g.tick")
    .append("line")
    .classed("grid-line", true)

  lines.attr("x1", 0)
    .attr("y1", 0)
    .attr("x2", 0)
    .attr("y2", -yAxisLength);
}

And add Transition!

  • Use the same chart setup for any data
  • .max is used to set the domain, and can be adjusted on the fly to dynamically adjust the chart when data changes
  • D3 provides many ways to simply add transitions
// initial yAxis declaration, scale previously established
yAxis = d3.svg.axis()
  .scale(scale)
  .tickSubdivide(1)
  .orient("left");

// On rescale, change domain to the new max value of dataset
yAxis.scale().domain([max, 0]);
svg.select("g.y-axis")
  .transition()      // <-- This makes everything smooth
    .duration(1000)  // But it was so easy!?
  .call(yAxis);

Axes and Grid lines

Randomly Rescale
01020304050607080901000102030405060708090100

Some other examples

  • This has just been the basic
  • Maps, motion, connections, line, spline, pie, etc..
  • Check out tons of implementations at d3js.org and github and here

Object-Identity

  • Keep object consistency throughout visualization
199
55
89
161
64
51
180
1
23
180
31
138
121
190
151
129
5
122
165
12

Bubble Chart

Update
012345678910012345678910

Topography

D3 and Drupal

  • Next steps:
  • What ways Drupal can output data in a convenient format
  • Make some simple prototypes within Drupal
D3 Data Driven Documents