#!/usr/bin/env python
# coding: utf-8
# # More examples
#
# > [ Simple example](./Simple%20example.ipynb)
# >
# > Advanced examples
# >
# > [ Linking and Layout](./Linking%20and%20Layout.ipynb)
# >
# > [ Exporting Images](./Exporting%20Images.ipynb)
# In[1]:
from ipysankeywidget import SankeyWidget
from ipywidgets import Layout
# > A convenience factory function
# In[2]:
layout = Layout(width="300", height="200")
def sankey(margin_top=10, **value):
"""Show SankeyWidget with default values for size and margins"""
return SankeyWidget(layout=layout,
margins=dict(top=margin_top, bottom=0, left=30, right=60),
**value)
# ## Rank assignment
# ### Rank sets
# You can adjust the left-right placement of nodes by putting them in rank sets: all nodes in the same set end up with the same rank.
# In[3]:
links = [
{'source': 'A', 'target': 'B', 'value': 1},
{'source': 'B', 'target': 'C', 'value': 1},
{'source': 'A', 'target': 'D', 'value': 1},
]
sankey(links=links)
# In[4]:
rank_sets = [
{ 'type': 'same', 'nodes': ['C', 'D'] }
]
sankey(links=links, rank_sets=rank_sets)
# In[5]:
order = [
['A'],
['D', 'B'],
['C'],
]
sankey(links=links, order=order)
# In[6]:
order = [
[ [ ], ['A'], [], ],
[ ['B'], [ ], ['D'] ],
[ [ ], ['C'], [] ],
]
sankey(links=links, order=order)
# ### Reversed nodes
# Most nodes are assumed to link from left to right, but sometimes there are return links which should be shown from right to left.
# In[7]:
links = [
{'source': 'A', 'target': 'B', 'value': 1},
{'source': 'B', 'target': 'C', 'value': 1},
{'source': 'C', 'target': 'D', 'value': 1},
{'source': 'A', 'target': 'E', 'value': 0.5},
]
nodes = [
{'id': 'C', 'direction': 'l'},
{'id': 'D', 'direction': 'l'},
]
sankey(links=links, nodes=nodes)
# Variations:
# In[8]:
nodes = [
{'id': 'C', 'direction': 'r'},
{'id': 'D', 'direction': 'l'},
]
sankey(links=links, nodes=nodes)
# In[9]:
nodes = [
{'id': 'C', 'direction': 'l'},
{'id': 'D', 'direction': 'r'},
]
sankey(links=links, nodes=nodes)
# ## Styling
# By default, the links are coloured according to their type:
# In[10]:
links = [
{'source': 'A', 'target': 'B', 'value': 3, 'type': 'x'},
{'source': 'B', 'target': 'C', 'value': 2, 'type': 'y'},
{'source': 'B', 'target': 'D', 'value': 1, 'type': 'z'},
]
sankey(links=links)
# You can also set the colours directly:
# In[11]:
links = [
{'source': 'A', 'target': 'B', 'value': 3, 'color': 'steelblue'},
{'source': 'B', 'target': 'C', 'value': 2, 'color': '#aaa'},
{'source': 'B', 'target': 'D', 'value': 1, 'color': 'goldenrod'},
]
sankey(links=links)
# Process titles default to their ids, but can be overridden. There are also one built-in alternative "style" of node:
# - `process` is drawn with a thicker line
# In[12]:
nodes = [
{'id': 'B', 'title': 'Middle node', 'style': 'process' },
]
sankey(links=links, nodes=nodes)
# Of course, you can also use CSS to adjust the styling:
# In[13]:
get_ipython().run_cell_magic('html', '', '\n')
# ## Aligning link types
# In[14]:
links = [
{'source': 'A1', 'target': 'B', 'value': 1.5, 'type': 'x'},
{'source': 'A1', 'target': 'B', 'value': 0.5, 'type': 'y'},
{'source': 'A2', 'target': 'B', 'value': 0.5, 'type': 'x'},
{'source': 'A2', 'target': 'B', 'value': 1.5, 'type': 'y'},
{'source': 'B', 'target': 'C', 'value': 2.0, 'type': 'x'},
{'source': 'B', 'target': 'C', 'value': 2.0, 'type': 'y'},
]
sankey(links=links, nodes=[])
# In[15]:
sankey(links=links, align_link_types=True)
# In[16]:
order = [
['A2', 'A1'],
['B'],
['C'],
]
sankey(links=links, align_link_types=True, order=order)
# ## Dynamic updating
# In[17]:
from ipywidgets import Button, VBox
links = [
{'source': 'A', 'target': 'B', 'value': 1},
{'source': 'B', 'target': 'C', 'value': 1},
{'source': 'A', 'target': 'D', 'value': 1},
]
order = [
['A'],
['D', 'B'],
['C'],
]
s = sankey(links=links, order=order)
def swap(x):
global order
order = [list(reversed(o)) for o in order]
s.order = order
b = Button(description='Swap')
b.on_click(swap)
VBox([b, s])
# ## Node groups
# In[18]:
links = [
{'source': 'A', 'target': 'B', 'value': 3, 'type': 'x'},
{'source': 'B', 'target': 'C', 'value': 2, 'type': 'y'},
{'source': 'B', 'target': 'D', 'value': 1, 'type': 'z'},
]
groups = [
{'id': 'G', 'title': 'Group', 'nodes': ['C', 'D']}
]
sankey(links=links, nodes=[], groups=groups, margin_top=30)
# ## Link labels
#
# Link labels show the numeric value of the link on the diagram:
# In[19]:
sankey(links=links, linkLabelFormat='.1f')
# By default the labels for small links are hidden, but you can customize this using `linkLabelMinWidth`:
# In[20]:
links[2]['value'] = 0.1
links[1]['value'] = 2.9
sankey(links=links, linkLabelFormat='.1f')
# In[21]:
sankey(links=links, linkLabelFormat='.1f', linkLabelMinWidth=4)
# ### Link markers
#
# **Warning**: this feature is experimental and may be changed.
# In[22]:
links[0]['marker'] = 2.5
sankey(links=links)
# ### Extra link info
#
# **Warning**: this feature is experimental and may be changed.
# In[23]:
links = [
{'source': 'A', 'target': 'B', 'value': 3, 'type': 'x', 'info_html': 'Hi!'},
{'source': 'B', 'target': 'C', 'value': 2, 'type': 'y', 'info_html': 'B to C'},
{'source': 'B', 'target': 'D', 'value': 1, 'type': 'z'},
]
sankey(links=links, show_link_info_html=True)
# ## Custom layout
#
# The d3-sankey-diagram layout algorithm can be bypassed completely by specifying coordinates for the nodes directly. This must be activated by the `node_position_attr` option.
# In[24]:
links = [
{'source': 'A', 'target': 'B', 'value': 30},
{'source': 'B', 'target': 'C', 'value': 20},
{'source': 'B', 'target': 'D', 'value': 10},
]
nodes = [
{'id': 'A', 'position': [0, 50]},
{'id': 'B', 'position': [100, 50]},
{'id': 'C', 'position': [200, 30]},
{'id': 'D', 'position': [200, 100]},
]
w = sankey(
links=links,
nodes=nodes,
node_position_attr='position'
)
w
# The positions are in display coordinates, within the margins specified. The scale is set to 1 by default, if not specified. When node positions are specified manually, they are not affected by the scale -- only the width of the lines is scaled.
# In[25]:
# Try changing this
w.scale = 2
# In[26]:
# Try changing this
w.nodes[0]['position'] = [50, 50]
w.send_state()
# In[27]:
# w.node_position_attr = None