Data Visualization workshop
Shirley Wu
(@sxywu)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3378222/july-final.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/4546610/Screenshot_2018-01-25_18.36.11.png)
You will learn:
SVG Paths
SVG Transforms
Selections
Data binding
Enter-append
Scales
Update & Exit
Transitions
Nesting elements
SVG Elements
rect
x: x-coordinate of top-left
y: y-coordinate of top-left
width
height
circle
cx: x-coordinate of center
cy: y-coordinate of center
r: radius
text
x: x-coordinate
y: y-coordinate
dx: x-coordinate offset
dy: y-coordinate offset
text-anchor: horizontal text alignment
Hi!
path
d: path to follow
Moveto, Lineto, Curveto, Arcto
SVG Paths
SVG Transforms
Exercise time
Create 3 unique petal shapes
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/5050164/Screen_Shot_2018-06-21_at_7.31.03_AM.png)
Selection & Data
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
<script>
var data = [100, 250, 175, 200, 120];
d3.selectAll('rect')
.data(data)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638370/Screenshot_2017-03-27_14.18.02.png)
Selection & Data
5 rectangle elements
Select all rectangle elements that exist
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638089/Screenshot_2017-03-27_13.23.35.png)
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
<script>
var data = [100, 250, 175, 200, 120];
d3.selectAll('rect')
.data(data)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
"Bind" data to the selections
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638237/data1.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638239/data2.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638246/Screenshot_2017-03-27_13.54.33.png)
Selection & Data
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
<script>
var data = [100, 250, 175, 200, 120];
d3.selectAll('rect')
.data(data)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Loop through each rectangle selection
Get passed in (data, index)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638324/Screenshot_2017-03-27_14.06.28.png)
Selection & Data
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
<script>
var data = [100, 250, 175, 200, 120];
d3.selectAll('rect')
.data(data)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Selection & Data
Play with the code
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/2022862/Screen_Shot_2015-12-02_at_10.10.31_AM.png)
(Change some numbers, add/remove a rect, console log all the things)
Enter-append
<svg></svg>
<script>
var rectWidth = 100;
var height = 300;
var data = [100, 250, 175, 200, 120];
var svg = d3.select('svg');
svg.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638370/Screenshot_2017-03-27_14.18.02.png)
Wut, no rectangle elements?!
So what are we even selecting?
A: an empty selection
So how are those bars appearing?
Enter-append
<svg></svg>
<script>
var rectWidth = 100;
var height = 300;
var data = [100, 250, 175, 200, 120];
var svg = d3.select('svg');
svg.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638982/enter1.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3638998/enter2.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3639000/enter3.jpg)
Magic ✨
Enter-append
<svg></svg>
<script>
var rectWidth = 100;
var height = 300;
var data = [100, 250, 175, 200, 120];
var svg = d3.select('svg');
svg.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Enter-append
Play with the code
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/2022862/Screen_Shot_2015-12-02_at_10.10.31_AM.png)
(Try adding/removing some numbers, and make sure to console log all the things)
let's make a flower
- Get data ready, and use the first movie data
- Create 6 petals using previously created paths
- Translate and rotate the petals
Scales & Axes
d3.scaleLinear()
.domain([min, max]) // input
.range([min, max]); // output
scale: mapping from
data attributes (domain)
to display (range)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3639158/scale.jpg)
date → x-value
value → y-value
value → opacity
etc.
Scales
// get min/max
var height = 600;
var data = [
{date: new Date('01-01-2015'), temp: 0},
{date: new Date('01-01-2017'), temp: 3}
];
var min = d3.min(data, d => d.date);
var max = d3.max(data, d => d.date);
// or use extent, which gives back [min, max]
var extent = d3.extent(data, d => d.date);
var yScale = d3.scaleLinear()
.domain([min, max])
.range([height, 0]);
Scales
// continuous
d3.scaleLinear()
d3.scaleLog()
d3.scaleTime()
// ordinal
d3.scaleBand()
Scales I use often:
Let's make a flower
Type of petal: parental guidance rating
Size of flower: IMDb rating out of 10
Number of petals: number of IMDb votes
- Create scale (scaleLinear and scaleQuantize)
- Get data ready, and use the first movie data
- Create number of petals based on data
- Rotate and scale petals
Hint: use SVG transform's translate, rotate, and scale (in that order)
update & exit
update & exit
// bars includes update selection
var bars = svg.selectAll('rect')
.data(data, d => d);
// exit
bars.exit().remove();
// enter
var enter = bars.enter().append('rect')
.attr('width', rectWidth)
.attr('stroke', '#fff');
// enter + update
bars = enter.merge(bars)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('height', d => d)
.attr('fill', d => colors(d));
key function: controls which datum is assigned to which element
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3654113/update1.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3654133/update2.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3654135/update3.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3654139/update4.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3654182/Screenshot_2017-03-30_20.03.43.png)
update & exit
// bars includes update selection
var bars = svg.selectAll('rect')
.data(data, d => d);
// exit
bars.exit().remove();
// enter
var enter = bars.enter().append('rect')
.attr('width', rectWidth)
.attr('stroke', '#fff');
// enter + update
bars = enter.merge(bars)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('height', d => d)
.attr('fill', d => colors(d));
Enter selection: chain attributes that don't depend on data
Combines 2 selections into one
Enter+update selection: chain attrs dependent on data
Exit selection
Play with the code
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/2022862/Screen_Shot_2015-12-02_at_10.10.31_AM.png)
Update-exit
Let's update the flowers!
At every second, draw the next flower.
- Create function, within function:
- Calculate petal data for specific movie
- Bind data and draw flower with enter-update-exit pattern
Transitions
Emphasize changes in state
(object constancy)
Transitions
var t = d3.transition()
.duration(1000);
var svg = d3.select('svg');
var bars = svg.selectAll('rect')
.data(data, d => d);
// exit
bars.exit().transition(t)
.attr('y', height)
.attr('height', 0)
.remove();
// enter
var enter = bars.enter().append('rect')
.attr('width', rectWidth)
.attr('stroke', '#fff')
.attr('y', height);
// enter + update
bars = enter.merge(bars)
.attr('x', (d, i) => i * rectWidth)
.attr('fill', d => colors(d))
.transition(t)
.attr('y', d => height - d)
.attr('height', d => d);
Define transition, syncs animation everywhere it's used
Animate height down to 0 before removing
Animate remaining <rect>'s height to their next state
Transitions
var t = d3.transition()
.duration(1000);
var svg = d3.select('svg');
var bars = svg.selectAll('rect')
.data(data, d => d);
// exit
bars.exit().transition(t)
.attr('y', height)
.attr('height', 0)
.remove();
// enter
var enter = bars.enter().append('rect')
.attr('width', rectWidth)
.attr('stroke', '#fff')
.attr('y', height);
// enter + update
bars = enter.merge(bars)
.attr('x', (d, i) => i * rectWidth)
.attr('fill', d => colors(d))
.transition(t)
.attr('y', d => height - d)
.attr('height', d => d);
go from state A to state B
Everything after .transition(): state B to transition to
Same attributes set before .transition(): state A to transition from
If attributes not specified for A, d3 extrapolates from default
Transitions
Play with the code
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/2022862/Screen_Shot_2015-12-02_at_10.10.31_AM.png)
nesting elements
Nest elements when you want to apply the same styles and transforms to a group of elements
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/4546593/Screenshot_2018-01-25_18.38.29.png)
<g> element that consist of petals and leaves, has translate and scale applied
<path> element for each petal with rotate applied
nesting elements
const data = [
{fill: 'red', circle: [2, 25, 50]},
{fill: 'blue', circle: [10, 100, 150]},
];
const svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
const groups = svg.selectAll('g')
.data(data).enter().append('g')
.attr('fill', d => d.fill)
.attr('transform', 'translate(10,10)');
const circles = groups.selectAll('circle')
.data(d => d.circle).enter().append('circle')
.attr('cx', d => d)
.attr('r', 10);
nesting elements
const data = [
{fill: 'red', circle: [2, 25, 50]},
{fill: 'blue', circle: [10, 100, 150]},
];
const svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
const groups = svg.selectAll('g')
.data(data).enter().append('g')
.attr('fill', d => d.fill)
.attr('transform', 'translate(10,10)');
const circles = groups.selectAll('circle')
.data(d => d.circle).enter().append('circle')
.attr('cx', d => d)
.attr('r', 10);
Use <g> element to nest child elements *in SVG, only <g> elements can have children
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/4546623/Paper.Talks_and_Workshops.1.png)
nesting elements
const data = [
{fill: 'red', circle: [2, 25, 50]},
{fill: 'blue', circle: [10, 100, 150]},
];
const svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
const groups = svg.selectAll('g')
.data(data).enter().append('g')
.attr('fill', d => d.fill)
.attr('transform', 'translate(10,10)');
const circles = groups.selectAll('circle')
.data(d => d.circle).enter().append('circle')
.attr('cx', d => d)
.attr('r', 10);
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/4546632/Paper.Talks_and_Workshops.1_2.png)
const data = [
{fill: 'red', circle: [2, 25, 50]},
{fill: 'blue', circle: [10, 100, 150]},
];
const svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
const groups = svg.selectAll('g')
.data(data).enter().append('g')
.attr('fill', d => d.fill)
.attr('transform', 'translate(10,10)');
const circles = groups.selectAll('circle')
.data(d => d.circle).enter().append('circle')
.attr('cx', d => d)
.attr('r', 10);
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/4546629/Paper.Talks_and_Workshops.1_2_copy.png)
nesting elements
const data = [
{fill: 'red', circle: [2, 25, 50]},
{fill: 'blue', circle: [10, 100, 150]},
];
const svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
const groups = svg.selectAll('g')
.data(data).enter().append('g')
.attr('fill', d => d.fill)
.attr('transform', 'translate(10,10)');
const circles = groups.selectAll('circle')
.data(d => d.circle).enter().append('circle')
.attr('cx', d => d)
.attr('r', 10);
nesting elements
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/4546645/Paper.Talks_and_Workshops.1_3.png)
Play with the code
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/2022862/Screen_Shot_2015-12-02_at_10.10.31_AM.png)
Nesting elements
Let's make all the flowers!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/4546610/Screenshot_2018-01-25_18.36.11.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/415741/images/3654588/giphy__1_.gif)
Full-day creative data visualization workshop
By Shirley Wu
Full-day creative data visualization workshop
- 3,099