"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import holoviews as hv\n",
"from holoviews import opts\n",
"hv.extension('bokeh')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The ``Graph`` element provides an easy way to represent and visualize network graphs. It differs from other elements in HoloViews in that it consists of multiple sub-elements. The data of the ``Graph`` element itself are the abstract edges between the nodes. By default the element will automatically compute concrete ``x`` and ``y`` positions for the nodes and represent them using a ``Nodes`` element, which is stored on the Graph. The abstract edges and concrete node positions are sufficient to render the ``Graph`` by drawing straight-line edges between the nodes. In order to supply explicit edge paths we can also declare ``EdgePaths``, providing explicit coordinates for each edge to follow.\n",
"\n",
"To summarize a ``Graph`` consists of three different components:\n",
"\n",
"* The ``Graph`` itself holds the abstract edges stored as a table of node indices.\n",
"* The ``Nodes`` hold the concrete ``x`` and ``y`` positions of each node along with a node ``index``. The ``Nodes`` may also define any number of value dimensions, which can be revealed when hovering over the nodes or to color the nodes by.\n",
"* The ``EdgePaths`` can optionally be supplied to declare explicit node paths.\n",
"\n",
"This reference document describes only basic functionality, for a more detailed summary on how to work with network graphs in HoloViews see the [User Guide](../../../user_guide/Network_Graphs.ipynb).\n",
"\n",
"To make the visualizations in this notebook easier to view, the first thing we will do is increase the default width and height:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"opts.defaults(opts.Graph(width=400, height=400))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### A simple Graph\n",
"\n",
"Let's start by declaring a very simple graph connecting one node to all others. If we simply supply the abstract connectivity of the ``Graph``, it will automatically compute a layout for the nodes using the ``layout_nodes`` operation, which defaults to a circular layout:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Declare abstract edges\n",
"N = 8\n",
"node_indices = np.arange(N)\n",
"source = np.zeros(N)\n",
"target = node_indices\n",
"\n",
"simple_graph = hv.Graph(((source, target),))\n",
"simple_graph"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Directed graphs\n",
"\n",
"The Graph element also allows indicating the directionality of graph edges using arrows. To enable the arrows set ``directed=True`` and to optionally control the ``arrowhead_length`` provide a length as a fraction of the total graph extent:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"simple_graph.opts(directed=True, arrowhead_length=0.05)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Accessing the nodes and edges\n",
"\n",
"We can easily access the ``Nodes`` and ``EdgePaths`` on the ``Graph`` element using the corresponding properties:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"simple_graph.nodes + simple_graph.edgepaths"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Additional features\n",
"\n",
"Next we will extend this example by supplying explicit edges, node information and edge weights. By constructing the ``Nodes`` explicitly we can declare an additional value dimensions, which are revealed when hovering and/or can be mapped to the color by specifying the ``color_index``. We can also associate additional information with each edge by supplying a value dimension to the ``Graph`` itself, which we can map to a color using the ``edge_color_index``."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Node info\n",
"np.random.seed(7)\n",
"x, y = simple_graph.nodes.array([0, 1]).T\n",
"node_labels = ['Output']+['Input']*(N-1)\n",
"edge_weights = np.random.rand(8)\n",
"\n",
"# Compute edge paths\n",
"def bezier(start, end, control, steps=np.linspace(0, 1, 100)):\n",
" return (1-steps)**2*start + 2*(1-steps)*steps*control+steps**2*end\n",
"\n",
"paths = []\n",
"for node_index in node_indices:\n",
" ex, ey = x[node_index], y[node_index]\n",
" paths.append(np.column_stack([bezier(x[0], ex, 0), bezier(y[0], ey, 0)]))\n",
"\n",
"# Declare Graph\n",
"nodes = hv.Nodes((x, y, node_indices, node_labels), vdims='Type')\n",
"graph = hv.Graph(((source, target, edge_weights), nodes, paths), vdims='Weight')\n",
"\n",
"graph.opts(node_color='Type', edge_color='Weight', cmap=['blue', 'red'], edge_cmap='viridis')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For full documentation and the available style and plot options, use ``hv.help(hv.Graph).``"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}