Got mad stacks

The stack layout allows us to stack regions in a chart, which is useful for things such as stacked area charts and what have you. It's in the d3-shape package.

Go back to main.js and comment out the last example and add the following:

westerosChart.init('stack',
'data/GoT-deaths-by-season.json', false);

We add another option to this because we're going to create two charts from this example. This time we're using our deaths-by-season dataset.

Inchapter7/index.js add the following to the bottom of the file:

westerosChart.stack = function Stack({ data }, isStream = false) { 
const episodesPerSeason = 10;
const totalSeasons = 6;
// Create a nest containing deaths per episode
const seasons = d3.nest()
.key(d => d.death.episode)
.key(d => d.death.season)
.entries(data.filter(d => !d.death.isFlashback))
.map(v => {
return d3.range(1, totalSeasons + 1)
.reduce((item, episodeNumber) => {
const deaths = v.values.filter(d =>
+d.key === episodeNumber)
.shift() || 0;
item[`season-${episodeNumber}`] = deaths ?
deaths.values.length : 0;
return item;
}, { episode: v.key });
})
.sort((a, b) => +a.episode - +b.episode);
}

We create a function that has two arguments, one expecting an object with a data attribute, and another a Boolean deciding whether this is a stream chart or not. We then create a nest of items and filter out anyone who died in a flashback. We get an array of episodes, with each having a certain mortality rate per season, defined by the season-n key.

Next, set up our stack layout generator and map it to our season-n key:

  const stack = d3.stack() 
.keys(d3.range(1, totalSeasons + 1)
.map(key => `season-${key}`));

Add this line to make it so that this instantly becomes a stream chart just by providing a truthy value to the function constructor:

if (isStream) stack.offset(d3.stackOffsetWiggle);

Next, we set up our x- and y-scales:

  const x = d3.scaleLinear() 
.domain([1, episodesPerSeason])
.range([this.margin.left, this.innerWidth - 20]);

const y = d3.scaleLinear()
.domain([
d3.min(stack(seasons), d => d3.min(d, e => e[0])),
d3.max(stack(seasons), d => d3.max(d, e => e[1]))
])
.range([this.height -
(this.margin.bottom + this.margin.top + 30), 0]);

Our stack layout creates an array of arrays, with each of those containing an upper and lower y-value. We can pass this to an area generator to create an area. Let's do that now:

  const area = d3.area() 
.x(d => x(d.data.episode))
.y0(d => y(d[0]))
.y1(d => y(d[1]))
.curve(d3.curveBasis);

Next, append the paths using our area generator:

  const stream = this.container.append('g') 
.attr('class', 'streams')
.selectAll('path')
.data(stack(seasons))
.enter()
.append('path')
.attr('d', area)
.style('fill', (d, i) => color(i));

We provide our seasons data object to stack, which we pass to .data(). We then use our area generator to set the region shape.

Next, we append an x axis:

  this.container.append('g') 
.attr('class', 'axis')
.attr('transform',
`translate(0,${this.height - (this.margin.bottom +
this.margin.top + 30)})`)
.call(d3.axisBottom(x));

And then a legend:

  const legendOrdinal = legend.legendColor() 
.orient('horizontal')
.title('Season')
.labels(d => d.i + 1)
.scale(color);

const legendTransform = isStream ?
`translate(50,${this.height -
(this.margin.bottom + this.margin.top + 130)})` :
`translate(50,0)`;

this.container.append('g')
.attr('class', 'legend')
.attr('transform', legendTransform)
.call(legendOrdinal);

Finally, it's tooltip time!:

stream.call(tooltip(d => `Season ${d.index + 1}`, this.container));

There we go!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset