Striking a chord

The chord layout creates a circular diagram showing relations in a dataset. We will use yet another dataset here, found in the data/ directory stormofswords.csv, from the Network of Thrones dataset available at https://www.macalester.edu/~abeverid/thrones.html.

This dataset was created by looking at the proximity of character names in the text of the book series in order to find the weight of each character's connection to the other characters. It is an ideal dataset for the next two examples, which look at arbitrary nonhierarchical connections between data.

Start by doing the comment out the last example and add this dance in main.js:

westerosChart.init('chord', 'data/stormofswords.csv');

Go back to chapter7/index and scaffold out the new chart method:

westerosChart.chord = function Chord(_data) {};

Nothing new. We will create an array of sources and links between them, assuming the strength of the connection is greater than 20. Add this to the Chord function:

  const minimumWeight = 20; 
const majorLinks = _data.filter(d => +d.Weight > minimumWeight);
const majorSources = uniques(majorLinks, d => d.Source);
const data = majorLinks.filter(d =>
majorSources.indexOf(d.Target) > -1);

We filter out links that don't have a target to get our data array.

We will pass this to a function to get a connection matrix, which is an array of arrays. Each element in the array's arrays references the other values in a circular fashion. Let's fill out connectionMatrix in common.js:

export function connectionMatrix(data, sourceKey = 'source', targetKey = 'target', valueKey = 'value') { 
const nameIds = nameId(allUniqueNames(data, 'Source', 'Target'), d => d);
const uniqueIds = nameIds.domain();
const matrix = d3.range(uniqueIds.length).map(() => d3.range(uniqueIds.length).map(() => 1));
data.forEach((d) => {
matrix[nameIds(d[sourceKey])][nameIds(d[targetKey])] += Number(d[valueKey]);
});

return matrix;
}

We create a scale of all the unique names in the data array. We then get an array of unique names by getting its domain. We then use d3.range() twice to get a matrix filled with arrays filled with 1s. We then replace each 1 with the value of the connection weight from the dataset, then return the matrix.

Go back to chapter7/index and add the following to the Chord function:

  const matrix = connectionMatrix(data, 'Source', 'Target', 'Weight'); 
const outerRadius = (Math.min(this.width, this.height) * 0.5) - 40;
const innerRadius = outerRadius - 30;

We create our matrix, then define an outer and inner radius.

Next, we create our chord layout generator, then an arc generator, then a ribbon generator:

  const chord = d3.chord() 
.padAngle(0.05)
.sortSubgroups(d3.descending);

const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);

const ribbon = d3.ribbon()
.radius(innerRadius);

Let's get this show on the road. First, append a group to our container, then pass our matrix to the chord layout:

  const chart = this.container 
.append('g')
.attr('class', 'chord')
.attr('transform', `translate(${this.innerWidth / 2}, ${this.innerHeight / 2})`)
.datum(chord(matrix));

We translate it to half the width and height because otherwise it would be in the top-left corner.

Next, we create groups for each of the items in our dataset:

  const group = chart.append('g').attr('class', 'groups') 
.selectAll('g')
.data(chords => chords.groups)
.enter()
.append('g');

group.append('path')
.style('fill', d => color(d.index))
.style('stroke', d => d3.color(color(d.index)).darker())
.attr('d', arc);

Lastly, we add our ribbons linking each of the groups:

  const ribbons = chart.append('g').attr('class', 'ribbons') 
.selectAll('path')
.data(chords => chords)
.enter()
.append('path')
.attr('d', ribbon)
.style('fill', d => color(d.target.index))
.style('stroke', d => d3.color(color(d.index)).darker());

Finally, call the tooltip on both the ribbons and the groups:

  ribbons.call(tooltip(d => majorSources[d.target.index], this.container)); 
group.call(tooltip(d => majorSources[d.index], this.container));

That's actually pretty cool!

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

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