Slide 1

Slide 1 text

Building Ambitious Data Visualisations IVAN VANDERBYL Co-founder Flood IO

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

en-AU

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

What is a data visualisation?

Slide 9

Slide 9 text

“Visual representations of abstract data to amplify cognition” — Readings in information visualization: using vision to think. Morgan Kaufmann, 1999.

Slide 10

Slide 10 text

What's the most natural tool for specifying a visualization?

Slide 11

Slide 11 text

A configurable chart?

Slide 12

Slide 12 text

High level charting library?

Slide 13

Slide 13 text

Low level geometric shapes and graphical markings?

Slide 14

Slide 14 text

efficiency expressiveness

Slide 15

Slide 15 text

Efficiency

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Configurable Charting Package® XP Pro

Slide 18

Slide 18 text

Configurable Charting Package® XP Pro

Slide 19

Slide 19 text

“If we endeavour to develop a charting instead of a graphing program, we will accomplish two things. First, we inevitably will offer fewer charts than people want. Second, our package will have no deep structure. Our computer program will be unnecessarily complex, because we will fail to reuse objects or routines that function similarly in different charts. And we will have no way to add new charts to our system without generating complex new code. Elegant design requires us to think about a theory of graphics, not charts.” — Leland Wilkinson, The Grammar of Graphics

Slide 20

Slide 20 text

“Elegant design requires us to think about a theory of graphics, not charts.”

Slide 21

Slide 21 text

Separation of concerns

Slide 22

Slide 22 text

series: [{ data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, {y: 216.4, marker: { fillColor: '#BF0B23', radius: 10 } }, 194.4] }] series: [{ data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, {y: 216.4, marker: { fillColor: '#BF0B23', radius: 10 } }, 194.1, 54.4] }] Data Presentation

Slide 23

Slide 23 text

Source: http://blog.codinghorror.com/the-php-singularity/

Slide 24

Slide 24 text

Single Responsibility Principle

Slide 25

Slide 25 text

scaleGridLineColor : "rgba(0,0,0,.05)", //Number - Width of the grid lines scaleGridLineWidth : 1, //Boolean - Whether to show horizontal lines (except X axis) scaleShowHorizontalLines: true, //Boolean - Whether to show vertical lines (except Y axis) scaleShowVerticalLines: true, //Boolean - Whether the line is curved between points bezierCurve : true, //Number - Tension of the bezier curve between points bezierCurveTension : 0.4, //Boolean - Whether to show a dot for each point pointDot : true, //Number - Radius of each point dot in pixels pointDotRadius : 4, //Number - Pixel width of point dot stroke pointDotStrokeWidth : 1, //Number - amount extra to add to the radius to cater for hit detection outside the drawn point pointHitDetectionRadius : 20, //Boolean - Whether to show a stroke for datasets datasetStroke : true, //Number - Pixel width of dataset stroke datasetStrokeWidth : 2, //Boolean - Whether to fill the dataset with a colour datasetFill : true, //String - A legend template legendTemplate : "
    -legend\"><% for (var i=0; i

Slide 26

Slide 26 text

import { technicalDebt } from "charting-package";

Slide 27

Slide 27 text

let chartConfig = { bigRedLine: true, bigRoundDotsOnLines: true, poniesEnabled: false, puppiesEnabled: true, thatFeatureMyBossAskedFor: true, notSureWhatThisOptionOptions: false, accuracy: “good” };

Slide 28

Slide 28 text

Expressiveness

Slide 29

Slide 29 text

var stations = []; // lazily loaded var formatTime = d3.time.format("%I:%M%p"); var margin = {top: 20, right: 30, bottom: 20, left: 100}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.time.scale() .domain([parseTime("5:30AM"), parseTime("11:30AM")]) .range([0, width]); var y = d3.scale.linear() .range([0, height]); var xAxis = d3.svg.axis() .scale(x) .ticks(8) .tickFormat(formatTime); var line = d3.svg.line() .x(function(d) { return x(d.time); }) .y(function(d) { return y(d.station.distance); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g")

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

d3-shape “A small JavaScript library for drawing geometric shapes commonly found in data visualizations.”

Slide 32

Slide 32 text

LIVE EXAMPLE: ec16.tomster.io/curves/linear

Slide 33

Slide 33 text

Live Example App: ec16.tomster.io/line-test

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

ember install ember-cli-d3-shape Use it with ember, today.

Slide 36

Slide 36 text

import { arc, pie } from 'd3-shape'; let arcFn = arc() .cornerRadius(8) .innerRadius(200) .outerRadius(232); let pieFn = pie().padAngle(5/360); arcFn(pieFn([80,20])[0]); arcFn(pieFn([80,20])[1]); //"M2.1271150941153607...

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

import { quantile } from 'd3-array'; quantile([0, 4, 7, 9, 12, 18, 22, 25, 28, 31], 0.95); // 29.65

Slide 39

Slide 39 text

import { deviation, extent, histogram, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, max, mean, median, min, permute, quantile, range, scan, shuffle, sum, ticks, transpose, variance, Zip, ... } from 'd3-array';

Slide 40

Slide 40 text

Theory of Graphics

Slide 41

Slide 41 text

"concurrency_mean": [ { "timestamp": 1450345920000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 200, "label": null }, { "timestamp": 1450345935000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 200, "label": null }, { "timestamp": 1450345950000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 400, "label": null }, { "timestamp": 1450345965000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 400, "label": null }, "response_time_mean": [ { "timestamp": 1450345920000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 1885, "label": null }, { "timestamp": 1450345935000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 2023, "label": null }, { "timestamp": 1450345950000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 1938, "label": null }, { "timestamp": 1450345965000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 1940, "label": null }, "transaction_rate_mean": [ { "timestamp": 1450345920000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 465, "label": null }, { "timestamp": 1450345935000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 1074, "label": null }, { "timestamp": 1450345950000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 1256, "label": null }, { "timestamp": 1450345965000, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 2090, "label": null },

Slide 42

Slide 42 text

Response Time

Slide 43

Slide 43 text

Response Time Concurrency

Slide 44

Slide 44 text

Response Time Concurrency Passed / RPM Failed / RPM

Slide 45

Slide 45 text

http://www.caltrain.com/schedules/PDF_Schedules.html

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

https://bl.ocks.org/mbostock/5544008

Slide 48

Slide 48 text

The correct representation doesn’t matter as much as the accuracy of the visualisation.

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

Fast & Furious 6 - Universal Pictures

Slide 52

Slide 52 text

You can’t exaggerate the presentation without disregarding the underlying data.

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

The Grammar of Graphics Wilkinson, Leland. The grammar of graphics. Springer Science & Business Media, 2006. 1. Data 2. Transforms 3. Scales 4. Coordinates 5. Elements 6. Guides

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

Empirical Data E.g. Events observed in the real world Abstract data E.g. Data generated by a modeling function. range(0, 100, 0.25), etc. Metadata E.g. Data about data

Slide 59

Slide 59 text

import Route from "ember-route"; import fetch from "ember-network/fetch"; export default Route.extend({ model() { return fetch("/api/metrics").then((response) => response.json()); }, setupController(controller, metrics) { controller.setProperties({ metrics }); } });

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

"response_time_mean": [ { "timestamp": 1450345920000.0, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 1885, "label": null }, { "timestamp": 1450345935000.0, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 2023, "label": null }, { "timestamp": 1450345950000.0, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 1938, "label": null }, { "timestamp": 1450345965000.0, "flood_id": 1697, "grid_id": 553, "project_id": 1, "value": 1940, "label": null }, [ [ 1450345920000, 1885 ], [ 1450345935000, 2023 ], [ 1450345950000, 1938 ], [ 1450345965000, 1940 ], [ 1450345980000, 1914 ], [ 1450345995000, 1996 ], [ 1450346010000, 1912 ], [ 1450346025000, 2171 ],

Slide 62

Slide 62 text

export default Controller.extend({ /** * Represents all the metrics we want to display. * Example: * {response_time_mean: [{timestamp: TIMESTAMP, value: VALUE, label...}, ...]} * * @type {Object} */ metrics: {}, /** * Computes response times collection containing [TIMESTAMP, VALUE] pairs. */ responseTimeValues: computed.map('metrics.response_time_mean.[]', (d) => [d.timestamp, d.value]), });

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

1500 54 SCALE Input domain=[1200,4000] Output domain=[0,500]

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

d3-scale github.com/d3/d3-scale

Slide 67

Slide 67 text

export default Component.extend(Coordinates, { values: [], xValues: computed.map('values.[]', (d) => new Date(d[0])), yValues: computed.mapBy('values.[]', 'lastObject'), xDomain: computedExtent('xValues.[]'), yDomain: computedExtent('yValues.[]'), xRange: computedExtent('plotArea.left', 'plotArea.width'), yRange: computedExtent('plotArea.top', 'plotArea.height'), xScale: computed('xRange', 'xDomain', { get() { const { xDomain: domain, xRange: range } = this.getProperties('xRange', 'xDomain'); return scaleTime().domain(domain).rangeRound(range); } }), yScale: computed('yRange', 'yDomain', { get() { const { yDomain: domain, yRange: range } = this.getProperties('yRange', 'yDomain'); return scaleLinear().domain(domain).rangeRound(range.reverse()); } }), });

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

Boilerplate

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

...

Slide 72

Slide 72 text

github.com/ivanvanderbyl/maximum-plaid

Slide 73

Slide 73 text

{{plaid-line values=values xScale=xScale yScale=yScale stroke="#2196F3" strokeWidth="2" fill="none" }}

Slide 74

Slide 74 text

{{#plaid-plot xScale yScale plotArea as |plot|}} {{plot.line values}} {{/plaid-plot}}

Slide 75

Slide 75 text

// components/plaid-plot/template.hbs {{yield (hash line=(component "plaid-line" xScale=xScale yScale=yScale x=plotArea.left y=plotA scatter=(component "plaid-scatter" xScale=xScale yScale=yScale x=plotArea.left y symbol=(component "plaid-symbol" x=plotArea.left y=plotArea.top) bottom-axis=(component "plaid-axis" orientation="bottom" scale=xScale top=plotAr )}}

Slide 76

Slide 76 text

{{#plaid-plot xScale yScale plotArea as |plot|}} {{plot.bottom-axis}} {{plot.line values stroke=stroke strokeWidth="2"}} {{#plot.scatter values as |x y|}} {{/plot.scatter}} {{/plaid-plot}}

Slide 77

Slide 77 text

LIVE EXAMPLE: ec16.tomster.io/timeline

Slide 78

Slide 78 text

{{plot.line values curve=(curve-step-after) }}

Slide 79

Slide 79 text

d3-shape features implemented: - [x] Lines (plaid-line) - [x] Symbols (plaid-symbol) - [ ] Areas (plaid-area) - [ ] Pie - [ ] Donut - [ ] Arc - [ ] Stack Layout - [-] Curves Additions: - [x] Computed data transforms https://github.com/ivanvanderbyl/maximum-plaid/issues/1

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

“All sufficiently ambitious applications will eventually require data visualisation”

Slide 83

Slide 83 text

Thank You

Slide 84

Slide 84 text

CODE: github.com/ivanvanderbyl/maximum-plaid DEMO: ec16.tomster.io SLIDES: ec16.tomster.io/slides NEXT: HallwayConf (Next to this room) Ember Community Slack “@ivan”