16

I want to draw a Sankey diagram using Javascript. Can anyone provide some direction regarding the algorithms or libraries that are available for this?

VividD
  • 10,456
  • 6
  • 64
  • 111
321zeno
  • 1,264
  • 1
  • 12
  • 24
  • Related question: http://stats.stackexchange.com/questions/24074/whats-a-good-tool-to-create-sankey-diagrams/24114#24114 – Thilo Mar 05 '12 at 10:38

7 Answers7

11

This is a basic Sankey diagram using raphaeljs

function Sankey(x0, y0, height, losses) {
    var initialcolor = Raphael.getColor();
    var start = x0 + 200;
    var level = y0 + height;
    var heightunit = height / 100;
    var remaining = 100 * heightunit;

    function drawloss(start, level, loss) {
        var thecolor = Raphael.getColor();
        paper.path("M" + (start - 100) + "," + (level - loss) + "L" + start + "," + (level - loss)).attr({stroke: thecolor});
        paper.path("M" + (start - 100) + "," + level + "L" + start + "," + level).attr({stroke: thecolor});
        paper.path("M " + start + "," + level + " Q" + (start + 100) + "," + level + " " + (start + 100) + "," + (level + 100)).attr({stroke: thecolor});
        paper.path("M " + start + "," + (level - loss) + " Q" + (start + 100 + loss) + "," + (level - loss) + " " + (start + 100 + loss) + "," + (level + 100)).attr({stroke: thecolor});
        paper.path("M " + (start + 100) + "," + (level + 100) + " L " + (start - 10 + 100) + "," + (level + 100) + " L " + (start + loss / 2 + 100) + "," + (level + 110) + " L " + (start + loss + 10 + 100) + "," + (level + 100) + " L " + (start + loss + 100) + ", " + (level + 100)).attr({stroke: thecolor});
    }

    function drawremaining(start, level, loss) {
        paper.path("M 100," + y0 + "L" + (start + 100) + "," + y0).attr({stroke: initialcolor});
        paper.path("M" + (start - 100) + "," + level + "L" + (start + 100) + "," + level).attr({stroke: initialcolor});
        paper.path("M " + (start + 100) + " " + y0 + " L " + (start + 100) + " " + (y0 - 10) + " L " + (start + 110) + " " + (y0 + loss / 2) + " L " + (start + 100) + " " + (level + 10) + " L " + (start + 100) + " " + level).attr({stroke: initialcolor});
    }

    function drawstart(x0, y0, width, height) {
        paper.path("M " + x0 + "," + y0 + "L" + (x0 + width) + "," + y0).attr({stroke: initialcolor});
        paper.path("M " + x0 + "," + (y0 + height) + "L" + (x0 + width) + "," + y0 + height)).attr({stroke:  initialcolor});
        paper.path("M " + x0 + "," + y0 + "L" + x0 + "," + (y0 + height)).attr({stroke: initialcolor});
    }

    drawstart(x0, y0, 100, height);

    for (var i in losses) {
        drawloss(start, level, losses[i] * heightunit);
        remaining -= losses[i] * heightunit;
        level -= losses[i] * heightunit;
        start += 100;
    }
}

And I use it like this:

<div id="notepad" style="height:1000px; width:1000px; background: #eee"></div>
<script type="text/javascript">
    var paper = Raphael(document.getElementById("notepad"), 1020, 1000);
    var losses=[50, 30, 5];
    Sankey(10, 100, 200, losses);
</script>
321zeno
  • 1,264
  • 1
  • 12
  • 24
11

In case helpful to others: I've extracted my javascript sankey diagram drawing code here:

http://tamc.github.com/Sankey/

The original usage is on this UK government site:

http://2050-calculator-tool.decc.gov.uk/pathways/2022222122222103332220023211022330220130233022012/sankey

Tom Counsell
  • 161
  • 1
  • 5
7

D3.js uses a plugin to create sankey diagrams pretty well.

http://bost.ocks.org/mike/sankey/

cmonkey
  • 4,256
  • 1
  • 26
  • 46
2

Here is a fairly detailed explanation of how Mike Bostock's D3-based Sankey DIagram code works: http://www.d3noob.org/2013/02/sankey-diagrams-description-of-d3js-code.html

I have implemented this on a Grails-based app server and it works.

Aml
  • 301
  • 1
  • 3
  • 6
2

Google Charts includes the Sankey Diagram: https://developers.google.com/chart/interactive/docs/gallery/sankey

Motin
  • 4,853
  • 4
  • 44
  • 51
1

Thanks to zenify for starting me on the path, I had to rejig some of the copied code above to get it to work but it definitely gives a good starting point. The code below can be copied into a .htm file and you just need to have raphael-min.js in the same directory for it to work.

Regards / Colm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" class="JS">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Raphael makes Sankey</title>
<script type="text/javascript" src="raphael-min.js"></script>
<script type="text/javascript">
function Sankey(x0,y0,height,losses){
    initialcolor= Raphael.getColor();
    var start=x0+200;
    var level=y0+height;    
    var heightunit=height/100;
    var remaining=100*heightunit;

function drawloss(start,level,loss){
    var thecolor=Raphael.getColor();
    paper.path("M"+(start-100)+","+(level-loss)+"L"+start+","+(level-loss)).attr({stroke: thecolor});
    paper.path("M"+(start-100)+","+(level)+"L"+start+","+(level)).attr({stroke: thecolor});
    paper.path("M "+start+","+level+" Q"+(start+100)+","+level+" "+(start+100)+","+(level+100)).attr({stroke: thecolor});
    paper.path("M "+start+","+(level-loss)+" Q"+(start+100+loss)+","+(level-loss)+" "+(start+100+loss)+","+(level+100)).attr({stroke: thecolor});
    paper.path("M "+(start+100)+","+(level+100)+" L "+(start-10+100)+","+(level+100)+" L "+(start+(loss/2)+100)+","+(level+110)+" L "+(start+(loss)+10+100)+","+(level+100)+" L "+(start+(loss)+100)+", "+(level+100)).attr({stroke: thecolor});
}

function drawremaining(start,level,loss){
    paper.path("M 100,"+y0+"L"+(start+100)+","+y0).attr({stroke: initialcolor});
    paper.path("M"+(start-100)+","+(level)+"L"+(start+100)+","+(level)).attr({stroke: initialcolor});
    paper.path("M "+(start+100)+" "+y0+" L "+(start+100)+" "+(y0-10)+" L "+(start+110)+" "+(y0+(loss/2))+" L "+(start+100)+" "+(level+10)+" L "+(start+100)+" "+(level)).attr({stroke: initialcolor});
}

function drawstart(x0, y0, width, height){
    paper.path("M "+x0+","+y0+"L"+(x0+width)+","+y0+"").attr({stroke: initialcolor});
    paper.path("M "+x0+","+(y0+height)+"L"+(x0+width)+","+y0+height+"").attr({stroke:  initialcolor});
    paper.path("M "+x0+","+y0+"L"+x0+","+(y0+height)+"").attr({stroke: initialcolor});
}

    drawstart(x0,y0,100,height);
    for (var i in losses){
      drawloss(start,level,losses[i]*heightunit);
      remaining-=losses[i]*heightunit;
      level-=losses[i]*heightunit;
      start+=100;
    }
    drawremaining(start, level, remaining);
}
</script>
</head>
<body id="blog">
    <div id="notepad" style="height:1000px; width:1000px; background: #eee"></div>
    <script type="text/javascript">
    var paper = Raphael(document.getElementById("notepad"), 1020, 1000);
    var losses=[50, 30, 5];
    Sankey(10, 100, 200, losses);
    </script>
</body>
</html>
Colm O'G
  • 11
  • 1
  • That's great! Now do you have any idea about how the created diagrams might be exported to an image format that might be saved by the user? – 321zeno Dec 31 '10 at 07:08
  • Hmm, that is beyond me for the moment. At the moment I'm thinking of the idea of a jquery plugin that acts on a html table of data where you input the table id, the data column and the title column to the plugin. The dream is that it generates the sankey and also extends the table to allow you to dynamically reorder the different values, set the angle/length of the arrow and a million other things. – Colm O'G Jan 04 '11 at 22:11
1

Update 2020:

For anyone struggling to bring D3 Sankey examples to life, I found this supereasy video tutorial. Worked like a charm for me :)

https://reactviz.holiday/sankey/

Also, in case you can't make this one work either, react-google-charts have a pretty nice looking alternative which couldn't be easier to work with (at least implementing the example was just copy-pasting the whole component from here https://react-google-charts.com/sankey-diagram):

import Chart from "react-google-charts";


<Chart
  width={600}
  height={'300px'}
  chartType="Sankey"
  loader={<div>Loading Chart</div>}
  data={[
    ['From', 'To', 'Weight'],
    ['A', 'X', 5],
    ['A', 'Y', 7],
    ['A', 'Z', 6],
    ['B', 'X', 2],
    ['B', 'Y', 9],
    ['B', 'Z', 4],
  ]}
  rootProps={{ 'data-testid': '1' }}
/>
DurkoMatko
  • 5,238
  • 4
  • 26
  • 35