Well Hello There
Recently I have seen a sharp uptick in the number of Sankey Diagrams on one of my favourite Subreddits r/DataIsBeautiful and decided it was about time I gave one a go.What is a Sankey Diagram?
A Sankey Diagram is a visualisation technique that displays data flows. A number of items(nodes) are represented by rectangles or text and the links between them are represented with lines that have a width proportional to the importance of the flow. They can be used to show how the data flows from source to end, or how the data evolves through each link.Use cases include showing the source of energy, paths and it's usage , another could be showing the flow of an infection such as Covid in a local community.
What We'll build
We will create a simple Sankey Diagram to will display the distribution flow of items sold to customers from a ficticious store offering instore and online sales to customers.Getting Started
First off add the script links for D3.js and following that the Sankey Plugin. The order here is important ! Then within the body tag create a Div element which we will insert the diagram into.
<!-- Load d3 -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- Load sankey.js -->
<script src="https://cdn.jsdelivr.net/gh/holtzy/D3-graph-gallery@master/LIB/sankey.js"></script>
<body>
<div id="sankey_example"></div>
</body>
Node and Link Structure
Next prepare the nodes and link variables. You could fetch these from an external source but I have created my own arrays. Their format is like below.
// Nodes are identified by the node property
var nodes = [{
node: 0,
name: "Takeaway"
}];
// The Links require a weight / value
// The Source and target properties are the values from the node property in the above.
var links = [{
source: 9,
target: 1,
value: 4
}]
Initialize the Sankey Diagram
Now comes the meat of the task - creating the diagram itself !
// set the dimensions of the graph and margins
var margin = {top: 10, right: 10, bottom: 10, left: 10},
width = (window.screen.availWidth /2) - margin.left - margin.right,
height = (window.screen.availHeight -100) - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#sankey_example").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Color scale used
var color = d3.scaleOrdinal(d3.schemeCategory10);
// Set the sankey diagram properties
var sankey = d3.sankey()
.nodeWidth(36)
.nodePadding(290)
.size([width, height]);
// Constructs a new Sankey generator with the default settings.
// Using the nodes and links from our data
sankey
.nodes(nodes)
.links(links)
.layout(32);
// Add in the links
var link = svg.append("g")
.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", sankey.link() )
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; })
;
// Add the link titles
link.append("title")
.text(function(d) {
return d.source.name + " → " + d.target.name + "\n" + (d.value); });
// add in the nodes
var node = svg.append("g")
.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.call(d3.drag()
.subject(function(d) { return d; })
.on("start", function() { this.parentNode.appendChild(this); })
.on("drag", dragmove));
// add the rectangles for the nodes
node
.append("rect")
.attr("height", function(d) { return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
// Add hover text
.append("title")
.text(function(d) { return d.name + "\n" + "There is " + d.value + " items of this type."; });
// add in the title for the nodes
node
.append("text")
.attr("x", -6)
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("transform", null)
.text(function(d) { return d.name; })
.filter(function(d) { return d.x < width / 2; })
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start");
// the function for moving the nodes
function dragmove(d) {
d3.select(this)
.attr("transform",
"translate("
+ d.x + ","
+ (d.y = Math.max(
0, Math.min(height - d.dy, d3.event.y))
) + ")");
sankey.relayout();
link.attr("d", sankey.link() );
}
Adding Interactivity
Next we can add some simple interactivity with an onlick function. I have added a simple Alert() but you can add anything you want here !
// Add Link Interactivity
link.on("click", function(d){
console.log(d);
alert("From: " + d.source.name +" To:" +d.target.name +" Value:"+d.value);
})
Comments
Post a Comment