matplotlibnetworkxdotnetgraph

Netgraph dot layout avoiding node labels overlap: howto?


I am generating simple syntactic dependecies trees such as in the following example: Newspaper phrase syntactic tree

Best layout is "dot" but on the top row things get very crowded. Is there any parameter or other way to avoid node labels overlapping with neighbouring nodes (and ideally edges)?

Maybe some matplotlib parameter to "draw on a very wide canvas"? (sorry totally matplotlib ignorant).

Here is the code I am currently using:

import platform
import matplotlib
import matplotlib.pyplot as plt
import stanza
import networkx as nx
from netgraph import InteractiveGraph

if platform.system() == "Darwin":
    matplotlib.use("TkAgg")  # MacOSX backend is bugged; use TkAgg

def display_graph(G: nx.DiGraph, title: str):
    """Use netgraph to show the graph"""
    nodecolors = nx.get_node_attributes(G, "nodecol")
    nodelabels = nx.get_node_attributes(G, "lemma")
    nodeshapes = nx.get_node_attributes(G, "nodeshape")
    edgelabels = nx.get_edge_attributes(G, "label")
    # now plot the graph
    plot_instance = InteractiveGraph(
        G,
        node_layout="dot",
        node_labels=nodelabels,
        node_color=nodecolors,
        node_label_fontdict=dict(size=12),
        node_shape=nodeshapes,
        node_alpha=0.5,  # node transparency
        edge_labels=edgelabels,
        edge_layout="curved",
        arrows=True,
    )
    plt.suptitle(title, wrap=True)
    plt.show()
    return

Solution

  • Is there any parameter or other way to avoid node labels overlapping with neighbouring nodes (and ideally edges)?

    If you set the parameter node_label_offset to some float, e.g. node_label_offset=0.05, then netgraph will find the position 0.05 away from the node center that is furthest away from all other nodes and edges. However, there is no guarantee that there aren't any label overlaps as the space may simply be too crowded.

    Maybe some matplotlib parameter to "draw on a very wide canvas"? (sorry totally matplotlib ignorant).

    The width and height of the canvas are controlled by the shape parameter. The default is shape=(1, 1). So shape=(2, 2) would double the size of the canvas. However, as fontsizes in matplotlib (and hence netgraph) are defined in display units, you would also have to increase the size of the matplotlib figure to decrease the size of the text relative to the whole figure.

    fig, ax = plt.subplots(figsize=(2*6.4, 2*4.8)) # default is 6.4, 4.8
    plot_instance = InteractiveGraph(G, node_layout='dot', shape=(2, 2), ax=ax, ...)
    

    Further suggestions:

    1. Remove the node edges. node_edge_color=nodecolors or node_edge_width=0..
    2. I would set the node alpha to one, as otherwise the underlying edge becomes visible. Use lighter node colors if needed.

    Basically, you want to remove any source of contrast that distract from the text if you want the labels to "pop".