{ "cells": [ { "cell_type": "markdown", "id": "cc191d89", "metadata": {}, "source": [ "# Advanced use\n", "\n", "This Jupyter notebook demonstrates some advanced features of the Python package [gravis](https://pypi.org/project/gravis). The .ipynb file can be found [here](https://github.com/robert-haas/gravis/tree/master/examples)." ] }, { "cell_type": "code", "execution_count": 1, "id": "51a0be7d", "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "import gravis as gv\n", "import networkx as nx" ] }, { "cell_type": "markdown", "id": "cb14b299", "metadata": {}, "source": [ "## Define a graph in all possible ways\n", "\n", "The following gives an overview of all ways that are available for defining a graph that is recognized by gravis." ] }, { "cell_type": "markdown", "id": "18d07cc4", "metadata": {}, "source": [ "### 1) String in gJGF" ] }, { "cell_type": "code", "execution_count": null, "id": "73643d6a", "metadata": {}, "outputs": [], "source": [ "graph1 = \"\"\"\n", "{\n", " \"graph\": {\n", " \"metadata\": {\"node_color\": \"blue\"},\n", " \"nodes\": [{\"id\": \"0\"}, {\"id\": \"1\"}, {\"id\": \"2\"}],\n", " \"edges\": [{\"source\": 0, \"target\": 1}, {\"source\": 1, \"target\": 2}, {\"source\": 2, \"target\": 0}]\n", " }\n", "}\n", "\"\"\"\n", "\n", "gv.d3(graph1, graph_height=100)" ] }, { "cell_type": "markdown", "id": "a224d5da", "metadata": {}, "source": [ "### 2) Dictionary in gJGF" ] }, { "cell_type": "markdown", "id": "fa9fab63", "metadata": {}, "source": [ "The Python dictionary needs to be equivalent to a JSON string in gJGF. The following demonstrates two [different ways a dictionary can be created in Python](https://docs.python.org/3/library/stdtypes.html#typesmapping)." ] }, { "cell_type": "code", "execution_count": null, "id": "6fda4ff0", "metadata": {}, "outputs": [], "source": [ "graph2 = {\n", " 'graph': {\n", " 'metadata': {'node_color': 'orange'},\n", " 'nodes': [{'id': '0'}, {'id': '1'}, {'id': '2'}],\n", " 'edges': [{'source': 0, 'target': 1}, {'source': 1, 'target': 2}, {'source': 2, 'target': 0}],\n", " }\n", "}\n", "\n", "gv.d3(graph2, graph_height=100)" ] }, { "cell_type": "code", "execution_count": null, "id": "23ade5af", "metadata": {}, "outputs": [], "source": [ "graph3 = dict(graph=dict(\n", " metadata=dict(node_color='red'),\n", " nodes=[dict(id=0), dict(id=1), dict(id=2)],\n", " edges=[dict(source=0, target=1), dict(source=1, target=2), dict(source=2, target=0)],\n", "))\n", "\n", "gv.d3(graph3, graph_height=100)" ] }, { "cell_type": "markdown", "id": "17ae0e0a", "metadata": {}, "source": [ "### 3) Text file in gJGF\n", "\n", "The text file needs to contain a string in gJGF." ] }, { "cell_type": "code", "execution_count": null, "id": "a54c7235", "metadata": {}, "outputs": [], "source": [ "graph4 = os.path.join('data', 'graph_basic.gjgf')\n", "\n", "gv.d3(graph4, graph_height=100)" ] }, { "cell_type": "markdown", "id": "40b49b01", "metadata": {}, "source": [ "### 4) Graph object from external library\n", "\n", "The following demonstrates [two ways](https://networkx.org/documentation/stable/tutorial.html) in which a graph can be defined in [NetworkX](https://networkx.org). Other libraries are also supported an shown in other examples." ] }, { "cell_type": "code", "execution_count": null, "id": "bcf8523b", "metadata": {}, "outputs": [], "source": [ "graph5 = nx.Graph()\n", "graph5.graph['node_color'] = 'green'\n", "graph5.add_node(0)\n", "graph5.add_node(1)\n", "graph5.add_node(2)\n", "graph5.add_edge(0, 1)\n", "graph5.add_edge(1, 2)\n", "graph5.add_edge(2, 0)\n", "\n", "gv.d3(graph5, graph_height=100)" ] }, { "cell_type": "code", "execution_count": null, "id": "2f48ff8e", "metadata": {}, "outputs": [], "source": [ "graph6 = nx.Graph()\n", "graph6.graph['node_color'] = 'cyan'\n", "graph6.add_edges_from([(0, 1), (1, 2), (2, 0)])\n", "\n", "gv.d3(graph6, graph_height=100)" ] }, { "cell_type": "markdown", "id": "2d4ab96a", "metadata": {}, "source": [ "Note that a graph object is internally converted into gJGF with a function that is also available to users" ] }, { "cell_type": "code", "execution_count": null, "id": "deaa1e7c", "metadata": {}, "outputs": [], "source": [ "dictionary = gv.convert.any_to_gjgf(graph6)\n", "dictionary" ] }, { "cell_type": "code", "execution_count": null, "id": "0d9d6b5b", "metadata": {}, "outputs": [], "source": [ "import json\n", "\n", "json_string = json.dumps(dictionary, indent=2)\n", "print(json_string)" ] }, { "cell_type": "markdown", "id": "4529fd8e", "metadata": {}, "source": [ "### 5) List of any of the previous" ] }, { "cell_type": "markdown", "id": "1cbe3776", "metadata": {}, "source": [ "Multiple graphs can be visualized in a single output cell by using the ``display`` method of a ``Figure`` object returned by a plotting function." ] }, { "cell_type": "code", "execution_count": null, "id": "d2dc8252", "metadata": {}, "outputs": [], "source": [ "graphs = [graph1, graph2, graph3, graph4, graph5, graph6]\n", "\n", "for g in graphs:\n", " print(type(g))\n", " fig = gv.d3(g, graph_height=75)\n", " fig.display(inline=True)" ] }, { "cell_type": "markdown", "id": "b9ae7809", "metadata": {}, "source": [ "Multiple graph can also be made available in a single visualization. The user can switch between them in the **Data selection** tab." ] }, { "cell_type": "code", "execution_count": null, "id": "832efd28", "metadata": {}, "outputs": [], "source": [ "gv.d3(graphs, graph_height=200)" ] }, { "cell_type": "markdown", "id": "f9a2354b", "metadata": {}, "source": [ "## Define a graph with all available annotations\n", "\n", "### 1) Using a dictionary in gJGF" ] }, { "cell_type": "code", "execution_count": null, "id": "af57e790", "metadata": {}, "outputs": [], "source": [ "graph_gjgf = {\n", " 'graph': {\n", " 'directed': True,\n", " 'metadata': {\n", " 'node_label_size': 14,\n", " 'node_label_color': 'green',\n", " 'edge_label_size': 10,\n", " 'edge_label_color': 'blue',\n", " },\n", " 'nodes': [\n", " {'id': '0', 'label': 'first node', 'metadata': {\n", " 'color': 'red',\n", " 'size': 15,\n", " 'shape': 'rectangle',\n", " 'opacity': 0.7,\n", " 'label_color': 'red',\n", " 'label_size': 20,\n", " 'border_color': 'black',\n", " 'border_size': 3,\n", " }},\n", " {'id': '1'},\n", " {'id': '2'},\n", " {'id': '3', 'metadata': {\n", " 'color': 'green',\n", " 'size': 15,\n", " 'shape': 'hexagon',\n", " 'opacity': 0.7,\n", " 'label_color': 'green',\n", " 'label_size': 10,\n", " 'border_color': 'blue',\n", " 'border_size': 3,\n", " }},\n", " {'id': '4'},\n", " {'id': '5'},\n", " {'id': '6', 'label': 'last node'},\n", " ],\n", " 'edges': [\n", " {'source': 0, 'target': 1},\n", " {'source': 1, 'target': 2, 'label': 'e2'},\n", " {'source': 2, 'target': 3},\n", " {'source': 3, 'target': 4},\n", " {'source': 4, 'target': 5, 'label': 'e5', 'metadata': {\n", " 'color': 'orange',\n", " 'label_color': 'gray',\n", " 'label_size': 14,\n", " 'size': 4.0,\n", " }},\n", " {'source': 5, 'target': 6},\n", " {'source': 6, 'target': 2, 'label': 'e2'},\n", " ]\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": null, "id": "14eb28fb", "metadata": {}, "outputs": [], "source": [ "gv.d3(graph_gjgf, graph_height=200,\n", " node_label_data_source='label',\n", " show_edge_label=True, edge_label_data_source='label')" ] }, { "cell_type": "markdown", "id": "15d0889a", "metadata": {}, "source": [ "### 2) Using NetworkX" ] }, { "cell_type": "code", "execution_count": null, "id": "2b3966e6", "metadata": {}, "outputs": [], "source": [ "# Graph and graph attributes\n", "graph = nx.DiGraph()\n", "graph.graph['node_label_size'] = 14\n", "graph.graph['node_label_color'] = 'green'\n", "graph.graph['edge_label_size'] = 10\n", "graph.graph['edge_label_color'] = 'blue'\n", "\n", "# Nodes and node attributes\n", "graph.add_node(0, label='first node', color='red', size=15, shape='rectangle', opacity=0.7,\n", " label_color='red', label_size=20, border_color='black', border_size=3)\n", "graph.add_node(3, color='green', size=15, shape='hexagon', opacity=0.7,\n", " label_color='green', label_size=10, border_color='blue', border_size=3)\n", "graph.add_node(6, label='last node')\n", "\n", "# Edges and edge attributes\n", "graph.add_edge(0, 1) # add_edge creates nodes if they don't exist yet\n", "graph.add_edge(1, 2, label='e2')\n", "graph.add_edge(2, 3)\n", "graph.add_edge(3, 4)\n", "graph.add_edge(4, 5, label='e5', color='orange', label_color='gray', label_size=14, size=4.0)\n", "graph.add_edge(5, 6)\n", "graph.add_edge(6, 2, label='e7')" ] }, { "cell_type": "code", "execution_count": null, "id": "16d3cb05", "metadata": {}, "outputs": [], "source": [ "gv.d3(graph, graph_height=200,\n", " node_label_data_source='label',\n", " show_edge_label=True, edge_label_data_source='label')" ] }, { "cell_type": "markdown", "id": "9ff65b47", "metadata": {}, "source": [ "### 3) Using a text file in gJGF" ] }, { "cell_type": "code", "execution_count": null, "id": "d95d0eff", "metadata": {}, "outputs": [], "source": [ "graph = os.path.join('data', 'graph_advanced.gjgf')\n", "\n", "gv.d3(graph, graph_height=200, node_label_data_source='label',\n", " show_edge_label=True, edge_label_data_source='label')" ] }, { "cell_type": "markdown", "id": "f6353a18", "metadata": {}, "source": [ "## Define a graph with multi-edges and self loops\n", "\n", "This graph is a depiction of a state machine. Nodes represent states, edges represent state transitions and edge labels represent inputs that cause the corresponding state transition." ] }, { "cell_type": "code", "execution_count": 2, "id": "9c28843f", "metadata": {}, "outputs": [], "source": [ "graph_gjgf = {\n", " 'graph':{\n", " 'directed': True,\n", " 'metadata': {'node_size': 20},\n", " 'nodes': {\n", " 's0': {'metadata': {'x': -150, 'y': 20, 'size': 0}},\n", " 's1': {'label': 'State 1', 'metadata': {'x': -100, 'y': 20, 'color': 'red', 'label_color': 'red'}},\n", " 's2': {'label': 'State 2', 'metadata': {'x': 0, 'y': 20, 'color': 'green', 'label_color': 'green'}},\n", " 's3': {'label': 'State 3', 'metadata': {'x': 100, 'y': 20, 'color': 'blue', 'label_color': 'blue'}},\n", " },\n", " 'edges': [\n", " {'source': 's0', 'target': 's1'},\n", " {'source': 's1', 'target': 's2', 'label': 'a'},\n", " {'source': 's1', 'target': 's2', 'label': 'b'},\n", " {'source': 's2', 'target': 's2', 'label': 'a'},\n", " {'source': 's2', 'target': 's2', 'label': 'b'},\n", " {'source': 's2', 'target': 's1', 'label': 'c'},\n", " {'source': 's2', 'target': 's1', 'label': 'd'},\n", " {'source': 's2', 'target': 's1', 'label': 'e'},\n", " {'source': 's2', 'target': 's3', 'label': 'f'},\n", " {'source': 's3', 'target': 's3', 'label': 'a'},\n", " {'source': 's3', 'target': 's3', 'label': 'b'},\n", " {'source': 's3', 'target': 's2', 'label': 'f'},\n", " ]\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 10, "id": "ac5b6ba5", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", "\n", "