Learn D3.js in 90 minutes
-
Upload
jos-dirksen -
Category
Data & Analytics
-
view
438 -
download
3
Transcript of Learn D3.js in 90 minutes
LEARNING D3.JS IN 90MINUTES
THIS SESSION~ WHAT ARE WE GOING TO DO ~
BEFORE WE STARTGet the code from:
You need a webserver for some of the examples:IntelliJ supports this (right click on HTML and run)Easiest is with python: check if you can run:
https://github.com/josdirksen/d3exercises
python -m SimpleHTTPServer (for Python 2) python3 -m http.server (for Python 3)
If not download the mongoose webserver, and you have
WHO AM ILove writing, and talking about technology (frontend as well
as backend). Writing about stuff for Packt and Manning
Twitter: Github: Contact me: Blog at:
@josdirksenhttp://www.github.com/josdirksen
Plug: Expert Data Visualization with D3.js (Q1 2017)
WHAT ARE WE GOING TO DOI'll introduce a subject (couple of minutes)I'll Explain the exerciseYou can fill in the blanks in the provided code.
// Step 2. We can also select multiple elements. Use d3.select and // d3.selectAll to see the difference when selecting all the // <li> elements, and use either classed or attr to set the class// of these elements to 'step2'.
// d3.selectAll(...).classed(..., ...)
Halfway during the exercise I'll push an answer to git andDon't hesitate to ask questions!
WE'RE GOING TO TRY AND COVER...Learn how D3.js binds data to DOM elements.Make basic charts and diagramsVisualize hierarchical dataShow connected graphsCombine gis/geo information with opendataMake art with voronois
WHAT AREN'T WE GOING TO COVERD3.js is big, very big. We'll explore the basics for data
visualization, but there is a whole lot more. Most importantstuff we skip:
User interaction, mouse events, drag events.AnimationsZooming & draggingStreaming dataloading and queue dataTypescript, ES6, Scale.js bindings...
D3.JS INTRODUCTION
WHAT IS D3.JS"D3.js is a JavaScript library for manipulating documentsbased on data. D3 helps you bring data to life using
. D3's emphasis on web standards gives you thefull capabilities of modern browsers without tying yourself toa proprietary framework, combining powerful
and a to DOMmanipulation." --- d3js.org
HTML,SVG, and CSS
visualizationcomponents data-driven approach
WHAT IS IT NOTNot a charting library: But provides the building blocks(see nvd3.js, Rickshaw.js, c3.js).Not a drawing library: Doesn't draw data itself, uses SVGfor visualization.Not an SVG abstraction layer: Manipulates the DOM andSVG directly.Not a Canvas/WebGL library: Minimal support for Canvas,for WebGL use Three.js.Not a framework: Can be used with Angular 1/2, React,Vue.js, , is just a (very extensive) library.
ONLINE INFORMATIONLots of online resources,
Most for v3. current version is v4.API:
Bit overwhelming..But won't be in about 80 minutes
https://github.com/d3/d3/blob/master/API.md
WORK BY EXAMPLEUsually easier to work from examplesBest starting point: Most examples still use v3 however..
https://bl.ocks.org/
HELLOWORLD OF D3.JS
HELLOWORLD OF D3.JSGoal of this first example, is to learn how you can select
elements with D3.js, append new ones, remove elements, andset basic properties on the elements.
SELECTIONSd3.select and d3.selectAll
Uses the W3C selection syntax: uses querySelectorand querySelectorAll
Examples:d3.selectAll('p') // Select all p elements in the document. d3.select('p') // Select first p element in the document. // Selects 1st span element of all <p> in the document d3.selectAll('p').select('span') d3.select('#id') // select on id d3.select('.classname') // select on css class
WE CAN CHANGE THE PROPERTIESOFF THESE SELECTIONS
attr(), style(), html(), text(), classed()
Value can also be a function:
d3.selectAll('img.thumb').attr('height', 50) d3.selectAll('span').style('color', 'steelblue') d3.selectAll('span').html('<strong>d3.js</strong>') d3.selectAll('span').text('content') // conditionally set or remove classes d3.select('div').classed('container', true)
// d is bound data, i is the index d3.selectAll('img').attr('src', function(d, i) { return 'image' + i + '.png'; });
MODIFY THE SELECTIONappend(): Append child to each element of selection
remove(): Remove all the elements in the selection
insert(): Insert an element before another element
d3.selectAll('p').append('span').
// remove the first li of every lu d3.selectAll('ul').select('li').remove()
// insert a new span as the first element of the p d3.selectAll('p').insert('span', ':first-child');
Selections are immutable, these actions return newselections, so we can chain them!
BUT JQUERY CAN DO...With you manipulate elements from a selection,with you bind data (next example)
has many data visualization utils and APIs and focusses on web app related plugins.
But there is a big overlap
both are librarieshave and use a .
Similar to D3.js: Raphael.js and Processing.js
jQueryD3.js
D3.jsjQuery
DOM manipulationCSS/W3C selectors
fluent API
LET'S GET STARTED$ git clone https://github.com/josdirksen/d3-exercises $ python -m SimpleHTTPServer
Once again, if you don't have python, you can usemongoose:
View at http://localhost:8000/src/exercise-01/E01.html
EXERCISE 1: LEARNING D3.JSFollow steps described in:<ROOT>/src/exercise-01/js/E01.js
Use d3.select(), d3.selectAll(), class(),attr(), append(), remove(), html(), text()If needed reference D3.js select API:
You'll get approximately 5 minutes.https://github.com/d3/d3-selection
Should you have question? Please ask.
USE DATABINDINGWITH D3.JS
D3.JS DATA BINDINGLearn how to create data driven document by usin the basicselect(), enter(), merge(), remove() cycle when
binding data to a selection
BASIC DATABINDINGBind data to a selection with data(...)
<!-- Input Dom --> <span></span> <span></span> <span></span> <span></span>
var values=['a', 'b', 'c']; d3.selectAll('span') .data(values) .text(function(d) {return d});
<!-- Output Dom --> <span>a</span> <span>b</span> <span>c</span> <span></span>
DATA DRIVEN ELEMENTSAssume an empty DOM
var values=['a', 'b', 'c']; var selection = d3.selectAll('span').data(values)
A selection is actually more than just the found elements
enter(): placeholder selection for unbound data // for all unbound data append a span attribute selection.enter().append('span').text(function(d) {return d});
exit(): placeholder selection for le�over elements // remove all the leftover elements selection.exit().remove()
LEADS TO THIS PATTERNfunction update(values) { // select all the current span elements and assign the data var selection = d3.selectAll('span').data(values) // for the unbound data, create a new span, and attach a class var newElements = selection.enter() .append('span') .attr('class', 'someClass');
// Merge combines two selections. For the combined selection // set the value of the span to the bound datum. newElements.merge(selection).text(function(d) {return d});
// and remove the now obsolete elements selection.exit().remove(); }
This is the basic pattern you use to bind data, add new DOMelements, updated existing ones, and remove obsolete ones.
WHICH PROVIDES US WITHAn easy way to let our data determine our elements.Reduces unnecessary creation of elements.Allow all kinds of hooks e.g. for animations.
// assume values contains some random numbers function update(values) { var selection = d3.selectAll('circle').data(values) var newElements = selection.enter() .append('circle') .attr('class', 'someClass') .attr('cx', function(d, i) {return i * 50}) .attr('cy', function() {return 50}) .merge(selection) .transition().duration(2000) .attr('r', function(d, i) {return d});
// and remove the now obsolete elements selection.exit().remove(); }
EXERCISE 2: GOAL
EXERCISE 2: DATABINDINGFollow steps described in:<ROOT>/src/exercise-02/js/E02.js
Use (same as previous) and enter(), exit(),append(), remove()If needed reference D3.js select API:
If time permits look at transistions API:
You'll get approximately 5 minutes.
https://github.com/d3/d3-selection
https://github.com/d3/d3-selection
Should you have question? Please ask.
BASIC CHARTS ANDDIAGRAMS
BASIC CHARTS AND ELEMENTSLearn how to create basic charts and elements using scales
and generators.
SVG, GROUPS AND POSITIONINGAbsolute positioning:
<rect x="100", y="100"></rect> <circle cx="100", cy="100"></circle>
Provides a g element for grouping
g doesn't have an x or y like attribute, uses transformAll attributes on this element apply on children<g transform="translate(50 200)">...</g> <!-- positioning --> <g transform="scale(0.5 0.3)">...</g> <!-- sizing --> <g transform="rotate(45)">...</g> <!-- rotate the g -->
These actions can be combinedBut positioning is still difficult
POSITIONING WITH SCALESA scale translates an input domain to an output rangeDifferent scales ( )
: continuous input to continuous outputscalePow, scaleLog, scaleTime
: continuous input to interpolatorscaleQuantize: continuous input to discrete rangescaleQuantile: sampled input to discrete rangescaleThreshold: scaleQuantize with thresholdsscaleOrdinal: discrete input to discrete range
, scalePointLots of useful helper functions
https://github.com/d3/d3-scalescaleLinear
scaleSequential
scaleBand
SCALELINEAR: CONTINUOUS INPUTTO CONTINUOUS OUTPUT
var x = d3.scaleLinear() .domain([10, 130]) // e.g the min and max of your data .range([0, 960]); // the width of your chart
x(20); // 80 x(50); // 320
var color = d3.scaleLinear() .domain([10, 100]) .range(["brown", "steelblue"]);
color(20); // "#9a3439" color(50); // "#7b5167"
WHAT CAN WE INTERPOLATE?The domain must be numbers, the range can be any of this:
And if this doesn't match, you can create your own.
SCALESEQUENTIAL: CONTINUOUSINPUT TO INTERPOLATOR
var color = d3.scaleSequential(d3.interpolatePlasma).domain([0, 100]);color(30); // #8f0da4 color(60); // #e16462 color(80); // #fca636
Many interpolators available:
HOW WOULD YOU USE THIS?// say our values contains data from 1984 to 2014 var min = d3.min(values, function (d) { return d.value; }); var max = d3.max(values, function (d) { return d.value; }); // evenly divide the years along the xAxis var xScale = d3.scaleLinear().domain([1984,2014]).range([0, 1080]); // evenly divide the values along the yAxis var yScale = d3.scaleLinear() .domain([min, max]).range([0, 700]); // get a color var col = d3.scaleSequential(d3.interpolatePlasma).domain([0, 100]);
d3.selectAll("rect").data(values).enter() .append("rect") .attr("x", xScale) // xScale = (d: any) => Numeric .attr("y", function(d) {return yScale(d)}) // alternatively .attr("fill", col)
SCALES AND AXISD3 provides easy way to create an axis:
https://github.com/d3/d3-axisvar min = d3.min(values, function (d) { return d.value; }); var max = d3.max(values, function (d) { return d.value; });
var yScale = d3.scaleLinear() .domain([min, max]).range([0, 700]);
// s denotes that we want to use international system of units // to display axis values var bottomAxis = d3.axisBottom().scale(yScale).ticks(20, "s");
Results in:
EXERCISE 3: GOAL
BEFORE WE STARTD3.js support easy loading csv, tsv, json, xml
name,sex,amount Emma,F,20355 Olivia,F,19553 ...
d3.csv('data/yob2015.txt', function (d) { return { name: d.name, sex: d.sex, amount: +d.amount }; }, function (data) { }
ALSO FOR MULTIPLE FILESd3.queue() .defer(d3.json, "./data/world-110m.v1.json") .defer(d3.csv, "./data/worldbank_popular_2014.csv") .defer(d3.csv, "./data/iso-mapping.csv") .await(function (error, topoData, worldbank, mapping) { }
EXERCISE 3: WORKING WITH SCALESFollow steps described in:<ROOT>/src/exercise-03/js/E03.js
If needed reference D3.js APIs:
You'll get approximately 8 minutes.
https://github.com/d3/d3-scalehttps://github.com/d3/d3-axis
Should you have question? Please ask.
USING SVG FOR CHARTSSVG provides a large number of elements:
But limited primitive shapes: <circle>, <ellipse>,<line>, <path>, <polygon>, <polyline>, <rect>
https://developer.mozilla.org/en-US/docs/Web/SVG
THE PATH ELEMENT IS VERSATILEthe d attribute describes a path:
<path class="arc" d="M136.86141570725613,-17.69047457265137A138,138, 0,0,1,124.60150267951192,59.31665474390461L62.948628653284814,28. 257214134993035A69,69,0,0,0,68.61145448511735,-7.312203049469534Z" style="fill: rgb(252, 160, 130);"></path>
M, L, A, C, A, Q, T ... for lines, arcs, curvesHard to determine the correct values yourselfD3.js provides generators for complex shapes
THE ARC GENERATORWe'll create an arc segment, a part of a pie chart
var arc = d3.arc() .outerRadius(height/2 * 0.6).innerRadius(height/2 * 0.3); // create the right half of a pie chart arc({ startAngle: 0, endAngle: Math.PI });
// "M1.469576158976824e-14,-240A240,240,0,1,1,1.469576158976824e-14, // 240L7.34788079488412e-15,120A120,120,0,1,0,7.34788079488412e-15, // -120Z"
COMBINED WITH THE PIE FUNCTIONPiechart: d3.pie() to generate config for d3.arc
var arc = d3.arc() .outerRadius(height/2 * 0.6).innerRadius(height/2 * 0.3); var data = [{count:10}, {count:20}, {count:30}] var pie = d3.pie() .padAngle(0.04) .value(function (d) { return d.count; });
var arcs = pie(data) // "[{"data":{"count":10},"index":2,"value":10, // "startAngle":5.215987755982988, // "endAngle":6.283185307179586,"padAngle":0.04}, // {"data":{"count":20},"index":1,"value":20, // "startAngle":3.121592653589793, // "endAngle":5.215987755982988,"padAngle":0.04} ... selectAll("path").data(arcs).enter() .append("path").attr("d", arc);
ANOTHER STANDARD PATTERN1. Define generator which creates paths based on properties.
(d3.arc)2. Define generator which creates config for other generators
(d3.pie)3. Pass data to step 2, result is enriched data with config.4. Use the normal selectAll(), enter(), merge(),exit() pattern
EXERCISE 4: GOAL
EXERCISE 4, CREATE A PIE CHARTFollow steps described in:<ROOT>/src/exercise-04/js/E04.js
If needed reference D3.js APIs:
You'll get approximately 5 minutes.https://github.com/d3/d3-shape
VISUALIZING TREESTRUCTURES
TREE AND HIERARCHIESSee what is possible in d3 to visualizes nested trees of data
D3.JS SUPPORTS TREE DATAAPI: Many standard visualizations: d3.tree()
https://github.com/d3/d3-hierarchy
D3.CLUSTER()Same as the tree, but leaves are at the same position
D3.TREEMAP()Supports different clustering algorithms
D3.PACK()Same as d3.treemap, but with circles
OR YOUR CUSTOM IMPLEMENTATION
Sample
ALL FOLLOW SAME APPROACH1. Load the data (d3.csv,d3.tsv,d3.json etc.)2. Convert the data into a tree stucture3. Pass it through a generator4. Use the output from the generator to draw the chart
NESTED DATA: D3.STRATISFYd3.stratisfy() can follow ids in your data
id,parentId,name,description 180580,,Felidae,cats 180581,552363,Lynx,lynxes 180582,180581,rufus,Bobcat 180583,180582,rufus,bobcat 180584,180581,lynx,Eurasian Lynx 180585,180581,canadensis,Canada lynx`
var stratify = d3.stratify(); var root = stratify(data);
NESTED: D3.NEST / D3.HIERARCHYd3.nest() can group data (multiple times) based on a key
"Country (en)";"Country (de)";"Country (local)";"Country code";"Continent""Afghanistan";"Afghanistan";"Afganistan/Afqanestan";"AF";"Asia"; "Egypt";"Ãgypten";"Misr";"EG";"Africa";
var entries = d3.nest() .key(function (d) {return d.Continent; }) .entries(data);
var root = d3.hierarchy({values: entries}, function(d) { return d.values; })
USE A GENERATORWith data in the correct structure, we can use a generator// normal node tree var tree = d3.tree() .size([height, width]) .separation(function(a, b) { return (a.parent === b.parent ? 5 : 13) });
// create a treemap var tree = d3.treemap() .size([width, height]) .padding(2) .tile(d3.treemapSquarify.ratio(1))
// enrich the root tree(root)
WHICH ENRICHES THE STRUCTURE
root.leaves() // return all the nodes without children root.descendants() // return array of this and all descendants
chart.selectAll(".node") .data(root.descendants()) .enter(..)
EXERCISE 5: GOAL
EXERCISE 5, CREATE A TREEMAPFollow steps described in:<ROOT>/src/exercise-05/js/E05.js
If needed reference D3.js APIs:
You'll get approximately 8 minutes.https://github.com/d3/d3-hierarchy
VISUALIZES GRAPHSOF DATA
VISUALIZE GRAPHSQuick introduction on different ways how to use the force,
hord, and matrix layouts to visualize a graph of data
FORCE LAYOUT FOR GRAPHSAPI: A graph is a set of nodes.Define forces which are applied:
to nodesto links
Run a simulation
https://github.com/d3/d3-force
CAN APPLY DIFFERENT FORCESforceCenter: keeps nodes in the centerforceCollide: nodes have a radius, avoid collisionsforceLink: push/pull nodes together based on distanceforceManyBody: simulate attraction or repulsionbetween nodesforceX: force nodes towards specific positionforceY: force nodes towards specific position
BASIC SETUPvar graph = ...
// define forces var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id; })) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(width / 2, height / 2));
// run a simulation simulation .nodes(graph.nodes) .on("tick", ticked);
// do something on each tick function ticked() {...}
FORCE LAYOUT MULTIPLE FORCES
~ example ~
ALTERNATIVE: CHORD DIAGRAM
~ example ~
ALTERNATIVE: MATRIX DIAGRAM
~ example ~
EXERCISE 6: GOALPlay around with the different forces and see the effect.
EXERCISE 6, APPLY FORCESFollow steps described in:<ROOT>/src/exercise-06/js/E06.js
If needed reference D3.js APIs:
You'll get approximately 5 minutes.https://github.com/d3/d3-force
GIS AND GEO DATA
GEOD3.js has extensive support for working with geo data. We'll
explore different ways of visualizing and interacting with geodata.
BEST PART OF D3.JSAt least for me..
CAUSE IT LOOKS NICE
AND INFORMATIVE
AND COLOURFUL
QUICK NOTE ON PROJECTIONSProjection defines how the coordinates from the source areprojected to a flat canvas. The projection:Sinu Mollweide
Example
CREATING A MAPGoing to explain how to create this:
Example
HOW TO LOAD DATAMost common format for GIS data is ArcGIS shapefile.
binary formatUse QGis, Mapshaper, OGR to convert to open formatsD3.js can work with:
GeoJSON: A standard supported by many applications.TopoJSON: A specific optimized format for smaller files.
GEOJSON AND TOPOJSON FORMATSCan contain multiple geometries.Each geometry is described (usually) as a path.Can contain additional properties
population, unemployment rate, etc.one file for everything
Try to work with TopoJSON, since it's much smaller{"type":"Topology","objects":{"cb_2015_us_county_500k": {"type": "GeometryCollection","geometries":[{"type":"Polygon","properties": {"GEOID":"01005","NAME":"Barbour"},"id":"01005","arcs":[[0,1,2,3 ,4,5,6,-7,6,7,8,9,10,11,12,13,14 ...
WORKING WITH TOPO DATA IN D3.JS(pretty much the same way as we did before)
1. Setup a path generator and projection2. Load the data3. Use the projection to generate path segments
LOAD THE DATA(this is prepared data, where value contains percentage of
internet users){"type":"Topology","objects":{"countries":{"type":"GeometryCollection" "geometries":[{"type":"Polygon","id":"004","arcs":[[0,1,2,3,4,5]], "properties":{"value":"7","countryA":"AFG","name":"Afghanistan"}}, {"type":"MultiPolygon","id":"024","arcs":[[[6,7,8,9 ....
d3.json("./data/world-110m-inet.json", function(loadedTopo) { // by using topojson.feature, we convert the topoJson to geojson, // where each country is a single feature in the features array. countries = topojson.feature(loadedTopo, loadedTopo.objects.countries).features; });
WHEN LOADED THE DATA LOOKSLIKE THIS
SETUP THE PATH GENERATORvar projection = d3.geoNaturalEarth() var path = d3.geoPath().projection(projection)
For all the supported projections see:
Pretty much any projection you can think offRelatively easy to create your own one
https://github.com/d3/d3-geo/https://github.com/d3/d3-geo-projection
WITH A GENERATOR AND THE DATAvar projection = d3.geoNaturalEarth() var path = d3.geoPath().projection(projection)
d3.json("./data/world-110m-inet.json", function(loadedTopo) { countries = topojson.feature(loadedTopo, loadedTopo.objects.countries).features;
svg.selectAll('.country').data(countries).enter() .append("path") .classed('country', true) .attr("d", path); }
And that's it..
AND YOU'RE DONE!
But colors?
ADD A SIMPLE SCALEvar color = d3.scaleSequential(d3.interpolateGreens).domain([0,100]) var projection = d3.geoNaturalEarth() var path = d3.geoPath().projection(projection)
d3.json("./data/world-110m-inet.json", function(loadedTopo) { countries = topojson.feature(loadedTopo, loadedTopo.objects.countries).features;
svg.selectAll('.country').data(countries).enter() .append("path") .classed('country', true) .attr("d", path); .attr("fill", function(d) {return d.properties.value ? color(+d.properties.value) : '#ccc'}); }
EASY RIGHT?
EXERCISE 7: GOALRender the US election
EXERCISE 7, RENDER THE USELECTION RESULTS
Follow steps described in:<ROOT>/src/exercise-07/js/E07.js
If needed reference D3.js APIs:
You'll get approximately 10 minutes.
https://github.com/d3/d3-geo/https://github.com/d3/d3-geo-projection
D3.JS AND GENERATIVEART
NOT EVERYTHING SHOULD HAVE AMEANING
D3.JS SUPPORTS VORONOISIn mathematics, a Voronoi diagram is a partitioning of a planeinto regions based on distance to points in a specific subset ofthe plane. That set of points (called seeds, sites, or generators)
is specified beforehand, and for each seed there is acorresponding region consisting of all points closer to that
seed than to any other.
VORONOI
MUST GO DEEPER
MUST GO DEEPER
AND DEEPER
AND DEEPER
ANY DEEPER BREAKS MY BROWSERThis is rendered as SVG elementsWe could output to canvas as well
EXERCISE 7, PLAY AROUND WITHTHE VORONOI CODE
Not really an exercise, but allows you to experiment withvoronois.
Follow steps described in:<ROOT>/src/exercise-08/js/E08.js
@josdirken ~ Exercises for this session: https://github.com/josdirksen/d3exercises
THANK YOU!