Skip to main content
v2026.11,858 entries · CC-BY 4.0

Sankey Diagram in Python: Plotly and Matplotlib Guide

Build a Sankey diagram in Python with Plotly or matplotlib, map pandas tables to source and target indices, and export vector figures for a paper.

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)

  1. Import the graph objects module: import plotly.graph_objects as go.
  2. Create the figure with a go.Sankey trace. 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])
    )])
  3. Add a title and font size: fig.update_layout(title_text="Basic Sankey Diagram", font_size=10).
  4. 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):

  1. Start from a DataFrame with columns from, to and n (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.
  2. Build the label list: labels = list(pd.unique(df[["from", "to"]].values.ravel())).
  3. Map names to positions: idx = {name: i for i, name in enumerate(labels)}.
  4. Pass the mapped columns to the link: link=dict(source=df["from"].map(idx), target=df["to"].map(idx), value=df["n"]), and node=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

  1. Import: import matplotlib.pyplot as plt and from matplotlib.sankey import Sankey.
  2. Create an Axes: fig, ax = plt.subplots().
  3. 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()
  4. Save with fig.savefig("balance.pdf") or show with plt.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, text and texts, which you can use to restyle labels afterwards.

Export a Sankey figure for a paper

  1. Install Kaleido 1.0.0 or later and Chrome as described above.
  2. Write the file: fig.write_image("fig1.png"). Plotly lists PNG, JPEG, WebP, SVG and PDF as supported formats, so use fig.write_image("fig1.pdf") or .svg when the journal wants vector graphics.
  3. To export several figures at once, Plotly provides plotly.io.write_images.
  4. 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.
  5. 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)

Related CASRAI guides: bar plots in matplotlib and box plots in matplotlib.

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Sankey Diagram in Python: Plotly and Matplotlib Guide

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →