Chord diagram
A chord diagram represents flows between several entities called nodes. Each node is represented by a fragment on the outer part of the circular layout. Then, arcs are drawn between each entities. The size of the arc is proportional to the importance of the flow..
Building a chord diagram with React and D3.js relies on the d3-chord
module that computes the node and arc positions for us. React
can then be used to draw everything in SVG. This page is a step by step tutorial with code sandboxes. It will teach you how to build a ChordDiagram
component.
The Data
The dataset required to build a chord diagram is a square matrix. It has a dimension of n x n
where n
is the number of nodes.
In javascript, this matrix is represented as an array of n
array. Each individual array also has n
items. The matrix of flow has a direction: the second item of the third row gives the flow from element 2 to element 3.
Usually an additional array is provided, giving the name of each node.
Here is a minimal example of the data structure:
// matrix of flow
const data = [
[11975, 0, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 0, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
// node names
const nodeNames = ['Barcelona', 'Paris', 'Dakar', 'NY']
Component skeleton
The goal here is to create a ChordDiagram
component that will be stored in a ChordDiagram.tsx
file. This component requires 3 props to render: a width
, a height
, some data
and a list of names
.
The shape of the data
is described above. The width
and height
will be used to render an svg
element in the DOM, in which we will insert the chord diagram.
To put it in a nutshell, that's the skeleton of our ChordDiagram
component:
import * as d3 from "d3"; // we will need d3.js
type ChordDiagramProps = {
width: number;
height: number;
data: number[];
};
export const ChordDiagram = ({ width, height, data }: ChordDiagramProps) => {
// read the data
// compute the nodes and ribbon position
// build the arcs
// build the ribbon
return (
<div>
<svg width={width} height={height}>
// render all the arcs and ribbons
</svg>
</div>
);
};
It's fundamental to understand that with this code organization, d3.js will be used to prepare the SVG circle
, but it's React that will render them in the return()
statement. We won't use d3 methods like append
that you can find in usual d3.js examples.
The chord()
function
We need to transform the flow matrix described in the data section into a list of node and connection coordinates. This is easy thanks to the chord()
function of d3.js.
The function can be used as follow:
const chordGenerator = d3
.chord()
.padAngle(0.05) // padding between nodes
.sortSubgroups(d3.descending);
const chord = chordGenerator(data);
The returned object (chord
in this example) is an array listing all the connections. For each, details about the source
and the target
are provided:
[
// first connection: flow between node 1 and node 1
{
source: { index: 0, startAngle: 0, endAngle: 0.84, value: 11975 },
target: { index: 0, startAngle: 0, endAngle: 0.84, value: 11975
}
},
// second connection: flow between node 2 and node 1
{
source: { index: 1, startAngle: 3.01, endAngle: 3.15, value: 1951 },
target: { index: 1, startAngle: 1.67, endAngle: 1.67, value: 0
}
},
// ...
]
Last but not least, the array also has a group
property with details about all nodes of the chord diagram.
Draw the nodes
Nodes are drawn using the group
property of the chord
object computed above. For each group, the start and end angles are provided.
From this information it is possible to draw an arc thanks to the arc()
function of d3. It is exactly the same process as for a donut chart. Please visit the donut section of the gallery for more explanation!
Nodes are drawn thanks to the arc() function of d3.js, like for a donut chart.
Draw the connections
We now have to draw the connections between nodes that are listed in the initial square matrix (data
).
The connection coordinates are listed in the chord
object computed in the previous section. For each connection we know the startAngle
and endAngle
of the source
and of thetarget
.
This is everything we need to compute the connections thanks to the ribbon()
function of d3 as follow:
const allConnections = chord.map((connection, i) => {
const d = ribbonGenerator(connection);
return <path key={i} d={d} />;
});
Connections between nodes are drawn thanks to the ribbon() function of d3.js.
d3.ribbon()
documentationResponsive Chord Diagram with react
The component above is not responsive. It expects 2 props called width
and height
and will render a Chord Diagram of those dimensions.
Making the Chord Diagram responsive requires adding a wrapper component that gets the dimension of the parent div
, and listening to a potential dimension change. This is possible thanks to a hook called useDimensions
that will do the job for us.
useDimensions
: a hook to make your viz responsive
export const useDimensions = (targetRef: React.RefObject<HTMLDivElement>) => {
const getDimensions = () => {
return {
width: targetRef.current ? targetRef.current.offsetWidth : 0,
height: targetRef.current ? targetRef.current.offsetHeight : 0
};
};
const [dimensions, setDimensions] = useState(getDimensions);
const handleResize = () => {
setDimensions(getDimensions());
};
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
useLayoutEffect(() => {
handleResize();
}, []);
return dimensions;
}
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Chord Diagram inspiration
If you're looking for inspiration to create your next Chord Diagram, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Chord Diagram looks good!
visitFirst chord diagram
I suggest 2 improvements to get a descent chord diagram:
→ Colors
Pretty straightforward to implement. You just need to create an array of colors
. Then, for each item to draw the index
is always available. It can be used to retrieve the color in the color array.
→ Labels
A new prop needs to be passed to the component with a list of names for the nodes. I suggest to position labels as for a donut chart but many other possibilities are available.
Connections between nodes are drawn thanks to the ribbon() function of d3.js.
ToDoAdd section on hover effect
ToDoTalk about chordDirected() and chordTranspose()
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data
with gmail.com
. You can also subscribe to the newsletter to know when I publish more content!