paulbrodersen / netgraph

Publication-quality network visualisations in python
GNU General Public License v3.0
660 stars 39 forks source link

Different labels for bidirectional arrows #92

Open jamesbraza opened 1 month ago

jamesbraza commented 1 month ago

This code, with netgraph==4.13.2 and matplotlib==3.9.0:

from netgraph import Graph
import matplotlib.pyplot as plt

edges = [("in", "out"), ("out", "in")]
edge_labels = {
    ("in", "out"): 3,
    ("out", "in"): 5,
}
graph = Graph(
    edges, node_labels=True, edge_labels=edge_labels, arrows=True, node_label_offset=(1e-4, 0)
)
plt.show()

Renders like so:

image

We can see it clobbered the 3 label. Any chance we can support bidirectional arrows, that have a different label per direction?

paulbrodersen commented 1 month ago

The text object for label 3 will be there, but the label 5 is occluding it currently. There are several options to deal with this issue.

  1. Don't plot the labels exactly at the center of the edge (not a great solution, but results in the least changes to the figure).
from netgraph import Graph
import matplotlib.pyplot as plt

edges = [("in", "out"), ("out", "in")]
edge_labels = {
    ("in", "out"): 3,
    ("out", "in"): 5,
}
graph = Graph(edges, node_labels=True, node_label_offset=(1e-4, 0),
              edge_labels=edge_labels, edge_label_position=0.33, arrows=True)

plt.show()

Figure_3

  1. Use a curved edge layout.
from netgraph import Graph
import matplotlib.pyplot as plt

edges = [("in", "out"), ("out", "in")]
edge_labels = {
    ("in", "out"): 3,
    ("out", "in"): 5,
}
graph = Graph(edges, node_labels=True, node_label_offset=(1e-4, 0), edge_labels=edge_labels,
              edge_layout="curved", edge_layout_kwargs=dict(bundle_parallel_edges=False),
              arrows=True)

plt.show()

Figure_1

  1. Use the MultiGraph class, which offsets parallel edges from each other (rather than drawing half-arrows).
from netgraph import MultiGraph
import matplotlib.pyplot as plt

# MultiGraph edges require an additional `key`, to distinguish edges connecting the same nodes in the same direction
edges = [("in", "out", 0), ("out", "in", 0)] 

edge_labels = {
    ("in", "out", 0): 3,
    ("out", "in", 0): 5,
}
graph = MultiGraph(edges, node_labels=True, node_label_offset=(1e-4, 0), edge_labels=edge_labels, arrows=True)
plt.show()

Figure_2

jamesbraza commented 1 month ago

Oh wow, thank you so much for the excellent answer! That totally clears me up on how to proceed.

I still think it's unideal that the default Graph behavior is one label completely occludes the other (without specifying edge_label_position or edge_layout="curved"). I am wondering if you can somehow adjust the default behavior to be "smarter", or at least log a warning that there's an occlusion taking place.

Maybe it's also worth mentioning MultiDiGraph on https://netgraph.readthedocs.io/en/latest/sphinx_gallery_output/plot_06_labels.html, as it seems relevant to occluded labels

paulbrodersen commented 1 month ago

I still think it's unideal that the default Graph behavior is one label completely occludes the other (without specifying edge_label_position or edge_layout="curved").

Agreed. However, I am trying pretty hard to keep things stable, and am hence reluctant to change behaviours that have been the default since version 0.0.0.

or at least log a warning that there's an occlusion taking place.

I will add a section to the tutorial along the lines of my pointers above, and issue a warning pointing to said tutorial, when a user

  1. uses the straight edge layout,
  2. has a directed graph with bi-directional edges (but not a MultiGraph),
  3. both directions are labelled for any of the bi-directional edges, and
  4. edge_label_position is 0.5.

Maybe it's also worth mentioning MultiDiGraph on https://netgraph.readthedocs.io/en/latest/sphinx_gallery_output/plot_06_labels.html, as it seems relevant to occluded labels

So far, only the dev branch implements MultiGraph. The documentation has already been updated, but RTD currently only displays the master branch (I will look into adding the docs for the dev branch, too).

jamesbraza commented 1 month ago

Sounds good, like your proposal! Thanks 👍

When updating the docs, my request is to include renderings of graphs involving bidirectional arrows. When I was reading the docs, I was visually skimming for examples of bidirectional arrows

paulbrodersen commented 1 month ago

What is missing in basic example #2: directed graphs then?

jamesbraza commented 1 month ago

What is missing in basic example #2: directed graphs then?

The renderings are great, I really like example 2. If you want to robust-ify it, then maybe add a second visualization showing separate labels on the bidirectional arrows