<jupyter_start><jupyter_text>Graphviz Rich Display CookbookWhile it doesn't have many opinions outside of editing files and image interaction, `jupyterlab_graphviz` is implemented as a [Mime Renderer](http://jupyterlab.readthedocs.io/en/stable/developer/extension_dev.htmlmime-renderer-extensions) and can be used to show static or even animated graphs generated by other parts of the Jupyter ecosystem.Here's all you need to get basic interactive graphviz into `IPython` Notebooks and Consoles.<jupyter_code>from IPython.display import display, update_display

def display_graphviz(dot, mimetype="application/vnd.graphviz", **kwargs):
    """ Send some DOT to the frontend
    
    Get a handle to update later by calling `display_graphviz` with `display_id=True` 
    """
    return display({mimetype: dot}, raw=True, **kwargs)

def update_graphviz(dot, handle, mimetype="application/vnd.graphviz", **kwargs):
    """ Update an already-displayed DOT
    """
    update_display({mimetype: dot}, display_id=handle.display_id, raw=True, **kwargs)<jupyter_output><empty_output><jupyter_text>>  🤔 Kernel SupportThese examples are for [IPython](http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.htmlIPython.display.display) and use some python 3 syntax, but other [Jupyter Kernels](https://github.com/jupyter/jupyter/wiki/Jupyter-kernels) offer access to the [Rich Display API](http://jupyter-client.readthedocs.io/en/stable/messaging.htmlid4). Raise an [issue](https://github.com/PhE/jupyterlab_graphviz/issues) or (better still) a [pull request](https://github.com/PhE/jupyterlab_graphviz/pulls) if you have great examples of using graphviz in your notebooks!  Make some DOT<jupyter_code>graph = """graph G { layout=dot A -- B}"""
digraph = """digraph G { layout=neato A -> B}"""<jupyter_output><empty_output><jupyter_text>Show some DOT<jupyter_code>display_graphviz(graph)<jupyter_output><empty_output><jupyter_text>Show some DOT and remember it for later<jupyter_code>first_handle = display_graphviz(digraph, display_id=True)<jupyter_output><empty_output><jupyter_text>Update some DOT<jupyter_code>update_graphviz(graph, first_handle)<jupyter_output><empty_output><jupyter_text>Update some DOT programatically<jupyter_code>from random import randint
    
def add_an_edge(dot_str, edge="--"):
    return "{}\n{} {} {}[color=red]\n}}".format(
        dot_str.strip()[:-1],
        # create two random, single letter nodes
        chr(randint(65, 90)),
        edge,
        chr(randint(65, 90))
    )
second_handle = display_graphviz(graph, display_id=True)
update_graphviz(add_an_edge(graph), second_handle)<jupyter_output><empty_output><jupyter_text>Animate some DOT<jupyter_code>import time

def animate(dot_str, layout="fdp", iterations=10):
    display_handle = display_graphviz(dot_str, display_id=True)
    for i in range(iterations):
        dot_str = add_an_edge(dot_str.replace("red", "black"))
        update_graphviz(dot_str, display_handle)
        time.sleep(1)
animate(graph)<jupyter_output><empty_output><jupyter_text>✨ MagicHere's a quick [IPython magic](http://ipython.readthedocs.io/en/stable/interactive/magics.html). You won't get pretty syntax highlighting as JupyterLab's ad-hoc syntax support isn't as flexible as Notebook Classic, but this will hopefully [improve in the future](https://github.com/jupyterlab/jupyterlab/issues/3869).<jupyter_code>from IPython.core.magic import register_line_cell_magic

@register_line_cell_magic("graphviz")
def graphviz_magic(line, cell=None):
    display_graphviz(cell if cell else line)
%graphviz digraph G {magic -> "more magic" -> "magic"}
%%graphviz
graph G {
    A -- B
}<jupyter_output><empty_output><jupyter_text>DO(N')T Write your ownSome Python libraries already support generating DOT for all kinds of useful things. Many of them use a wrapper for the canonical `graphviz` command line tool, so you have to do a bit of work to get at the underlying DOT.>  ⚠️ WarningWith some of these libraries, it's easy to generate tons of nodes and edges which will eventually cause viz.js (the underlying rendering engine) to fall over, the browser to become unresponsive, or even a tab crash. Stay tuned for approaches to working around this!  SymPy[SymPy](http://www.sympy.org/en/index.html) can draw [symbolic math](http://docs.sympy.org/latest/tutorial/printing.htmldot).<jupyter_code>from sympy import Symbol
from sympy.printing.dot import dotprint

expr = Symbol('x') + Symbol('y') / Symbol('z')
display_graphviz(dotprint(expr))<jupyter_output><empty_output><jupyter_text>NetworkX[NetworkX](https://networkx.github.io/documentation) can draw [arbitrary networks](https://networkx.github.io/documentation/stable/reference/generated/networkx.drawing.nx_agraph.to_agraph.html).<jupyter_code>import networkx as nx

G = nx.generators.directed.random_k_out_graph(10, 3, 0.5)
display_graphviz(nx.nx_agraph.to_agraph(G).to_string())<jupyter_output><empty_output><jupyter_text>PyCallGraph[PyCallGraph](https://pycallgraph.readthedocs.io/) can draw [Python call graphs](https://pycallgraph.readthedocs.io/en/master/) of live code execution.<jupyter_code>from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

class StringGraphvizOutput(GraphvizOutput):
    """ We don't want a file generated
    """
    def done(self):
        display_graphviz(self.generate())
        

pcg_output = StringGraphvizOutput()
        
with PyCallGraph(output=pcg_output):
    animate(graph)<jupyter_output><empty_output><jupyter_text>Dask[Dask](https://dask.pydata.org) can show how a [distributed computing](http://dask.pydata.org/en/latest/graphviz.html) job will be parallelized before running it.<jupyter_code>import dask.array as da
from dask.dot import to_graphviz

x = da.ones((15, 15), chunks=(5, 5))
y = x + x.T

# y.compute()
display_graphviz(to_graphviz(y.dask).source)<jupyter_output><empty_output><jupyter_text>pyreversepyreverse, part of [Pylint](https://pylint.readthedocs.io/en/latest/), can draw [software architecture](https://github.com/PyCQA/pylint/tree/master/pylint/pyreverse) including package structure and class diagrams. These are really useful, but require a fair amount of fiddly configuration.<jupyter_code>def pyreverse(*modules, **_config):
    import os
    from pathlib import Path
    from tempfile import TemporaryDirectory
    
    from traitlets.utils.bunch import Bunch
    
    from IPython.utils.capture import capture_output
    
    from pylint.config import ConfigurationMixIn
    from pylint.pyreverse.inspector import Linker, project_from_files
    from pylint.pyreverse.diadefslib import DiadefsHandler
    from pylint.pyreverse import writer
    from pylint.pyreverse.utils import insert_default_options
    from pylint.pyreverse.writer import DotWriter
    from pylint.graph import DotBackend
    
    default_config = dict(
        module_names=modules,
        classes=[],
        mode="PUB_ONLY", # or "ALL"
        all_ancestors=True,
        all_associated=True,
        show_ancestors=True,
        show_associated=True,
        only_classnames=False,
        output_format="dot",
        show_builtin=True,
    )
    
    config = dict()
    config.update(default_config)
    config.update(_config)
    
    config_bunch = Bunch(config)
    
    with capture_output(display=False):
        project = project_from_files(modules)
    linker = Linker(project, tag=True)
    handler = DiadefsHandler(config_bunch)
    diadefs = handler.get_diadefs(project, linker)
    with TemporaryDirectory() as td:
        old_cwd = os.getcwd()
        os.chdir(td)
        try:
            writer.DotWriter(config_bunch).write(diadefs)
        finally:
            os.chdir(old_cwd)
        for path in sorted(Path(td).glob("*.dot")):
            display_graphviz(path.read_text())<jupyter_output><empty_output><jupyter_text>>  ⚠️ WarningYeah, this can be really slow. Just keep scrolling.<jupyter_code>pyreverse("jupyterlab")<jupyter_output><empty_output><jupyter_text>scikit-learn[scikit-learn](http://scikit-learn.org/stable/) can visualize [decision trees](http://scikit-learn.org/stable/modules/generated/sklearn.tree.export_graphviz.html).<jupyter_code>from sklearn.datasets import load_iris
from sklearn import tree
iris = load_iris()
clf = tree.DecisionTreeClassifier()
clf = clf.fit(iris.data, iris.target)
display_graphviz(
    tree.export_graphviz(
        clf, out_file=None, 
        feature_names=iris.feature_names,  
        class_names=iris.target_names,  
        filled=True, rounded=True,  
        special_characters=True))<jupyter_output><empty_output><jupyter_text>transitions[transitions](https://github.com/pytransitions/transitions) can draw [state machines](https://github.com/pytransitions/transitions-diagrams).<jupyter_code>from transitions.extensions import LockedHierarchicalGraphMachine

machine = LockedHierarchicalGraphMachine(
    states=["standing", "walking", {"name": "caffeinated", 
                                    "children":["dithering", "running"]}], 
    transitions=[
      ["walk", "standing", "walking"],
      ["stop", "walking", "standing"],
      ["drink", "*", "caffeinated"],
      ["walk", ["caffeinated", "caffeinated_dithering"], "caffeinated_running"],
      ["relax", "caffeinated", "standing"]
    ], 
    initial="standing", 
    ignore_invalid_triggers=True)
display_graphviz(machine.get_graph().to_string())<jupyter_output><empty_output><jupyter_text>nltk[nltk](https://www.nltk.org/) can show [dependency graphs](https://www.nltk.org/_modules/nltk/parse/dependencygraph.html).<jupyter_code>from nltk.corpus import dependency_treebank
from nltk.parse.dependencygraph import DependencyGraph
t = dependency_treebank.parsed_sents()[6]
print(" ".join(dependency_treebank.sents()[6]))
display_graphviz(DependencyGraph(t.to_conll(3)).to_dot())<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11508017048914146, 'fraction_numerical': 0.0032474122183884717, 'mean_word_length': 5.060885608856089, 'pattern_counts': {'":': 2, '<': 69, '<?xml version=': 0, '>': 75, 'https://': 15, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '627608', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 3312, 'n_tokens_neox': 3211, 'n_words': 664, 'path': 'samples/Graphviz Rich Display Cookbook.ipynb', 'repo_name': 'PhE/jupyterlab_graphviz', 'size': '9854'}