Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ricklupton/d3-sankey-diagram
Sankey diagram for d3
https://github.com/ricklupton/d3-sankey-diagram
Last synced: 8 days ago
JSON representation
Sankey diagram for d3
- Host: GitHub
- URL: https://github.com/ricklupton/d3-sankey-diagram
- Owner: ricklupton
- License: mit
- Created: 2016-03-07T15:28:01.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2024-07-19T11:44:08.000Z (4 months ago)
- Last Synced: 2024-10-25T08:38:05.767Z (15 days ago)
- Language: JavaScript
- Homepage: https://ricklupton.github.io/d3-sankey-diagram
- Size: 1.63 MB
- Stars: 107
- Watchers: 6
- Forks: 35
- Open Issues: 25
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome-starred - ricklupton/d3-sankey-diagram - Sankey diagram for d3 (others)
README
# d3-sankey-diagram
[![Build Status](https://travis-ci.org/ricklupton/d3-sankey-diagram.svg?branch=master)](https://travis-ci.org/ricklupton/d3-sankey-diagram)
[Sankey diagrams](https://en.wikipedia.org/wiki/Sankey_diagram) show flows between processes or relationships between sets. This library, is a [reusable d3 diagram](https://bost.ocks.org/mike/chart/) featuring:
- automatic layout
- multiple types of flow
- loops / reversed flows
- flow routing across layersSee the **[demo](https://ricklupton.github.io/d3-sankey-diagram)** for examples of these.
d3-sankey-diagram versions v0.6 and up are based on d3 v4.
## Installation
Install using npm if you are using browserify or the like:
```js
npm install d3-sankey-diagram
```Or download the [standalone bundle](https://github.com/ricklupton/d3-sankey-diagram/releases/latest) and include in your page as
```html```
## Usage
```js
var layout = d3.sankey()
.extent([[100, 10], [840, 580]]);var diagram = d3.sankeyDiagram()
.linkColor(function(d) { return d.color; });d3.json('uk_energy.json', function(energy) {
layout.ordering(energy.order);
d3.select('#sankey')
.datum(layout(energy))
.call(diagram);
});
```Try more [live examples](https://ricklupton.github.io/d3-sankey-diagram).
If you use the Jupyter notebook, try
[ipysankeywidget](https://github.com/ricklupton/ipysankeywidget).`d3-sankey-diagram` works both in node (using jsdom) and in the browser.
## Contributing 🎁
Thanks for your interest in contributing! To get started see [CONTRIBUTING.md](CONTRIBUTING.md) and our [code of
conduct](CODE_OF_CONDUCT.md). We have a [Roadmap](https://github.com/ricklupton/d3-sankey-diagram/projects/1) showing what we
are working on, and you can browse the list of [good first issues](https://github.com/ricklupton/d3-sankey-diagram/labels/good%20first%20bug) for ideas.## Documentation
d3-sankey-diagram is a JavaScript library for creating [Sankey diagrams](https://en.wikipedia.org/wiki/Sankey_diagram) using [d3](https://d3js.org/). See the [live examples](https://ricklupton.github.io/d3-sankey-diagram) to get an idea of what it does.The main high-level components:
- [d3.sankey](#sankey): Sankey layout
- [d3.sankeyDiagram](#sankeyDiagram): SVG Sankey diagram componentLower-level components:
- [d3.sankeyNode](#sankeyNode): SVG Sankey node component
- [d3.sankeyLink](#sankeyLink): SVG path generator for Sankey diagram links
- [d3.sankeyLinkTitle](#sankeyLinkTitle): Helper function to generate link titles### Sankey layout
# sankey()
Creates a new Sankey layout component.
# layout(arguments...)
Apply the layout to the given arguments. The arguments are
arbitrary; they are simply propagated to the accessor functions
[nodes](#layout-nodes) and [links](#layout-links).```js
var layout = d3.sankey();var graph = layout({
nodes: [
{"id": "a", "title": "Source"},
{"id": "b", "title": "Stage 1"}
],
links: [
{"source": "a", "target": "b", "type": "x", "value": 2}
]
});
// Resulting graph object can be used as datum for d3.sankeyDiagram() below
```#### Data accessors
# layout.nodes([nodes])
If *nodes* is specified, sets the nodes accessor to the specified function and
returns this layout. If *nodes* is not specified, return the current accessor, which defaults to:
```js
function nodes(graph) {
return graph.nodes;
}
```# layout.links([links])
If *links* is specified, sets the links accessor to the specified function and
returns this layout. If *links* is not specified, return the current accessor, which defaults to:
```js
function links(graph) {
return graph.links;
}
```# layout.nodeId([nodeId])
If *nodeId* is specified, sets the node id accessor to the specified function and
returns this layout. If *nodeId* is not specified, return the current accessor, which defaults to:
```js
function nodeId(d) {
return d.id;
}
```# layout.sourceId([sourceId])
# layout.targetId([targetId])If *sourceId*/*targetId* is specified, sets the link source/target id accessor to the specified function and
returns this layout. If *sourceId*/*targetId* is not specified, return the current accessor, which defaults to:
```js
function sourceId (d) {
return {
id: typeof d.source === 'object' ? d.source.id : d.source,
port: typeof d.sourcePort === 'object' ? d.sourcePort.id : d.sourcePort
}
}
// similarly for targetId
```See below for more discussion of ports. If this accessor returns a string, it is
interpreted as the node id and the port is set to `undefined`.# layout.nodeBackwards([nodeBackwards])
If *nodeBackwards* is specified, sets the node direction accessor to the specified function and
returns this layout. If *nodeBackwards* is not specified, return the current accessor, which defaults to:
```js
function nodeBackwards(d) {
return d.direction && d.direction.toLowerCase() === 'l';
}
```# layout.linkValue([linkValue])
If *linkValue* is specified, sets the link value accessor to the specified function and
returns this layout. If *linkValue* is not specified, return the current accessor, which defaults to:
```js
function linkValue(d) {
return d.value;
}
```#
layout.linkType([linkType])If *linkType* is specified, sets the link type accessor to the specified function and
returns this layout. If *linkType* is not specified, return the current accessor, which defaults to:
```js
function linkType(d) {
return d.type;
}
```#### Adjusting layout
# layout.ordering([ordering])
If *ordering* is specified, sets the node ordering to the specified value and
returns this layout. If *ordering* is not specified, return the current value, which defaults to `null`.When *ordering* is null, the node ordering will be calculated automatically.
When *ordering* is specified, it is used directly and no rank assignment or
ordering algorithm takes place. The *ordering* structure has three nested lists:
*ordering* is a list of layers, each of which is a list of bands, each of which is
a list of node ids. For example,
```js
[
[ ["layer 1 band 1"], ["layer 1 band 2"] ],
[ ["layer 2 band 1"], ["layer 2 band 2"] ],
...
]
```# layout.rankSets([rankSets])
If *rankSets* is specified, sets the rank sets to the specified value and
returns this layout. If *rankSets* is not specified, return the current value,
which defaults to `[]`.Rank sets are optional constraints to keep nodes in the same layer. Each entry
has the form
```js
{
type: 'same|min', // optional, default 'min'
nodes: [node ids], // required
}
```# layout.sortPorts([sortPorts])
If *sortPorts* is specified, sets the port sorting function to the specified
function and returns this layout. If *sortPorts* is not specified, return the
current value, which defaults to:
```js
function sortPorts(a, b) {
return a.id.localeCompare(b.id)
}
```**Note**: in a future version this may be changed to sort ports to avoid
crossings by default.#### Dimensions
# layout.extent([extent])
If *extent* is specified, sets this layout’s extent to the specified array of
points [[*x0*, *y0*], [*x1*, *y1*]], where [*x0*, *y0*] is the top-left corner
and [*x1*, *y1*] is the bottom-right corner, and returns this tile layout. If
*extent* is not specified, returns the current layout extent.# layout.size([size])
If *size* is specified, sets this layout’s size to the specified two-element
array of numbers [*width*, *height*] and returns this layout. If *size* is not
specified, returns the current layout size. This is a convenience method
equivalent to setting the [extent](#layout-extent) to [[0, 0], [*width*,
*height*]].# diagram.scale([scale])
If *scale* is specified as a number, sets the layout's scale (from data units to
pixels). If *scale* is `null`, the scale will be reset and automatically
calculated the next time the diagram is called, to achieve the desired
whitespace fraction (below). If *scale* is not specified, return the current
scale.# diagram.whitespace([whitespace])
If *whitespace* is specified as a number, sets the layout's whitespace fraction,
used when automatically calculating a scale (above). If *whitespace* is not
specified, return the current whitespace.#### Manual positioning
# diagram.nodePosition([nodePosition])
If *nodePosition* is specified, use the specified function to directly set node
positions, bypassing the layout algorithm (link positions and shapes are still
calculated). If *nodePosition* is not specified, return the current
function, which defaults to `null`.### SVG Sankey diagram component
# sankeyDiagram()
Creates a new Sankey diagram component.
# diagram(selection)
Apply the diagram to a selection, which should be an `svg` element.
```js
var diagram = d3.sankeyDiagram();
d3.select('#sankey')
.datum(sankey)
.call(diagram);
```The Sankey data is taken from the selection's bound data, which should be a
graph object, as generated by [d3.sankey](#sankey).#### Dimensions
# diagram.margin({ [top], [right], [bottom], [left] })
If called with an argument, set the margins of the diagram, otherwise return the
current value.#### Titles
# diagram.nodeTitle([nodeTitle])
If called with an argument, set the node title to the specified function,
otherwise return the current function, which defaults to:
```js
function nodeTitle(d) {
return d.title !== undefined ? d.title : d.id;
}
```# diagram.nodeValue([nodeValue])
If called with an argument, set the node value getter to the specified function,
otherwise return the current function, which defaults to:
```js
function nodeValue(d) {
return null;
}
```The node value is shown with an SVG text element within the node body, only when
the `nodeWidth` is greater than zero.#
diagram.linkTitle([linkTitle])If called with an argument, set the link title to the specified function,
otherwise return the current function, which defaults to:
```js
const fmt = d3.format('.3s')
function linkTitle(d) {
const parts = []
const sourceTitle = nodeTitle(d.source)
const targetTitle = nodeTitle(d.target)
const matTitle = d.typeparts.push(`${sourceTitle} → ${targetTitle}`)
if (matTitle) parts.push(matTitle)
parts.push(fmt(d.value))
return parts.join('\n')
}
```The link title is displayed in an SVG `title` element, visible on hover.
To make it easier to customise this function to your data, you can use
`d3.sankeyLinkTitle` to generate new functions:# sankeyLinkTitle(nodeTitle, typeTitle, fmt)
Generates a function similar to the one above, with custom accessors for the
node title `nodeTitle`, link-type title `typeTitle` and number format `fmt`.# diagram.linkLabel([linkLabel])
If called with an argument, set the link label to the specified function,
otherwise return the current function, which defaults to:
```js
function linkLabel(d) {
return null
}
```The link label is displayed in an SVG `text` element, so unlike the /title/, it
is visible all the time.#### Link appearance
# diagram.linkColor([linkColor])
If *linkColor* is specified, sets the link color accessor to the specified
function, otherwise return the current accessor, which defaults to:
```js
function linkColor(d) {
return null;
}
```# diagram.linkMinWidth([linkMinWidth])
If *linkMinWidth* is specified, sets the minimum link width accessor to the
specified function, otherwise return the current accessor, which by default
returns `1`.#### Node groups
# diagram.groups([groups])
If *groups* is specified, sets the list of node groups to the specified value.
If *groups* is not specified, return the current list. Node groups display a box
around the specified nodes, and should be given in the following format:
```js
[
{ title: "Group title to be displayed above nodes",
nodes: ["nodeid1", "nodeid", ...]
}
]
```#### Events
# diagram.on(type[, listener])
Adds or removes an event *listener* for the specified *type*. The *type* string
is one of `selectNode`, `selectLink` or `selectGroup`. The *listener* is invoked
with the context as the element and one argument, the corresponding data.If *listener* is not specified, returns the currently-assigned listener for the
specified *type*, if any.## Tests
Run the tests:
```js
npm test
```## Licence
MIT licence.
## Contributors
- Rick Lupton
- @harisbal
- @svwielga4## Changelog
### Unreleased
### v0.8.0
- Modified code to assign `type` and `value` attributes to `link` objects
(thanks @harisbal)
- EXPERIMENTAL: allow short "stub" flows in or out of nodes using
`fromElsewhere` and `toElsewhere` attributes. These can be useful for
simplifying diagrams by avoiding uninteresting flows while still showing how
nodes balance.### v0.7.3
- Fix packaging to avoid overwriting the d3 global object in UMD module!
- Add a basic link label feature to show the values of links with SVG text
elements. See [`diagram.linkLabel`](#linkLabel). (#2)### v0.7.2
- Update packaging to produce different module builds:
- `d3-sankey-diagram.esm.js`: An ES module
- `d3-sankey-diagram.cjs.js`: A CommonJS module for use with NodeJS
- `d3-sankey-diagram.umd.js`: A UMD module for use in `` tags
- `d3-sankey-diagram.min.js`: A minified version of the UMD module### v0.7.1
- Unused code tidied up (thanks svwielga4)
- Improved node value labels: with wide nodes, the labels are shown within the
nodes. Otherwise they are now shown after the node titles in parentheses.
**[See
example](https://bl.ocks.org/ricklupton/8a9a9501883a5645202cb439def65d31)**.
- By default, node values are not shown by default (the default in v0.7.0 was to
show them). Use [`diagram.nodeValue`](#nodeValue) to set the format to show
them.### v0.7.0
- Add option to show node values, customizable with
[`diagram.nodeValue`](#nodeValue).