netgraph
netgraph copied to clipboard
(Un)directed mixed graph
Hi there! Is there the possibility to generate a graph with some edges that are directed (with arrow) and some undirected?
Thank you, Luca
It's not a feature that is currently supported, but I will add it to the TODO list.
The only workaround I can see is to plot the network twice, first all edges with arrow heads and then the remaining ones (or vice-versa).
import matplotlib.pyplot as plt
import netgraph
edges = [(0, 1), (1, 2), (2, 0)]
# set or precompute node positions
pos = {
0 : (0.1, 0.1),
1 : (0.9, 0.1),
2 : (0.5, 0.7),
}
fig, ax = plt.subplots()
netgraph.Graph(edges[:-1], nodes=[0, 1, 2], node_layout=pos, arrows=True, ax=ax)
netgraph.Graph(edges[-1:], nodes=[0, 1, 2], node_layout=pos, arrows=False, ax=ax)
plt.show()
If the desired node_alpha
is less than one, one of the two function calls to Graph
should be with node_alpha
set to zero.
Note that the interactive variants won't work properly with this workaround (at least I would be surprised if they did!).
Actually, I came up with a better way that doesn't break interactive Graph
variants.
Similarly, to issue #83, you can simply instantiate the Graph
object (or any of its derived classes), and then manipulate the edge artists individually, and either add or remove arrows by setting the head_width
and head_length
attributes appropriately.
import matplotlib.pyplot as plt
from netgraph import Graph
fig, ax = plt.subplots()
g = Graph([(0, 1), (1, 2), (2, 0)], arrows=True, node_labels=True, ax=ax)
edge_artist = g.edge_artists[(0, 1)]
edge_artist.head_width = 1e-12 # don't set them to zero as this can cause numerical issues
edge_artist.head_length = 1e-12
edge_artist._update_path()
plt.show()
Hi,
Thank you for the solutions; the second one seems to be the best.
I tried to play around with the library and managed to achieve a similar result with minimal modifications.
I defined a dictionary called "arrows":
arrows = {
(0, 1) : False,
(1, 2) : True,
(2, 0) : True
}
Then, I used the dictionary in the graph definition:
fig, ax = plt.subplots()
g = Graph([(0, 1), (1, 2), (2, 0)], arrows=arrows, node_labels=True, ax=ax)
plt.show()
To implement this, I only needed to modify the "draw_edges" method in the "_main.py" script as follows:
if arrows[edge]:
head_length = 2 * edge_width[edge]
head_width = 3 * edge_width[edge]
else:
head_length = 0
head_width = 0
instead of
if arrows:
head_length = 2 * edge_width[edge]
head_width = 3 * edge_width[edge]
else:
head_length = 0
head_width = 0
However, I haven't extensively tested this modification for all types of graphs, so I do not know what else it can affects.
Luca
Hi Luca, your proposal is very similar to what I have in mind. Basically, the plan is to support 3 cases:
-
arrows
is a just a boolean. Everything remains as before to retain backwards compatibility. -
arrows
is a dict. a)arrows
is a dict mapping edge IDs to a boolean (= your suggestion). b)arrows
is a dict mapping edge IDs to a tuple (head length, head width) (= solution for issue #83).
Would that work for you?
Yes absolutely :)