Written and maintained by CASRAI Editorial Board
Last updated
A Sankey diagram draws flows between stages as bands whose width is proportional to the quantity moving. In Python there are two routes: Plotly’s go.Sankey for multi-stage source-to-target flows (the usual choice for figures in papers), and matplotlib’s matplotlib.sankey.Sankey for a single flow-balance diagram where inputs and outputs must add up. This guide shows both, how to turn a pandas table into the integer indices Plotly requires, and how to export a figure for a manuscript.
What a Sankey diagram is used for in research
Matplotlib’s documentation describes a Sankey diagram as “a specific type of flow diagram, in which the width of the arrows is shown proportionally to the flow quantity.” Researchers use the form wherever a quantity is split and recombined across stages: participants moving between study arms and outcome categories, funding moving between sources and uses, records passing through screening steps, or categories reassigned between two classification schemes. Read the width of each band as the amount; the diagram shows no uncertainty, so pair it with a table of the underlying counts.
Choose a library: Plotly or matplotlib
| Question | Plotly go.Sankey |
matplotlib Sankey |
|---|---|---|
| Data shape | Link lists: source, target, value, plus node labels |
A list of flows: inputs positive, outputs negative |
| Stages | Any number of stages in one figure | One subdiagram per add() call, joined with connect and prior |
| Balance rule | Nodes are laid out from the links you supply | The scale is set so the product of scale and the sum of the inputs is about 1.0; flows are meant to balance |
| Output | Interactive figure; static export needs Kaleido | A normal matplotlib figure, saved with savefig |
If your data is a table of “from, to, count” rows with several stages, use Plotly. If you have one process with a few inputs and outputs that must balance (an energy or budget balance, for example) and want the result inside an existing matplotlib figure, use matplotlib.
Install or access
From the Plotly getting-started page: pip install plotly or conda install -c conda-forge plotly. Matplotlib ships its Sankey class as matplotlib.sankey; install matplotlib with pip or conda (see the matplotlib install documentation). To show Plotly figures in JupyterLab, the Plotly page says to install jupyterlab and anywidget in the same environment as plotly.
Static image export from Plotly (needed for journal figures) requires Kaleido 1.0.0 or greater: pip install --upgrade kaleido (or conda install -c conda-forge python-kaleido). Kaleido uses Chrome, which is not bundled from version 1.0.0; install it with the plotly_get_chrome command or, from Python, import plotly.io as pio then pio.get_chrome().
Step-by-step: build a Sankey diagram with Plotly
A minimal example (this is the Plotly documentation’s basic example)
- Import the graph objects module:
import plotly.graph_objects as go. - Create the figure with a
go.Sankeytrace. Nodes carry the labels; links reference nodes by position in that label list:fig = go.Figure(data=[go.Sankey( node=dict(pad=15, thickness=20, line=dict(color="black", width=0.5), label=["A1", "A2", "B1", "B2", "C1", "C2"], color="blue"), link=dict(source=[0, 1, 0, 2, 3, 3], target=[2, 3, 3, 4, 4, 5], value=[8, 4, 2, 8, 4, 2]) )]) - Add a title and font size:
fig.update_layout(title_text="Basic Sankey Diagram", font_size=10). - Display it with
fig.show().
source and target are indices into the node label list: link 0 above runs from node 0 (“A1”) to node 2 (“B1”) with value 8.
From a pandas table of counts to source and target indices
Real data usually arrives as names, not integers. Because the Plotly indices correspond to positions in the label list, build that list once and map names to positions. The following is a general pandas pattern applied to the Plotly structure above (the column names are your own):
- Start from a DataFrame with columns
from,toandn(one row per flow). If a category appears at two stages and should be drawn as two nodes, give the stage-specific names (for example “Enrolled” and “Analysed”) so they are different labels. - Build the label list:
labels = list(pd.unique(df[["from", "to"]].values.ravel())). - Map names to positions:
idx = {name: i for i, name in enumerate(labels)}. - Pass the mapped columns to the link:
link=dict(source=df["from"].map(idx), target=df["to"].map(idx), value=df["n"]), andnode=dict(label=labels).
If you start from a cross-tabulation, df.stack() or melt produces the three columns. For general pandas column handling see adding a column in pandas.
Node order and direction (Plotly 7.0)
The Plotly Sankey page notes that node.sort and link.sort are new in 7.0 (setting them to "input" keeps the order you supplied) and that direction is new in 7.0 (default "forward", or "reversed"). link.hovercolor is available since 5.19.
Step-by-step: a flow-balance diagram with matplotlib
- Import:
import matplotlib.pyplot as pltandfrom matplotlib.sankey import Sankey. - Create an Axes:
fig, ax = plt.subplots(). - Build the diagram by chaining. Per the documentation, inputs are positive and outputs negative, and
add()returns the Sankey instance so calls can be chained:Sankey(ax=ax, unit=None).add( flows=[1.0, -0.6, -0.4], labels=["Input", "Output A", "Output B"], orientations=[0, 0, 0], ).finish() - Save with
fig.savefig("balance.pdf")or show withplt.show().
Here orientations of 0 puts inputs on the left and outputs on the right; 1 places a flow at the top and -1 at the bottom. unit=None suppresses quantity labels; otherwise format (default '%G') controls how quantities are written. To chain a second subdiagram, pass prior (the index of the previous diagram) and connect to the next add() call.
Output and how to read it
- Band width is proportional to the flow quantity; compare widths within one figure only.
- In matplotlib,
finish()returns a list of information about the subdiagram(s):patch,flows,angles,tips,textandtexts, which you can use to restyle labels afterwards.
Export a Sankey figure for a paper
- Install Kaleido 1.0.0 or later and Chrome as described above.
- Write the file:
fig.write_image("fig1.png"). Plotly lists PNG, JPEG, WebP, SVG and PDF as supported formats, so usefig.write_image("fig1.pdf")or.svgwhen the journal wants vector graphics. - To export several figures at once, Plotly provides
plotly.io.write_images. - Check your target journal’s figure specification (format, resolution, size) in its author instructions before choosing a format; the requirement is set by the journal, not by the library.
- Put the full data table and a short figure legend beside the figure, and keep the script and data under version control so the figure can be regenerated; see computational reproducibility.
Troubleshooting
The figure is empty or links go to the wrong nodes
Plotly’s source and target must be integer positions in the label list, not names. Rebuild the mapping and confirm the label list has no duplicates you did not intend.
write_image fails
Plotly’s export page requires Kaleido 1.0.0 or greater and notes that Kaleido uses Chrome, which is not bundled from 1.0.0. Run pip install --upgrade kaleido, then install Chrome with plotly_get_chrome or pio.get_chrome().
Quantities do not appear on a matplotlib Sankey
With unit=None no quantities are labelled. Set unit to a string to show them, and use format to control the number format.
The matplotlib diagram is far too large or small
Adjust scale; the documentation describes the product of scale and the sum of the inputs as approximately 1.0.
Free alternatives
| Option | Best for | Source |
|---|---|---|
Plotly go.Sankey |
Multi-stage source/target/value data, interactive and exportable figures | plotly.com/python/sankey-diagram |
matplotlib matplotlib.sankey |
Single flow-balance diagrams inside a matplotlib figure | matplotlib.org/stable/api/sankey_api.html |
FAQ
How do I make a Sankey diagram in Python?
Use plotly.graph_objects.Sankey with a node label list and link source, target and value lists, or matplotlib.sankey.Sankey for a single balanced flow. Both are free and open source; the steps are above.
Can matplotlib draw a Sankey diagram?
Yes, with matplotlib.sankey.Sankey. It builds flow-balance diagrams from a list of positive inputs and negative outputs rather than from a source/target table.
Which format should I export for a journal?
Plotly can write PNG, JPEG, WebP, SVG and PDF. Choose according to the journal’s own figure instructions.
Sources (official documentation)
- Matplotlib, Sankey API (matplotlib 3.11.2): matplotlib.org/stable/api/sankey_api.html, read 2026-11-08.
- Plotly, Sankey diagrams in Python: plotly.com/python/sankey-diagram, read 2026-11-08.
- Plotly, Static image export in Python: plotly.com/python/static-image-export, read 2026-11-08.
- Plotly, Getting started: plotly.com/python/getting-started, read 2026-11-08.
Related CASRAI guides: bar plots in matplotlib and box plots in matplotlib.







