Dr. Luciano Nocera
Infographics (a clipped compound of "information" and "graphics") are graphic visual representations of information, data or knowledge intended to present information quickly and clearly. They can improve cognition by utilizing graphics to enhance the human visual system's ability to see patterns and trends.
-- Wikipedia infographic
A dashboard is a type of graphical user interface which often provides at-a-glance views of key performance indicators (KPIs) relevant to a particular objective or business process. In other usage, "dashboard" is another name for "progress report" or "report.".
-- Wikipedia Dashboard (business)
Figuration-AbstractionMeasures the distance fromreferent referent is the thing that a graphical form or mark stands for. to the representation representation is the graphical form or mark |
$ | |
Decoration-FunctionalityMeasures the amount of informative content |
||
Lightness-DensityMeasures the amount of content displayed in relation to space |
||
Unidimensionality-MultidimensionalityMeasures the number of layers and forms used to encode the data |
||
Familiarity-OriginalityMeasures how challenging the forms are for the user to understand |
||
Redundancy-NoveltyMeasures the number of times things are explained |
Javascript object
<TABLE>
<TBODY>
<TR>
<TD>Col 1</TD>
<TD>Col 2</TD>
</TR>
<TR>
<TD>Col 3</TD>
<TD>Col 4</TD>
</TR>
</TBODY>
</TABLE>
display: [inline|block]
propertyblock
or inline
span is <span>inline</span>
span is inline
div is <div>block</div>
div can become <div style="display: inline">inline</div>
id
, class
, style
)
<!-- Use id to reference containers for dynamic charts -->
<div id="chart1"></div>
<svg id="chart2"></svg>
<style>
div.bar { background-color: red; }
circle.dot { fill: red; }
</style>
<!-- Use class to apply common styles -->
<div class="bar" style="width: 600px">bar 1</div>
<svg style="background-color: lightpink">
<circle class="dot" cx="5" cy="5" r="2"/>
</svg>
<!-- Use element specific attributes to place and size -->
<img src="pict.png" width="20px"\>
<svg style="background-color: lightpink">
<circle cx="5" cy="5" r="2"/>
</svg>
style
for coloring & box sizing
background-color: brown; /* background color of an element */
color: white; /* color of text within element */
padding: 1em; /* Apply to all four sides */
padding: 5% 10%; /* vertical | horizontal */
padding: 1em 2em 2em; /* top | horizontal | bottom */
padding: 5px 1em 0 2em; /* top | right | bottom | left */
border: 1px solid red; /* width | style | color */
margin: 1em; /* Apply to all four sides */
margin: 5% auto; /* vertical | horizontal */
margin: 1em auto 2em; /* top | horizontal | bottom */
margin: 2px 1em 0 auto; /* top | right | bottom | left */
style
for coloring & box sizing (example)
before
<div style="color: yellow; background-color:blue; margin: 100px 100px; padding: 50px 50px; border: 10px solid red; ">DIV</div>
after
style
for sizing
<div style="background-color: red; margin-bottom: 10px;">bar1</div>
<div style="background-color: red; margin-bottom: 10px; width: 200px; height: 20px;">bar2</div>
<div style="background-color: red; width: 100px; height: 20px;">bar3</div>
<style>
.redbar {
background-color: red;
height: 50px;
margin-bottom: 10px;
}
</style>
<div style="height: 20px; font-size: 0.5em; font-weight: bolder;">
<div class="redbar">bar1</div>
<div class="redbar" style="width: 200px;">bar2</div>
<div class="redbar" style="width: 100px;">bar3</div>
</div>
HTML | SVG | |
---|---|---|
Placement & sizing | CSS box model relative/absolute placement | Cartesian coordinate system and viewport |
Primitives | CSS box model boxes | Named shapes, curves |
Text | inner HTML | <text> |
Drawing order | CSS box model / DOM order | Painter's algorithm coding order → depth order |
Hierarchy | CSS box model | Nesting using <g> |
<rect x="10" y="10" width="80" height="80" rx="5" ry="5" fill="orange"/>
<circle cx="150" cy="50" r="40" fill="green"/>
<ellipse cx="260" cy="50" rx="50" ry="25" fill="brown"/>
<line x1="320" y1="20" x2="380" y2="80" stroke="blue"/>
<polyline points="420,35 490,65 490,35 420,65" stroke="red" fill="none"/> <!-- open -->
<polygon points="560,10 600,30 600,70 560,90 520,70 520,30" fill="tan"/> <!-- closed -->
0
blackfor shapes and
nonefor text
none
<rect x="10" y="10" idth="80" height="80" rx="5" ry="5" fill="orange"/> <!-- width="0" -->
<circle x="150" y="50" r="40" fill="green"/> <!-- cx="0" cy="0"-->
<ellipse cx="260" cy="50" rx="50" ry="25" background-color="brown"/> <!-- fill="black" -->
<line x1="320" y1="20" x2="380" y2="80" color="red"/> <!-- stroke="none" -->
<polyline points="420,35 490,65 490,35 420,65" stroke="myred" fill="none"/> <!-- stroke="none" -->
<polygon oints="560,10 600,30 600,70 560,90 520,70 520,30" fill="tan"/> <!-- points="" -->
<text x="600">Hello</text> <!-- text element requires a y, here y="none" -->
MOST BUT NOT ALL SVG attributes have CSS styling properties
<svg>
<rect style="x: 10; y: 10; width: 80; height: 80; rx: 5; ry: 5; fill: orange;"/>
<circle style="cx: 150; cy: 50; r: 40; fill: lime; stroke:orange; stroke-width: 8px;"/>
<ellipse style="cx: 260; cy: 50; rx: 50; ry: 25; fill: brown;"/>
<line style="x1: 320; y1: 20; x2: 380; y2: 80; stroke: blue;"/>
<polyline style="points: 420,35 490,65 490,35 420,65; stroke: red; fill: none;"/>
<polygon style="points: 560,10 600,30 600,70 560,90 520,70 520,30; fill: tan;"/>
</svg>
<svg>
<text x="0" y="70">Text0<text/>
</svg>
x, y
define where the text is placed in the SVG (by default the text bottom left is located at x, y
)x, y
text-anchor: start ← default text-anchor: middle text-anchor: end
|
x, y
alignment-baseline: hanging alignment-baseline: middle alignment-baseline: unset ← default
|
<svg>
<text x="0" y="70">Text0<text/>
<text x="200" y="70" text-anchor="middle" alignment-baseline="middle">Text1<text/>
<text x="350" y="70" style="text-anchor: middle; alignment-baseline: middle;">Text2<text/>
</svg>
<svg>
<polygon points="60,10 100,30 100,70 60,90 20,70 20,30" fill="tan">text</polygon>
</svg>
<svg>
<circle cx="100" cy="100" fill="red"/>
<text>Hello</text>
</svg>
<svg style="background-color: lightgrey">
<rect A="100" B="0" C="50" D="200" fill="red"/>
</svg>
TL;DR. JavaScript library to bind data to the DOM, i.e., vocabulary of graphical marks come directly from web standards: HTML, SVG, and CSS.
To generate a graphic:
- Get the data (properly formatted as an array!)
- Use D3 to map the data to HTML elements
- Leave-it to the browser to do the rest!
Basic mechanisms to map data to HTML elements:
- Select & append
- Data join
Paradigm | Imperative | Declarative |
---|---|---|
Properties |
|
|
Examples | Google Charts Matplotlib WebGL |
D3 ggplot2 |
// https://d3js.org Version 5.5.0. Copyright 2018 Mike Bostock.
(function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})})(this,function(t){"use strict";function n(t,n){return t<n?-1:t>n?1:t>=n?0:NaN}function e(t){return 1===t.length&&(t=function(t){return function(e,r){return n(t(e),r)}}(t)),{left:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r<i;){var o=r+i>>>1;t(n[o],e)<0?r=o+1:i=o}return r},right:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r<i;){var o=r+i>>>1;t(n[o],e)>0?i=o:r=o+1}return r}}}function r(t,n){return[t,n]}function i(t){return null===t?NaN:+t}function o(t,n){var e,r,o=t.length,a=0,u=-1,f=0,c=0;if(null==n)for(;++u<o;)isNaN(e=i(t[u]))||(c+=(r=e-f)*(e-(f+=r/++a)));else for(;++u<o;)isNaN(e=i(n(t[u],u,t)))||(c+=(r=e-f)*(e-(f+=r/++a)));if(a>1)return c/(a-1)}function a(t,n){var e=o(t,n);return e?Math.sqrt(e):e}function u(t,n){var e,r,i,o=t.length,a=-1;if(null==n){for(;++a<o;)if(null!=(e=t[a])&&e>=e)for(r=i=e;++a<o;)null!=(e=t[a])&&(r>e&&(r=e),i<e&&(i=e))}else for(;++a<o;)if(null!=(e=n(t[a],a,t))&&e>=e)for(r=i=e;++a<o;)null!=(e=n(t[a],a,t))&&(r>e&&(r=e),i<e&&(i=e));return[r,i]}function f(t){return function(){return t}}function c(t){return t}function s(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r<i;)o[r]=t+r*e;return o}function l(t,n,e){var r,i,o,a,u=-1;if(n=+n,t=+t,e=+e,t===n&&e>0)return[t];if((r=n<t)&&(i=t,t=n,n=i),0===(a=h(t,n,e))|...
(function() {})();
//without chaining
obj.method3(obj.method2(obj.method1()));
//same
var s = obj.method1();
s = s.method2();
s = s.method3();
//with chaining
obj.method1()
.method2()
.method3();
this:
var obj = {
method1: function() {
console.log('method1');
return this; //this
refers to the current object instance
},
method2: function(a) {
console.log('method2');
return this;
}
};
obj.method1().method2();
method1
method2
select()
and selectAll()
return a selection
var selection = d3.select(selector); //select first matching element in the document
var selection = d3.selectAll(selector); //select all matching elements in the document
//use chaining to select on the selection object
var selection = selection.select(selector); //select first matching element in the selection
var selection = selection.selectAll(selector); //select all matching elements in the selection
var s = d3.select('body'); //selects <body> in <html>
s.select('p'); //selects first <p> in <body>
d3.select('body') //same as above using chaining
.select('p');
d3.selectAll('p'); //selects all <p> in parent
d3.select('#chart'); //selects first element in parent with id="chart"
d3.selectAll('.red'); //selects all elements in parent with class="red"
selection.append(type)
appends a new element to the last child of each selected elementselection.insert(type[, before])
inserts a new element before
the first element matching the specified before selector for each selected elementselection.append
and selection.insert
return added elements
<body></body>
d3.select('body')
.append('p') //append p as the last child of body
.text('Text0'); //set text to "Text0"
<body>Text0
</body>
d3.selectAll('p') //selects all elements of type p
.append('p') //append p as the last child of the selection
.text('Text1'); //set text to "Text1"
<body><p>Text0<p>Text1</p></p></body>
selection.remove()
Removes the selected elements from the document
<body>Text0
</body>
d3.selectAll('p') //selects all elements of type p
.remove(); //removes selected elements
<body></body>
selection.text(text)
changes the text of HTML elements elementsselection.style(style)
sets the style of HTML elements elements
Paragraph1
Paragraph2
d3.selectAll('p') //select all p in document
.text('Paragraph');
d3.select('p') //select first p in document
.text('paragraph2');
.style('color', 'red');
<div id="chart"></div>
<script>
d3.select('#chart')
.append('div')
.attr('width', '300px')
.attr('background-color', '#eee')
.text('300,000');
</script>
<body>
<p>Paragraph1</p>
<p>Paragraph2</p>
<script type="text/javascript">
d3.selectAll('p')
.text('Paragraph1');
d3.select('p')
.text('Paragraph2')
.style('color', 'red');
</script>
</body>
Paragraph1
Paragraph2
Paragraph2$\leftarrow$
Paragraph1
Paragraph1
Paragraph2
Paragraph2
Paragraph1
<body>
<script type="text/javascript">
d3.select("div").text("div").style("color", "orange");
</script>
</body>
<body>
text
<script type="text/javascript">
d3.selectAll("p").text("p").style("color", "orange");
</script>
</body>
<body>
<span>Potato </span>
<script type="text/javascript">
d3.select("body").select("span").text("Tomato ");
d3.selectAll("span").append("span").text("Salad ");
d3.selectAll("span").append("span").text("Carrot ");
</script>
</body>
var dataset = [0, 1, 2];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d) { return d; });
0
1
2
var dataset = [0, 1, 2];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d) { return d; });
var dataset = [0, 1, 2];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d) { return d; });
var dataset = [0, 1, 2];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d) { return d; });
var dataset = [0, 1, 2];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d) { return d; });
var dataset = [0, 1, 2];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d) { return d; });
var dataset = [0, 1, 2];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d) { return d; })
var dataset = [0, 1, 2];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d) { return d; });
var dataset = [1, 2, 3];
d3.select('body')
.selectAll('p')
.data(dataset)
.enter()
.append('p')
.text(function(d, i) { return 'd=' + d + ' i=' + i; })
.style('color', function(d, i) { return (i % 2) ? 'red' : 'blue'; });
var dataset = [1, 2, 3];
d3.select('body')
.selectAll('p')
.data(dataset) //dataset must be an array!
.enter()
...
var data = [ //array of objects
{name: 'A', frequency: .08167},
{name: 'B', frequency: .01492},
{name: 'C', frequency: .02780},
{name: 'D', frequency: .04253},
{name: 'E', frequency: .12702}
];
var data = [ //array of arrays
[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]
];
var dataset = ["red ", "blue ", "red ", "blue ", "red "];
d3.select('body').selectAll('span')
.data(dataset)
.enter()
.append('span')
.text(function (d) { return d });
d3.selectAll("span")
.style("text-decoration", function (d, i) { return (i % 2) ? "none" : "underline"; })
.append("span").text("blue ");
<body>
<script type="text/javascript">
var dataset = ['red ', 'blue ', 'red ', 'blue ', 'red '];
d3.select('body')
.selectAll('span')
.data(dataset)
.enter()
.append('span')
.text(function (d) { return d });
d3.selectAll("span")
.style("text-decoration", "underline");
dataset = shuffle(dataset);
d3.select('body')
.selectAll('span')
.data(dataset)
.enter()
.append('span')
.text(function (d) { return d });
d3.selectAll("span")
.style("text-decoration", "none");
function shuffle(array) { // Shuffles array.
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m], array[m] = array[i], array[i] = t;
}
return array;
}
</script>
</body>
TL;DR. Graphic & data described as JSON (spec)
To generate a graphic:
- Write a Vega or Vega-lite spec
- Use a Javascript library (vegaEmbed) to render the spec
{
"$schema": "https://vega.github.io/schema/vega-lite/v3.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A","b": 28},
{"a": "B","b": 55},
{"a": "C","b": 43},
{"a": "D","b": 91},
{"a": "E","b": 81},
{"a": "F","b": 53},
{"a": "G","b": 19},
{"a": "H","b": 87}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "a", "type": "ordinal"},
"y": {"field": "b", "type": "quantitative"}
}
}
{
"$schema": "https://vega.github.io/schema/vega/v4.json",
"width": 400,
"height": 200,
"padding": 5,
"data": [
{
"name": "table",
"values": [
{"category": "A", "amount": 28},
{"category": "B", "amount": 55},
{"category": "C", "amount": 43},
{"category": "D", "amount": 91},
{"category": "E", "amount": 81},
{"category": "F", "amount": 53},
{"category": "G", "amount": 19},
{"category": "H", "amount": 87}
]
}
],
"signals": [
{
"name": "tooltip",
"value": {},
"on": [
{"events": "rect:mouseover", "update": "datum"},
{"events": "rect:mouseout", "update": "{}"}
]
}
],
"scales": [
{
"name": "xscale",
"type": "band",
"domain": {"data": "table", "field": "category"},
"range": "width",
"padding": 0.05,
"round": true
},
{
"name": "yscale",
"domain": {"data": "table", "field": "amount"},
"nice": true,
"range": "height"
}
],
"axes": [
{ "orient": "bottom", "scale": "xscale" },
{ "orient": "left", "scale": "yscale" }
],
"marks": [
{
"type": "rect",
"from": {"data":"table"},
"encode": {
"enter": {
"x": {"scale": "xscale", "field": "category"},
"width": {"scale": "xscale", "band": 1},
"y": {"scale": "yscale", "field": "amount"},
"y2": {"scale": "yscale", "value": 0}
},
"update": {
"fill": {"value": "steelblue"}
},
"hover": {
"fill": {"value": "red"}
}
}
},
{
"type": "text",
"encode": {
"enter": {
"align": {"value": "center"},
"baseline": {"value": "bottom"},
"fill": {"value": "#333"}
},
"update": {
"x": {"scale": "xscale", "signal": "tooltip.category", "band": 0.5},
"y": {"scale": "yscale", "signal": "tooltip.amount", "offset": -2},
"text": {"signal": "tooltip.amount"},
"fillOpacity": [
{"test": "datum === tooltip", "value": 0},
{"value": 1}
]
}
}
}
]
}