Data Visualization with D3

David Leonard

March 10th, 2016

David Leonard

David Leonard

What I do:

Grad Student 

Dev Games

Yahoo!

What is D3?

Data

- Driven

Documents

HTML

Hyper

Text

Markup 

Language

<tagname>content</tagname>

HTML

HTML

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
  </head>
  <body>
    <h1>My First Heading</h1>
    <p>My first paragraph.</p>
  </body>
</html>

HTML Attributes

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
  </head>
  <body>
    <h1 id='someID'>My First Heading</h1>
    <p  class='someClass'>My first paragraph.</p>
  </body>
</html>

DOM Tree

Document Object Model

CSS

Cascading

Style

Sheets


  p { color: red; font-size: 10px; }

CSS

css selector

property

value

CSS

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
    




  </head>
  <body>
    <h1>My First Heading</h1>
    <p>My first paragraph.</p>
  </body>
</html>
<style type='text/css'>
  p {
    color: red;
  }
</style>

External CSS

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
    
  </head>
  <body>
    <h1>My First Heading</h1>
    <p>My first paragraph.</p>
  </body>
</html>
p {
  color: red;
}

styles.css

<link rel='stylesheet' href='styles.css'/>

CSS ID Selector

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
    
  </head>
  <body>
    <h1 id='someID'>My First Heading</h1>
    <p>My first paragraph.</p>
  </body>
</html>
#someID {
  color: red;
}

styles.css

<link rel='stylesheet' href='styles.css'/>

CSS Class Selector

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
    
  </head>
  <body>
    <h1 class='someClass'>My First Heading</h1>
    <p>My first paragraph.</p>
  </body>
</html>
.someClass {
  color: red;
}

styles.css

<link rel='stylesheet' href='styles.css'/>

JavaScript

What can JavaScript do?

<!DOCTYPE html>
<html>
  <head>
    <title>DOM Manipulation example</title>
  </head>
  <body>
    <p>JavaScript is magic.</p>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <title>DOM Manipulation example</title>
  </head>
  <body>
    <p>JavaScript is magic.</p>

  </body>
</html>
<script type='text/javascript'>
   var paragraphs = document.getElementsByTagName('p');
   for(var i = 0; i < paragraphs.length; i++) {
     paragraphs[i].style.color = 'red';
   }
</script>

JavaScript is pretty cool!

...right?

Interactive Stories

credit: http://www.dangersoffracking.com/

Awesome Browser Games

Interactive Visualizations

EcmaScript

EcmaScript

Language Specification​​

EcmaScript 2016 (ES6)

Latest Version of JavaScript

Classes

Modules

Let / Const

Scope

EcmaScript 2016 (ES6)

JavaScript Crash Course

Functions

function multiply(x, y) {
   return x * y;
}

Function constructor

var multiply = function(x, y) {
   return x * y;
};

Anonymous Function

var multiply = function func_name(x, y) {
   return x * y;
};

Function in variable

Scope

var snack = 'Meow Mix';

function getFood(food) {
    if (food) {
        var snack = 'Friskies';
        return snack;
    }
    return snack;
}

getFood(false);
// undefined
// undefined

Scope

var snack = 'Meow Mix';

function getFood(food) {
    var snack;
    
    if (food) {
        snack = 'Friskies';
        return snack;
    }
    return snack;
}

getFood(false); 

Hoisting!

(A)

(B)

Scope Continued

function Person(name) {
    this.name = name;
}

Person.prototype.prefixName = function (arr) {
    return arr.map(function (character) {
        return this.name + character;
    });
}

Object Property

// Cannot read property 'name' of undefined

Scope Continued

function Person(name) {
    this.name = name;
}

Person.prototype.prefixName = function (arr) {
    var that = this;
    return arr.map(function (character) {
        return that.name + character;
    });
};
// Store this

Arrow Functions (ES6)

function Person(name) {
    this.name = name;
}

Person.prototype.prefixName = function (arr) {
    return arr.map((character) => this.name + character);
}

Arrow Functions (ES6)

return arr.map(function (character) {
  return that.name + character;
});
return arr.map((character) => this.name + character);

Objects

Just a collection of key / value pairs

var person = {
  firstName:'David', 
  lastName:'Leonard', 
  age:25, 
  eyeColor:'brown'
};
> person.firstName  // 'David'
> person.eyeColor   // 'brown

Method Chaining

var obj = {   
    first:  function() { alert('first');   return obj; },
    second: function() { alert('second');  return obj; },
    third:  function() { alert('third');   return obj; }
}
obj.first().second().third();

JavaScript is Aysnchronous!

Sync vs Async

var users = [];

$.get('/users', function(data) {
  users = data;
});

 

renderUsersOnPage(users);

Asynchronous Call

Callback Functions

$.get('/users', (data) => {
  var users = data;
  renderUsersOnPage(users);
});

Callback Hell

func1(function (value1) {
    func2(value1, function (value2) {
        func3(value2, function (value3) {
            func4(value3, function (value4) {
                func5(value4, function (value5) {
                    // Do something with value 5
                });
            });
        });
    });
});

Promises

new Promise(resolve => resolve(data))
    .then(result => console.log(data));
new Promise((resolve, reject) => 
    reject(new Error('Failed to fufill Promise')))
    .catch(reason => console.log(reason));

Promises

Promises

var userData = new Promise((resolve, reject) => {
  $.get('/users', (data) => {
    if (data) {
      resolve(data);
    } else {
      reject(new Error('Failed to fetch data'));
    }
  });
});

renderUsersOnPage(userData);

D3 Fundamentals

Selections

var paragraphs = document.getElementsByTagName('p');
 for(var i = 0; i < paragraphs.length; i++) {
  paragraphs[i].style.color = 'red';
}

.selectAll( )

d3.selectAll('p').style('color', 'red');

.select( )

d3.select('body').append('p').text('New paragraph!');
var body = d3.select('body');
var p = body.append('p');
p.text('New paragraph!');

Binding Data

d3.selectAll('p')
    .data([4, 8, 15, 16, 23, 42])
    .style('font-size', function(d) { return d + 'px'; });

Virtual Selections

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="d3.v2.min.js"></script>
  </head>
  <body>
  </body>
</html
var dataset = [ 1, 2, 3 ]

var p = d3.select('body').selectAll('p')
  .data(dataset)
  .enter()
  .append('p')
  .text('hello');

Virtual Selections

.enter( )

.update( )

.exit( )

Virtual Selections

var p = d3.select('body').selectAll('p')
  .data(dataset)
  .enter()

Virtual Selections

var p = d3.select('body').selectAll('p')
  .data(dataset)
  .enter()
  .append('p')

Virtual Selections

var p = d3.select('body').selectAll('p')
  .data(dataset)
  .enter()
  .append('p')
  .text('hello')

Virtual Selections

var p = d3.select('body').selectAll('p')
  .data(dataset)
  .enter()
  .append('p')
  .text('hello')

console.log(p);

Virtual Selections

var p = d3.select('body').selectAll('p')
  .data(dataset)
  .enter()
  .append('p')
  .text( function (d) { return d; } );

Virtual Selections

var p = d3.select('body').selectAll('p')
  .data(dataset)
  .enter()
  .append('p')
  .text( function (d, i) { return 'd=' + d + ' , i= ' + i ; } );

console.log(p);

Update and Exit

var numbers = [15, 8, 42, 4];
<div id="chart">
  <div class="bar" style="height: 15px; margin-top: 85px;"></div>
  <div class="bar" style="height: 8px; margin-top: 92px;"></div>
  <div class="bar" style="height: 42px; margin-top: 58px;"></div>
  <div class="bar" style="height: 4px; margin-top: 96px;"></div>
</div>
function update() {
  var selection = d3.select('#chart')
    .selectAll('.bar').data(numbers)
}

Update and Exit

function update() {
  var selection = d3.select('#chart')
    .selectAll('.bar').data(numbers)










}
    .style('height', function(d) { 
      return d; 
    })
    .style('margin-top', function(d) { 
      return 100 - d; 
    })

 

.on('click', function(e, i) {
  numbers.splice(i, 1);
  update();
});

Update and Exit

function update() {
  ...
  
  











  
};
selection.enter()
.append('div').attr('class', 'bar')
.style('height', function(d){ 
  return d; 
})
.style('margin-top', function(d){ 
  return 100 - d; 
})
.on('click', function(e, i){
  numbers.splice(i, 1);
  update();
});
selection.exit().remove();

Update and Exit

d3.select('#add-btn').on('click', function(e) {
  numbers.push(Math.round(Math.random() * 100));
  update();
});

Update and Exit

Scales

Scales are functions that map from an input domain to an output range.

Mike Bostock

Scales

var dataset = [ 100, 200, 300, 400, 500 ];

What if we had larger numbers?

Linear Scales

credit: http://www.jeromecukier.net/wp-content/uploads/2011/08/d3scale1.png

Linear Scale Construction

var scale = d3.scale.linear() 
  .domain([100, 500])
  .range([10, 350]);


> scale(100); // Returns 10 
> scale(300); // Returns 180 
> scale(500); // Returns 350

Scalable Vector Graphics

SVG Rectangle

<svg width='50' height='50'>
  <rect x='0' y='0' width='50' height='50' fill='green' />
</svg>

SVG Circle

<svg width='50' height='50'>
  <circle cx='25' cy='25' r='25' fill='purple' />
</svg>

SVG Ellipse

<svg width='50' height='50'>
  <ellipse cx='25' cy='25' rx='15' ry='10' fill='red' />
</svg>

SVG Lines

<svg width='50' height='50'>
  <line x1='5' y1='5' x2='40' y2='40' stroke='gray' stroke-width='5' />
</svg>

SVG Paths

<svg width='100' height='100'>
  <path d='
    
    
    
    
    stroke='red'
    stroke-width='2'
    fill='none' 
  />
</svg>

 

M 10 25
L 10 75
L 60 75
L 10 25'

SVG Groups

<svg width='200' height='200'>
 
   <circle cx='20' cy='20' r='20' fill='green' />
   <circle cx='70' cy='70' r='20' fill='purple' />
 
 
   <rect x='110' y='110' height='30' width='30' fill='blue' />
   <rect x='160' y='160' height='30' width='30' fill='red' />
 
</svg>
<g>
<g>
<g/>
<g/>

Axes

Constructing Axes

var svgContainer = d3.select('body').append('svg')
  .attr('width', 400)
  .attr('height', 100);
var axisScale = d3.scale.linear()
  .domain([0, 100])
  .range([0, 400]);
var xAxis = d3.svg.axis()
  .scale(axisScale);
var xAxisGroup = svgContainer.append("g")
  .call(xAxis);

Constructing Axes

Constructing Axes

var width = 500;
var height = 300;
var padding = 30;
        
var dataset = [
  [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
  [410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
];

Constructing Axes

var xScale = d3.scale.linear()
  .domain([0, d3.max(dataset, function(d) { return d[0]; })])
  .range([padding, width - padding * 2]);
var yScale = d3.scale.linear()
  .domain([0, d3.max(dataset, function(d) { return d[1]; })])
  .range([height - padding, padding]);
var rScale = d3.scale.linear()
  .domain([0, d3.max(dataset, function(d) { return d[1]; })])
  .range([2, 5]);

 

Constructing Axes

var xAxis = d3.svg.axis()
  .scale(xScale)
  .orient('bottom')
  .ticks(5);
var yAxis = d3.svg.axis()
  .scale(yScale)
  .orient('left')
  .ticks(5);

Constructing Axes

var svg = d3.select('body')
  .append('svg')
  .attr('width', width)
  .attr('height', height);

Constructing Axes

var svg = d3.select('body')
  .append('svg')
  .attr('width', width)
  .attr('height', height);

Constructing Axes

svg.selectAll('circle')
  .data(dataset)
  .enter()
  .append('circle')
  .attr('cx', function(d) {
    return xScale(d[0]);
  })
  .attr('cy', function(d) {
    return yScale(d[1]);
  })
  .attr('r', function(d) {
    return rScale(d[1]);
  });

Constructing Axes

svg.selectAll('text')
  .data(dataset)
  .enter()
  .append('text')
  .text(function(d) {
    return d[0] + ',' + d[1];
  })
  .attr('x', function(d) {
    return xScale(d[0]);
  })
  .attr('y', function(d) {
    return yScale(d[1]);
  })
  .attr('font-family', 'sans-serif')
  .attr('font-size', '11px')
  .attr('fill', 'red');

Constructing Axes

svg.append('g')
  .attr('class', 'axis')
  .attr('transform', 'translate(0,' + (height - padding) + ')')
  .call(xAxis);
svg.append('g')
  .attr('class', 'axis')
  .attr('transform', 'translate(' + padding + ',0)')
  .call(yAxis);

Constructing Axes

Thanks!

D3 Resources