Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error Attempting to Iterate Over LineCollection #3337

Closed
dblclik opened this issue Mar 7, 2019 · 5 comments · Fixed by #4360
Closed

Error Attempting to Iterate Over LineCollection #3337

dblclik opened this issue Mar 7, 2019 · 5 comments · Fixed by #4360

Comments

@dblclik
Copy link

dblclik commented Mar 7, 2019

This is less a bug with the code base and more a bug in the examples, but as a result a number of pieces of example code do not work any longer.

I have cobbled together code that uses the Directed Graph (https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_directed.html#) example code to implement color maps, line alphas, and a colorbar. Apparently matplotlib.collections.LineCollection objects are no longer iterable, and thus the code

for i, arc in enumerate(edges):
    arc.set_alpha(edge_alphas[i])

does not work (nor does using something like for e in edges: or for i in range(len(edges)):

The problem also arises in trying to create a colorbar legend by passing the edges object to mpl.collections.PatchCollection() as this too raise the error that LineCollection objects are not iterable.

Versions:

Python - 3.6
Networkx - 2.2
Matplotlib - 3.0.2

Here is the full code (assuming you have a graph G already):

pos = nx.circular_layout(G)

M = G.number_of_edges()
node_sizes = [3 + 10 * i for i in range(len(G))]
edge_colors = range(2, M + 2)
edge_alphas = [(5 + i) / (M + 4) for i in range(M)]

nodes = nx.draw_networkx_nodes(G, pos, node_color='red')
edges = nx.draw_networkx_edges(G, pos, edge_color=edge_colors,edge_cmap=plt.cm.coolwarm, width=2)

nx.draw_networkx_labels(G,pos,node_labels,font_size=16)

# set alpha value for each edge
for i, arc in enumerate(edges):
    arc.set_alpha(edge_alphas[i])

pc = mpl.collections.PatchCollection(edges, cmap=plt.cm.Blues)
pc.set_array(edge_colors)
plt.colorbar(pc)

plt.axis('off')
plt.show()
@dschult
Copy link
Member

dschult commented Mar 19, 2019

The example code you point to doesn't have a list that iterates over the edges.
Both stable and latest version of documentation uses the following idiom to set the alpha values:

# set alpha value for each edge
for i in range(M):
    edges[i].set_alpha(edge_alphas[i])

Try the code at this documentation link

@dblclik
Copy link
Author

dblclik commented Mar 19, 2019

Thanks for the reply. I have tried this way just now and it does not work either.

This might be an issue with a loss of functionality in a new version of matplotlib, what version of matplotlib are you using? I am on 3.0.2, which apparently does not support indexing on LineCollection objects.

@dschult
Copy link
Member

dschult commented Mar 21, 2019

Hmmm... I think 3.0.2 does allow indexing on LineCollection. I have used it with 3.0.3 so I know it works there, and it worked with 2.x.

This is an example session. Figure shows up fine -- no errors.

Python 3.6.8 |Anaconda, Inc.| (default, Dec 29 2018, 19:04:46) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import matplotlib as mpl

In [2]: mpl.__version__
Out[2]: '3.0.3'

In [3]: from  __future__ import division
   ...: import matplotlib as mpl
   ...: import matplotlib.pyplot as plt
   ...: import networkx as nx
   ...: 
   ...: G = nx.generators.directed.random_k_out_graph(10, 3, 0.5)
   ...: pos = nx.layout.spring_layout(G)
   ...: 
   ...: node_sizes = [3 + 10 * i for i in range(len(G))]
   ...: M = G.number_of_edges()
   ...: edge_colors = range(2, M + 2)
   ...: edge_alphas = [(5 + i) / (M + 4) for i in range(M)]
   ...: 
   ...: nodes = nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='blue')
   ...: edges = nx.draw_networkx_edges(G, pos, node_size=node_sizes, arrowstyle='->',
   ...:                                arrowsize=10, edge_color=edge_colors,
   ...:                                edge_cmap=plt.cm.Blues, width=2)
   ...: # set alpha value for each edge
   ...: for i in range(M):
   ...:     edges[i].set_alpha(edge_alphas[i])
   ...: 
   ...: pc = mpl.collections.PatchCollection(edges, cmap=plt.cm.Blues)
   ...: pc.set_array(edge_colors)
   ...: plt.colorbar(pc)
   ...: 
   ...: ax = plt.gca()
   ...: ax.set_axis_off()
   ...: plt.show()
   ...: 

@ffranchina
Copy link

Hello everyone :) In the matplotlib documentation about Collections API they state (somehow, it's not very clear) that the Collections act as a whole. I've talked with the developers of matplotlib and they explained me that it's a design choice not to let the users iterate this monolithic structure. So it's not a bug.

As to the alpha channels of the edges, there's a trick. Not very elegant but, at least, relies fully on the public API and without any bad workaround. It works by setting the alpha values in the RGBA notation.

edges = nx.draw_networkx_edges(G, ... )
edges.set_alpha(None) # needed up to Networkx 3.6
edges.set_color( RGBAList )

Where RGBA_List is the list of the colors (with alpha channel) we want to assign to the edges. Unfortunately every other effort seems to make use of the internals of the LineCollection of matplotlib and, therefore, it's not reliable.

I hope it can help others in future :)

@tacaswell
Copy link
Contributor

I don't think that Matplotlib's Collection object have ever been iterable. Internally we do a fair amount of implicit broadcasting / zipping / colormapping that would be very difficult to keep in sync if we also let users reach in and touch individual elements (the number of notional elements is not ever really fixed if you set the offsets).

It looks like in nx21 (via #2760) nx changed to sometimes returning a LineCollection and sometimes returning a list of FancyArrowPatch objects so in some cases you can iterate over the object returned by draw_networkx_edges and sometimes not.

Not sure what the best path out of this is though.

tacaswell added a commit to tacaswell/networkx that referenced this issue Nov 15, 2020
draw_networkx_edges used touse a LineCollection (presumably for
performance reasons) when drawing un-directed graphs and generate many
FancyArrowPatch objects when drawing directed graphs.  This resulted
in issues due to return type instability (networkx#3337) and inconsistency
with handling the input (networkx#3398).

closes networkx#3398
closes networkx#3337
jarrodmillman pushed a commit that referenced this issue Nov 19, 2020
…4360)

* API: always use list of FancyArrowPatch rather than LineCollection

draw_networkx_edges used touse a LineCollection (presumably for
performance reasons) when drawing un-directed graphs and generate many
FancyArrowPatch objects when drawing directed graphs.  This resulted
in issues due to return type instability (#3337) and inconsistency
with handling the input (#3398).

closes #3398
closes #3337

* DOC: add link to PR

* MNT: clean out remaining LineCollection usage

* ENH: add a first attempt at drawing a self-loop

It works, but still needs some thought on deciding how big to draw the
self-loop.

* Change tests for LineCollection replacement with list of FancyArrows

Co-authored-by: Dan Schult <dschult@colgate.edu>
MridulS pushed a commit to MridulS/networkx that referenced this issue Feb 4, 2023
…etworkx#4360)

* API: always use list of FancyArrowPatch rather than LineCollection

draw_networkx_edges used touse a LineCollection (presumably for
performance reasons) when drawing un-directed graphs and generate many
FancyArrowPatch objects when drawing directed graphs.  This resulted
in issues due to return type instability (networkx#3337) and inconsistency
with handling the input (networkx#3398).

closes networkx#3398
closes networkx#3337

* DOC: add link to PR

* MNT: clean out remaining LineCollection usage

* ENH: add a first attempt at drawing a self-loop

It works, but still needs some thought on deciding how big to draw the
self-loop.

* Change tests for LineCollection replacement with list of FancyArrows

Co-authored-by: Dan Schult <dschult@colgate.edu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

4 participants