ricklupton / d3-sankey-diagram Goto Github PK
View Code? Open in Web Editor NEWSankey diagram for d3
Home Page: https://ricklupton.github.io/d3-sankey-diagram
License: MIT License
Sankey diagram for d3
Home Page: https://ricklupton.github.io/d3-sankey-diagram
License: MIT License
Take this data:
{
"nodes": [
{ "id": "a" }, { "id": "b" }, { "id": "c" }, {"id": "d" }, {"id": "e" }, {"id": "f" }
],
"links": [
{ "source": "a", "target": "b", "value": 1 },
{ "source": "a", "target": "c", "value": 1000 },
{ "source": "b", "target": "d", "value": 1 },
{ "source": "c", "target": "d", "value": 1000 },
{ "source": "d", "target": "e", "value": 1 },
{ "source": "e", "target": "f", "value": 1 }
]
}
put into https://ricklupton.github.io/d3-sankey-diagram/
Observe strange layout if links from "a" to "c" (largest one)
First of all, thank you for this very nice library @ricklupton
I have seen a strange behavior when using update
: when calling sankey.update(graph)
with an unmodified graph object, circular links (usually made out of 4 segments) are now made out of 2 segments.
The layout goes from:
to the following wrong result upon calling update
:
Programmatically, this can be seen by running the following:
var graph = sankey(data);
var before = graph.links.map(function(link) {
return link.points.map(function(pts) {
return pts.d;
});
});
console.log(before);
// [['r', 'r'], ['r', 'r', 'r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'l', 'l', 'r'], ['r', 'r'], ['r', 'l', 'l', 'r']]
graph = sankey.update(graph);
var after = graph.links.map(function(link) {
return link.points.map(function(pts) {
return pts.d;
});
});
console.log(after);
// [['r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'r']]
Using the basic example on https://ricklupton.github.io/d3-sankey-diagram/, I get the following:
-sankey-diagram.umd.js:20584 Uncaught TypeError: Cannot read properties of undefined (reading 'length')
at titlePosition (d3-sankey-diagram.umd.js:20584:1)
at HTMLUnknownElement.eval (d3-sankey-diagram.umd.js:20467:1)
at Selection.eval [as each] (each.js:5:1)
at sankeyNode (d3-sankey-diagram.umd.js:20458:1)
at Selection.eval [as call] (call.js:4:1)
at updateNodes (d3-sankey-diagram.umd.js:20744:1)
at HTMLDivElement.eval (d3-sankey-diagram.umd.js:20711:1)
at Wn.each (d3.min.js:2:25626)
at exports (d3-sankey-diagram.umd.js:20674:1)
at Wn.call (d3.min.js:2:25196)
t
0.8.0 d3-sankey-diagram
7.8.4 d3
This is confusing, and it would be easy to filter groups given to the diagram component to do this if needed.
What do you think about there being a customisation method to support adding CSS classes to nodes and links? This should (I think) then allow the image styling to be performed in CSS rather than via color settings in the data config.
Great work on extending the original D3 sankey lib :)
Project Reference: https://github.com/nxt3AT/sankeydiagram.net
Hello,
I managed to update the bundle d3-sankey-diagram.js to make it compatible with the version 7 of d3.js.
Main changes is that d3-collection no longer exists. See migration guide for version 6, here, still working for version 7 https://observablehq.com/@d3/d3v6-migration-guide#collection
Modified file:
d3-sankey-diagram.js.txt
Changes:
With these changes, that example https://bl.ocks.org/ricklupton/6344d23aa6418702f120b34343dce7cb works.
Hi,
In the original d3-sankey it is possible to store specific position of each nodes, as you can see in https://sankey.csaladen.es/.
The data structure uses the following format:
params":[0.5,0.25,0,0,0],"fixedlayout":[[400.5,218.78016596921236],[0,497.0588235294118],[0,61.70588235294119],[587.25,0],[195.75,274.87527492724956],[790,0],[581.75,0]]}
Can this be used in the d3-sankey-diagram?
Currently, it works fine if we do all the layout on a new graph, OR the graph is supplied with nodePositions
set. But it's difficult to do little bits.
For example,
source
and target
as node objects, node incoming
and outgoing
as lists)The following json file is used as example:
https://ricklupton.github.io/d3-sankey-diagram/uk_energy.json
I'm seeing a lot of unused and undocumented fields:
links
:
time
(seems unused and undocumented, always set to "*"
)type
(used in default linkTitle
but otherwise not documented)title
(seems unused and undocumented)groups
:
bundle
(seems unused and undocumented, always set to null
)def_pos
(seems unused and undocumented, always set to null
)id
(seems unused and undocumented)type
(seems unused and undocumented)nodes
:
bundle
(seems unused and undocumented, always set to null
)def_pos
(seems unused and undocumented, always set to null
)visibility
(seems unused and undocumented, always set to visible
)style
(seems unused and undocumented, always set to visible
)direction
(used by nodeBackwards
but otherwise not documented)As raised in ricklupton/ipysankeywidget#5
We would like to [optionally] add text elements to the links to show the magnitude/type of the link on the diagram. Currently you can only see this by hovering to show the tooltip.
Here is the right function to modify
New contributors are very welcome -- we can help you. Reply below if you are interested.
$ npm install d3-sankey-diagram
npm ERR! code EBADPLATFORM
npm ERR! notsup Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm ERR! notsup Valid OS: darwin
npm ERR! notsup Valid Arch: any
npm ERR! notsup Actual OS: linux
npm ERR! notsup Actual Arch: x64
The use of fsevents
is unclear and since it was added in conjunction with the addition of prettier, it is probably not needed as a dependency.
Looking at the live examples page, the Bands and groups example doesn't display the group.
How to?
with old d3-sankey it was possible.
d3 version 4 is much more modular and changes some APIs.
While upgrading, I'm planning to revise the diagram API and bring the sankey-layout
package into this package, with a clean separation between graph layout (ranking and ordering node), Sankey node/link layout (curve radii etc) and SVG components.
the extent() property nicely bounds the verticals for the chart, but it doesn't account for labels and paths which happen to appear outside of the area bounded by those verticals. as a result i need to manually set an extent after i've seen a rending to know how much padding i'm going to need to properly render the chart without obscuring the labels and paths which appear outside the extent. it would nice to have a way to bound the chart which included these outlying labels and paths so i can ensure my entire chart is plotted inside my target rectangle.
Hi @ricklupton ,
Really great visualization!
When grouping nodes the direction of the links seem to do whatever direction suits best. I would like them to always go right side out left side in. In many cases this is going OK but in this example the diagram doing it (for me) the wrong way.
Is there any way in correcting this?
I tried direction: "r" on the nodes with no success.
This is part(!) of my JSON:"
{
"nodes": [{
"id": "01 - Homepage",
"direction": "r"
},
{
"id": "02 - Park - Homepages",
"direction": "r"
},
{
"id": "03 - Park - Content pages",
"direction": "r"
},
{
"id": "04 - Park - Acco(detail)",
"direction": "r"
}
],
"links": [{
"source": "Enter",
"target": "02 - Park - Homepages",
"value": 64223,
"type": "enter"
},
{
"source": "02 - Park - Homepages",
"target": "04 - Park - Acco(detail)",
"value": 55397,
"type": "homepage"
},
{
"source": "04 - Park - Acco(detail)",
"target": "Exit",
"value": 45991,
"type": "exit"
},
{
"source": "02 - Park - Homepages",
"target": "Exit",
"value": 38980,
"type": "exit"
},
{
"source": "Enter",
"target": "01 - Homepage",
"value": 34687,
"type": "enter"
},
{
"source": "02 - Park - Homepages",
"target": "03 - Park - Content pages",
"value": 30135,
"type": "homepage"
}
],
"groups": [{
"type": "Group",
"title": "Enter",
"bundle": null,
"id": "Enter",
"nodes": ["Enter"],
"def_pos": null
},
{
"type": "Group",
"title": "Broad - Shopping",
"bundle": null,
"id": "Broad",
"nodes": ["01 - Homepage", "05 - Theme - Home", "06 - Theme - Content", "07 - Offer - Overview", "08 - Offer - Detail", "09 - Search - Lists results", "11 - Maps", "14 - Site-Content"],
"def_pos": null
},
{
"type": "Group",
"title": "Narrow - Shopping",
"bundle": null,
"id": "Narrow",
"nodes": ["02 - Park - Homepages", "03 - Park - Content pages", "04 - Park - Acco(detail)", "15 - Compare"],
"def_pos": null
},
{
"type": "Group",
"title": "Booking",
"bundle": null,
"id": "Booking",
"nodes": ["12 - IBE", "17 - My Vacation", "NoMatch"],
"def_pos": null
},
{
"type": "Group",
"title": "Pre Holiday",
"bundle": null,
"id": "Pre Holiday",
"nodes": ["13 - My Account"],
"def_pos": null
},
{
"type": "Group",
"title": "Exit",
"bundle": null,
"id": "Exit",
"nodes": ["Exit"],
"def_pos": null
}
],
"order": [
[
["Enter"],
[],
[]
],
[
["01 - Homepage", "05 - Theme - Home", "06 - Theme - Content", "07 - Offer - Overview", "08 - Offer - Detail", "09 - Search - Lists results", "11 - Maps", "14 - Site-Content"],
[],
[]
],
[
["02 - Park - Homepages", "03 - Park - Content pages", "04 - Park - Acco(detail)", "15 - Compare"],
[],
[]
],
[
["12 - IBE", "17 - My Vacation", "NoMatch"],
[],
[]
],
[
["13 - My Account"],
[],
[]
],
[
["Exit"],
[],
[]
]
],
"rankSets": [{
"type": "min",
"nodes": ["Enter"]
},
{
"type": "min",
"nodes": ["01 - Homepage", "05 - Theme - Home", "06 - Theme - Content", "07 - Offer - Overview", "08 - Offer - Detail", "09 - Search - Lists results", "11 - Maps", "14 - Site-Content"]
},
{
"type": "min",
"nodes": ["02 - Park - Homepages", "03 - Park - Content pages", "04 - Park - Acco(detail)", "15 - Compare"]
},
{
"type": "min",
"nodes": ["12 - IBE", "17 - My Vacation", "NoMatch"]
},
{
"type": "min",
"nodes": ["13 - My Account"]
}, {
"type": "min",
"nodes": ["Exit"]
}
]
}
See example: ricklupton/floweaver#21
I wonder if the problem is the R -> L
link which is positioned at the bottom of the node, where the self-link should be?
I might be missing a piece of the puzzle but I'm looking for the source of a complete example using d3-sankey-diagram.
I'm mostly missing the svg creation part.
This is the source of the github page, but it's transpiled and therefore hard to read:
https://github.com/ricklupton/d3-sankey-diagram/blob/gh-pages/d3-sankey-diagram.js
I'm currently coming from d3-sankey where d3.sankeyLinkHorizontal
is used to extract the svg pathdata for the links. This function doesn't seem to exist when using d3-sankey-diagram
.
Love the package so far, but would be great to be able to pass custom strings to append to the label (i.e. d.custlabel) when passing the links. Use case for me would be for including in addition to the total count the percent of records from a particular node moving down a given forward.
const fmt = d3.format('.3s')
function linkTitle(d) {
const parts = []
const sourceTitle = nodeTitle(d.source)
const targetTitle = nodeTitle(d.target)
const matTitle = d.type
parts.push(`${sourceTitle} โ ${targetTitle}`)
if (matTitle) parts.push(matTitle)
parts.push(fmt(d.value))
return parts.join('\n')
}
const fmt = d3.format('.3s')
function linkTitle(d) {
const parts = []
const sourceTitle = nodeTitle(d.source)
const targetTitle = nodeTitle(d.target)
const matTitle = d.type
const custTitle = d.custlabel
parts.push(`${sourceTitle} โ ${targetTitle}`)
if (matTitle) parts.push(matTitle)
parts.push(fmt(d.value))
if (custTitle) parts.push(custTitle)
return parts.join('\n')
}
When a nodeWidth of more than 1 is used some circular link paths are drawn angled / rotated.
Example code:
https://codesandbox.io/s/nodewidth-d3-sankey-diagram-phmpr
Copy of example code
import * as d3Base from "d3";
import * as sankey from "d3-sankey-diagram";
const d3 = {
...d3Base,
...sankey
};
var data = {
nodes: [
{
id: "a",
title: "a",
direction: "r"
},
{
id: "b",
title: "b",
direction: "r"
},
{
id: "c",
title: "c",
direction: "r"
}
],
links: [
{
source: "a",
target: "b",
value: 1
},
{
source: "b",
target: "c",
value: 1.2
},
{
source: "c",
target: "a",
value: 0.2
}
]
};
var svg = d3.select("svg");
var width = +svg.attr("width");
var height = +svg.attr("height");
var margin = { top: 10, left: 50, bottom: 10, right: 50 };
var layout = d3
.sankey()
.nodeWidth(30)
.extent([
[margin.left, margin.top],
[width - margin.left - margin.right, height - margin.top - margin.bottom]
]);
// Render
var color = d3.scaleOrdinal(d3.schemeCategory10);
var diagram = d3
.sankeyDiagram()
.linkMinWidth(() => 0.1)
.linkColor(d => color(d.type));
svg.datum(layout(data)).call(diagram);
This one kind of builds on #22. Node groups clearly states "should be given in the following format" which then gives a clear example.
I noticed that the expected format for links
, nodes
data on the other hand is kind of undocumented. Especially since there are properties like the following which are otherwise only documented elsewhere.
links
โ type
Used in default linkTitle
nodes
โ direction
Used in default nodeBackwards
With d3-sankey I have to manually draw the svg for the links, but this does give me more control, for example to add gradients. What would be the best way to tackle this in d3-sankey-diagram?
I've used Vue to handle the actual drawing of the svg, here's an example of te links drawing part, where I include a linearGradient
per link:
<g id="links" fill="none" stroke-opacity="0.5">
<g
v-for="link in links"
:key="`${link.source.name}>${link.target.name}`"
style="mix-blend-mode: multiply"
>
<linearGradient
:id="link.uid"
gradientUnits="userSpaceOnUse"
:x1="link.source.x1"
:x2="link.target.x0"
>
<stop offset="0%" :stop-color="color(link.source.name)"></stop>
<stop offset="100%" :stop-color="color(link.target.name)"></stop>
</linearGradient>
<path
:d="pathData(link)"
:stroke="`url(#${link.uid})`"
:stroke-width="Math.max(minStrokeWidth, link.width)"
></path>
<title>{{ linkTitleText(link) }}</title>
</g>
</g>
(This approach also works when loading the svg into applications like Inkscape and Illustrator)
Hi @ricklupton,
Thanks for your great library. It's really awesome.
I would like to integrate it with Angular 5.
Have you tested if this works with Angular 5?
Thanks
Is it possible to export the generated diagram as an image like a SVG or similar?
I'm aware you can customize part of the link title using linkTypeTitle
but I couldn't find a way of completely configuring the link title.
If links
includes duplicates as far as (source, target, type) is concerned, the later links aren't processed -- and you then get errors if you try to render them
excuse me, when i read the source code.
i wonder why if (true) {...} else {...}
?
test code?
d3-sankey-diagram/src/linkPath.js
Lines 119 to 141 in d254b32
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.