Function Repository Resource:

 SankeyChart

Source Notebook

Generate a Sankey (flow) diagram from a directed weighted graph, matrix or OptimumFlowData object

Contributed by: Jon McLoone

ResourceFunction["SankeyChart"][g]

draws a Sankey flow diagram for the directed, weighted graph g (a matrix or OptimumFlowData object also work).

Details and Options

ResourceFunction["SankeyChart"][g] draws a Sankey flow diagram for a directed graph g whose edges carry an EdgeWeight (the flow along that edge).
Layout: vertices are assigned to layers (columns) by longest path from a source, so every edge points strictly left-to-right; within a layer, vertex order is refined by several barycenter-heuristic sweeps to reduce ribbon crossings; node height is proportional to vertex capacity, and each edge is drawn as a smooth Bezier ribbon whose thickness equals its flow.
Besides a Graph, ResourceFunction["SankeyChart"] also accepts adjacency matrices and OptimumFlowData objects.
"ColorFunction"Automaticcustom vertex-to-color function; ignored if VertexColors is set.
"EdgeOffset"0how far ribbons reach into each vertex, 0 to 1.
"EdgeOpacity"0.55opacity of edge ribbons, tinted by their source vertex.
"EdgeOrigin"Leftside sources are drawn on: Left,Right,Top, or Bottom.
"EdgeValuePositions"Noneposition of each edge's flow-value label; None hides it.
"LabelStyle""Text"style applied on top of BaseStyle for all labels.
"LayerSpacing"1scales spacing between layer columns along the x-axis.
"OrderIterations"8number of barycenter sweeps used to reduce ribbon crossings.
"ValueFormat"Automaticformatting function applied to displayed vertex/edge values.
"VertexColors"Automaticsets vertex colors: list, association, scheme name, or integer.
"VertexLabelPosition"{Center,Center}position of each vertex's name label relative to its box.
"VertexLabels"Automaticnames vertices when ResourceFunction["SankeyChart"] is given a matrix, or renames the vertices of a Graph.
"VertexPositions"Automaticoverrides the automatic {x,y} center of specific vertices.
"VertexRenderingFunction"Automaticcustom shape function replacing the default rectangle vertex.
"VertexSpacing"0.2fraction of column height used as gaps between vertices.
"VertexValuePosition"Noneposition of each vertex's flow-value label; None hides it.
"VertexWidth"1/12width of each vertex rectangle, in LayerSpacing units.
SankeyChart also accepts every standard Graphics option (AspectRatio, ImageSize, Background, PlotLabel, Epilog, BaseStyle, Frame, etc.), passed straight through to the underlying Graphics call.

Examples

Basic Examples (3) 

A simple diamond-shaped flow network: one source splits two ways, then re-merges:

In[1]:=
ResourceFunction["SankeyChart"][
 Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}]]
Out[1]=

A many-to-one-to-many network: four power sources feed a single grid hub, which fans out to four consumer categories:

In[2]:=
g2 = Graph[{"Coal", "Gas", "Solar", "Wind", "Grid", "Residential", "Commercial", "Industrial", "Losses"}, {"Coal" \[DirectedEdge] "Grid", "Gas" \[DirectedEdge] "Grid", "Solar" \[DirectedEdge] "Grid", "Wind" \[DirectedEdge] "Grid", "Grid" \[DirectedEdge] "Residential", "Grid" \[DirectedEdge] "Commercial", "Grid" \[DirectedEdge] "Industrial", "Grid" \[DirectedEdge] "Losses"}, EdgeWeight -> {40, 30, 20, 10, 45, 35, 15, 5}];
ResourceFunction["SankeyChart"][g2]
Out[3]=

A four-layer manufacturing network (raw materials -> processing -> assembly -> products), with cross-wiring between layers:

In[4]:=
g3 = Graph[{"RM1", "RM2", "RM3", "P1", "P2", "P3", "A1", "A2", "Prod1", "Prod2", "Prod3"}, {"RM1" \[DirectedEdge] "P1", "RM1" \[DirectedEdge] "P2", "RM2" \[DirectedEdge] "P2", "RM2" \[DirectedEdge] "P3", "RM3" \[DirectedEdge] "P3", "P1" \[DirectedEdge] "A1", DirectedEdge["P2", "A1"], "P2" \[DirectedEdge] "A2", "P3" \[DirectedEdge] "A2", "A1" \[DirectedEdge] "Prod1", "A1" \[DirectedEdge] "Prod2", "A2" \[DirectedEdge] "Prod2", "A2" \[DirectedEdge] "Prod3"}, EdgeWeight -> {20, 10, 15, 10, 15, 20, 10, 15, 25, 18, 12, 20, 20}];
ResourceFunction["SankeyChart"][g3]
Out[5]=

Scope (3) 

SankeyChart will interpret a square matrix of integers as a WeightedAdjacenecyGraph, with zero or Infinity indicating no flow. The vertices will be labeled 1,2,…,n unless you provide a "VertexLabels":

In[6]:=
matrix = {{0, 12, 8, 0}, {0, 0, 0, 12}, {0, 0, 0, 8}, {0, 0, 0, 0}};
ResourceFunction["SankeyChart"][matrix, "VertexLabels" -> {"A", "B", "C", "D"}]
Out[7]=

SankeyChart can also accept OptimimFlowData objects generated by FundMaximalFlow:

In[8]:=
flowGraph = \!\(\*
GraphicsBox[
NamespaceBox["NetworkGraphics",
DynamicModuleBox[{Typeset`graph = HoldComplete[
Graph[{1, 2, 3, 4}, {{{1, 2}, {1, 3}, {2, 4}, {3, 4}}, Null}, {EdgeCapacity -> {10, 5, 10, 5}}]]}, 
TagBox[GraphicsGroupBox[
         GraphicsComplexBox[{{-1.8369701987210297`*^-16, 1.}, {1., 1.2246467991473532`*^-16}, {-1., -2.4492935982947064`*^-16}, {6.123233995736766*^-17, -1.}}, {
{Hue[0.6, 0.7, 0.7], Opacity[0.7], CapForm["Round"], Arrowheads[
            Medium], ArrowBox[{{1, 2}, {1, 3}, {2, 4}, {3, 4}}, 0.02261146496815286]}, 
{Hue[0.6, 0.5, 1.], EdgeForm[{GrayLevel[0], Opacity[0.7]}], DiskBox[1, 0.02261146496815286], DiskBox[2, 0.02261146496815286], DiskBox[3, 0.02261146496815286], DiskBox[4, 0.02261146496815286]}}]],
MouseAppearanceTag["NetworkGraphics"]],
AllowKernelInitialization->False]],
DefaultBaseStyle->"NetworkGraphics",
FormatType->TraditionalForm,
FrameTicks->None,
ImageSize->{74.359375, Automatic}]\);
flowData = FindMaximumFlow[flowGraph, 1, 4, "OptimumFlowData"];
ResourceFunction["SankeyChart"][flowData]
Out[10]=

"Backflow" is supported:

In[11]:=
ResourceFunction["SankeyChart"][
 Graph[{"S", "A", "B", "D", "F"}, {"S" \[DirectedEdge] "A", "D" \[DirectedEdge] "A", "A" \[DirectedEdge] "B", "D" \[DirectedEdge] "B", "B" \[DirectedEdge] "D", "D" \[DirectedEdge] "F"}, EdgeWeight -> {10, 3, 13, 2, 15, 10}]]
Out[11]=

Options (20) 

EdgeOrigin (1) 

Controls which side sources are drawn on -- Left, Right, Top, or Bottom -- and hence which direction flow travels across the page:

In[12]:=
g2 = Graph[{"Coal", "Gas", "Solar", "Wind", "Grid", "Residential", "Commercial", "Industrial", "Losses"}, {"Coal" \[DirectedEdge] "Grid", "Gas" \[DirectedEdge] "Grid", "Solar" \[DirectedEdge] "Grid", "Wind" \[DirectedEdge] "Grid", "Grid" \[DirectedEdge] "Residential", "Grid" \[DirectedEdge] "Commercial", "Grid" \[DirectedEdge] "Industrial", "Grid" \[DirectedEdge] "Losses"}, EdgeWeight -> {40, 30, 20, 10, 45, 35, 15, 5}]; ResourceFunction[
 "SankeyChart"][g2, "EdgeOrigin" -> Top]
Out[12]=

"VertexLabelPosition"  (2) 

By default only the vertex name is shown (centered) but "VertexLabelPosition" can take values None, Above, Below, Top, Bottom or a number:

In[13]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g, "VertexLabelPosition" -> #, PlotLabel -> #] & /@ {Center, None, Above, Below, Top, Bottom, 1, -1, 0}
Out[14]=

"VertexLabelPositon" can also take a second value for the alternate dimension:

In[15]:=
ResourceFunction["SankeyChart"][g, "VertexLabelPosition" -> {Center, #}, PlotLabel -> {Center, #}] & /@ {Left, Right, Center, 5, -5}
Out[15]=

VertexValuePosition (1) 

The value of the vertex flow can be placed with the same settings as VertexLabelPosition:

In[16]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g, "VertexLabelPosition" -> Top, "VertexValuePosition" -> Bottom]
Out[17]=

EdgeValuePosition (1) 

Label edges with the value of the flow through it. The values Above, Below, Top, Bottom, Center, None and numerical values are supported, as well as {val1,val2} pairs:

In[18]:=
g2 = Graph[{"Coal", "Gas", "Solar", "Wind", "Grid", "Residential", "Commercial", "Industrial", "Losses"}, {"Coal" \[DirectedEdge] "Grid", "Gas" \[DirectedEdge] "Grid", "Solar" \[DirectedEdge] "Grid", "Wind" \[DirectedEdge] "Grid", "Grid" \[DirectedEdge] "Residential", "Grid" \[DirectedEdge] "Commercial", "Grid" \[DirectedEdge] "Industrial", "Grid" \[DirectedEdge] "Losses"}, EdgeWeight -> {40, 30, 20, 10, 45, 35, 15, 5}];
ResourceFunction["SankeyChart"][g2, "EdgeValuePosition" -> Center]
Out[19]=

VertexSpacing (1) 

The fraction of each column's rendered height taken up by gaps between its vertices:

In[20]:=
g2 = Graph[{"Coal", "Gas", "Solar", "Wind", "Grid", "Residential", "Commercial", "Industrial", "Losses"}, {"Coal" \[DirectedEdge] "Grid", "Gas" \[DirectedEdge] "Grid", "Solar" \[DirectedEdge] "Grid", "Wind" \[DirectedEdge] "Grid", "Grid" \[DirectedEdge] "Residential", "Grid" \[DirectedEdge] "Commercial", "Grid" \[DirectedEdge] "Industrial", "Grid" \[DirectedEdge] "Losses"}, EdgeWeight -> {40, 30, 20, 10, 45, 35, 15, 5}];
GraphicsRow[{ResourceFunction["SankeyChart"][g2, "VertexSpacing" -> 0.02], ResourceFunction["SankeyChart"][g2, "VertexSpacing" -> 0.6]}]
Out[21]=

VertexColors (3) 

The color of vertices and the corresponding outflows are controlled by "VertexColors". A list of colors can be provided:

In[22]:=
g3 = Graph[{"RM1", "RM2", "RM3", "P1", "P2", "P3", "A1", "A2", "Prod1", "Prod2", "Prod3"}, {"RM1" \[DirectedEdge] "P1", "RM1" \[DirectedEdge] "P2", "RM2" \[DirectedEdge] "P2", "RM2" \[DirectedEdge] "P3", "RM3" \[DirectedEdge] "P3", "P1" \[DirectedEdge] "A1", DirectedEdge["P2", "A1"], "P2" \[DirectedEdge] "A2", "P3" \[DirectedEdge] "A2", "A1" \[DirectedEdge] "Prod1", "A1" \[DirectedEdge] "Prod2", "A2" \[DirectedEdge] "Prod2", "A2" \[DirectedEdge] "Prod3"}, EdgeWeight -> {20, 10, 15, 10, 15, 20, 10, 15, 25, 18, 12, 20, 20}];
ResourceFunction["SankeyChart"][g3, "VertexColors" -> {Yellow, Red, Green, Blue, Yellow, Cyan}]
Out[23]=

A named color theme from ColorData can be used:

In[24]:=
ResourceFunction["SankeyChart"][g3, "VertexColors" -> "AlpineColors"]
Out[24]=

A numbered "Indexed" color theme can be used from ColorData:

In[25]:=
ResourceFunction["SankeyChart"][g3, "VertexColors" -> 4]
Out[25]=

VertexRenderingFunction (1) 

A custom Function[{centerX, centerY, width, height}, …] can draw each vertex as something other than a rectangle; the color set by "VertexColors" is prepended automatically:

In[26]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}]; ResourceFunction["SankeyChart"][g, "VertexRenderingFunction" -> Function[{x, y, w, h}, {EdgeForm[Black], Disk[{x, y}, {w/2, h/2}]}]]
Out[26]=

EdgeOffset (1) 

The edge flows meet behind the vertex but you can choose to space them around the vertex:

In[27]:=
g=Graph[{"A","B","C","D"},{"A"\[DirectedEdge]"B","B"\[DirectedEdge]"C","A"\[DirectedEdge]"D","D"\[DirectedEdge]"C"},EdgeWeight->{12,12,8,8}];
diskFn = Function[{x, y, w, h}, {EdgeForm[Black], Disk[{x, y}, {w/2, h/2}]}];
GraphicsRow[{SankeyChart[g, "VertexRenderingFunction" -> diskFn, "EdgeOffset" -> 0],
  SankeyChart[g, "VertexRenderingFunction" -> diskFn, "EdgeOffset" -> 1]
}]
Out[28]=

VertexLabels (2) 

When the input is a matrix, vertices are labelled by the index number. You can override those with "VertexLabels":

In[29]:=
matrix = {{0, 12, 8, 0}, {0, 0, 0, 12}, {0, 0, 0, 8}, {0, 0, 0, 0}};
ResourceFunction["SankeyChart"][matrix, "VertexLabels" -> {"Source", "Mid1", "Mid2", "Sink"}]
Out[30]=

"VertexLabels" can be used to specify more complex labels that can be specified by "LabelStyle":

In[31]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g,
 "VertexLabels" -> (Framed[Rotate[#, Pi/2], Background -> White, RoundingRadius -> 5] & /@ {"A", "B", "C", "D"})
 ]
Out[32]=

LayerSpacing (2) 

The coordinate system places vertices at integer x-positions with y-values that correspond to the flow values:

In[33]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g, Axes -> True, PlotRange -> {{0, 4}, Automatic}]
Out[34]=

Space the vertices differently with "LayerSpacing":

In[35]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g, Axes -> True, PlotRange -> {{0, 12}, Automatic}, "LayerSpacing" -> 3]
Out[36]=

VertexWidth (1) 

Control the width of vertices:

In[37]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g, "VertexWidth" -> #] & /@ {0.05, 0.2, 0.5}
Out[38]=

OrderIterations

SankeyChart attempts to minimize the times that flows cross over each other. In complex graphs this can take time. "OrderIterations" limits the computation. The default is 8 iterations but any integer value can be used.

EdgeOpacity (1) 

Outflow edges are colored the same as their source vertex but are distinguished by Opacity:

In[39]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g, "EdgeOpacity" -> #] & /@ {0.2, 0.5,
   1}
Out[40]=

LabelStyle (1) 

Control the appearance of text labels:

In[41]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g, LabelStyle -> {FontSize -> 50, FontColor -> Blue, FontFamily -> "Courier New"}]
Out[42]=

VertexPositions (2) 

Vertices are placed automatically:

In[43]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g]
Out[44]=

But one or more can be placed manually:

In[45]:=
g = Graph[{"A", "B", "C", "D"}, {"A" \[DirectedEdge] "B", "B" \[DirectedEdge] "C", "A" \[DirectedEdge] "D", "D" \[DirectedEdge] "C"}, EdgeWeight -> {12, 12, 8, 8}];
ResourceFunction["SankeyChart"][g, "VertexPositions" -> {"B" -> {2.5, 10}} ]
Out[46]=

Properties and Relations (1) 

SankeyChart and the resource function AlluvialFlowChart are broadly equivalent with different data representations:

In[47]:=
ResourceFunction[
  "AlluvialFlowChart"][{{(*A->{Public,Private,
   Unemployed}*){4, 2, 2},(*B*){2, 4, 2},(*C*){2, 4, 3}}}, {(*district*){"A", "B", "C"},(*employed*){"Public", "Private", "Unemployed"}},(*stages*){"District", "Employment"}, "ImageSize" -> 400]
Out[47]=
In[48]:=
ResourceFunction["SankeyChart"][WeightedAdjacencyGraph[
  {(*A->{Public,Private,Unemployed}*)
   {0, 0, 0, 4, 2, 2},
   (*B*){0, 0, 0, 2, 4, 2},
   (*C*){0, 0, 0, 2, 4, 3}, {4, 2, 2, 0, 0, 0},
   {2, 4, 4, 0, 0, 0},
   (*C*){2, 2, 3, 0, 0, 0} }], "VertexLabels" -> {"A", "b", "C", "Unmployed", "Private", "Public"}, "EdgeValuePosition" -> Center]
Out[48]=

Possible Issues (1) 

Vertices with only outflow are treated as sources, vertices with only inflow are treated as destinations. All other vertices must have balanced inflow and outflow must match. For example this graph has an inflow of 5 and an outflow of 10 on vertex 2:

In[49]:=
badGraph = Graph[{1, 2, 3}, {1 \[DirectedEdge] 2, 2 \[DirectedEdge] 3}, EdgeWeight -> {5, 10}];
ResourceFunction["SankeyChart"][badGraph]
Out[50]=

Neat Examples (1) 

A Manipulate that lets you interactively drag the vertices to new positions. Click the button to capture your chosen option value:

In[51]:=
Manipulate[
 ResourceFunction[
  "SankeyChart"][{{0, 7, 16, 0, 0, 0}, {0, 0, 0, 11, 0, 0}, {0, 4, 0, 0, 12, 0}, {0, 0, 0, 0, 7, 4}, {0, 0, 0, 0, 0, 19}, {0, 0, 0, 0, 0, 0}},
  "VertexLabels" -> {"Vancouver", "Edmonton", "Calgary", "Regina", "Saskatoon", "Winnipeg"}, Axes -> True,
  "VertexPositions" -> MapThread[
    Rule, {{"Vancouver", "Calgary", "Edmonton", "Regina", "Saskatoon",
       "Winnipeg"}, loc}], ImageSize -> 500],
 {{loc, {#, 0} & /@ Range[6]}, Locator},
 Button["Print Vertex positions", Print["VertexPositions" -> MapThread[
     Rule, {{"Vancouver", "Calgary", "Edmonton", "Regina", "Saskatoon",
        "Winnipeg"}, loc}]]]]
Out[51]=

Publisher

Jon McLoone

Version History

  • 1.0.0 – 22 July 2026

Related Resources

License Information