Revamping (yes!) the plotting interface: matplotlib and plotly#425
Revamping (yes!) the plotting interface: matplotlib and plotly#425ntamas merged 101 commits intoigraph:developfrom
Conversation
|
Question (really, just a question): What will be the performance effects of switching to matplotlib? How much performance do we lose or gain? Is plotting gigantic graphs (which are becoming more and more common) still possible, as e.g. on Tamás's website? http://sixdegrees.hu/last.fm/ |
|
@szhorvat That graph on my website was created with a custom plotting function, not with igraph's built-in plot subsystem; it would have been too large for that, plus it did not offer the opportunity to put images as backgrounds in the nodes. (If you zoom in close enough on the interactive version of the artist map, you can actually see the profile pictures of the artists in the nodes). I think that the current plotting backend is not really performant either, and I agree with @iosonofabio in the sense that it is probably a bit overengineered (this was my first attempt at a plotting backend). Also, I was in love with metaclasses at that time and I tried to solve everything with metaclasses :) So, making the Matplotlib backend more structured while keeping only the good bits of the current plotting backend is a good direction to go in. |
|
A quick update in case you are interested. Turns out many objects across the library have a This design might or might not stay depending on everyone's opinion, but at least it'd be great to move all floating chunks of Cairo code into |
|
Thanks for the update @iosonofabio ; I'm keeping an eye on this PR. Yes, ideally all Cairo-specific stuff should be hidden behind the Cairo backend in |
|
Alright, three things obviously missing and then it's ready for discussion:
Will take another day or two I think. |
|
Alright, everything is implemented except for label positioning for vertices and edges. The main reason is that I'm a little unsure how matplotlib handles calculations for text box sizes across its own backends. @ntamas if you could take an initial look at the many changes and advise on improvements, that'd be great. I'll try to figure out the text box thing in the meanwhile. This should close a bunch of open bugs because it aligns the matplotlib infra with the existing Cairo, i.e. anything Cairo does right or wrong, mpl just follows. Thanks! |
|
There's one more thing I could do here, but it'd be good to hear opinions on it. ATM in the single If we do this (I'd be in favour) then it'd be a good idea to rename all those What do you think? |
|
I'll go through the commits in this PR shortly, but a few responses first:
|
|
Okay, it seems like a common pattern that you need is "if the context seems to imply that the user wants the Matplotlib backend, then construct a I'm starting to think that maybe the I've also seen that you have created a |
|
Thanks @ntamas Re the if self._backend == 'matplotlib':
(function body)
else:
(function body)which is quite an antipattern I think. Slightly better would be to have pairs of methods: class Plot:
def _redraw_matplotlib(self):
...
def _redraw_cairo(self)
...
def redraw(self):
'''(docstring)'''
return getattr(self, '_redraw_'+self._backend)()Next version would be to have two classes, This infrastructure, as well as bounding boxes, can be all done by hand in matplotlib, but most people wouldn't. That works especially well in interactive backends, where you can dynamically resize the figure with the mouse, because then the absolute pixel coordinates of the plotted objects change (I do that all the time): matplotlib registers a callback function into the interactive backend that updates the Alright, in conclusion let me have a look and see if I can come up with something that makes sense. |
|
@iosonofabio Maybe this is of interest to you? Does matplotlib have built-in legending which can easily be made use of with the new matplotlib backend, but not with cairo? |
|
Thanks @szhorvat yes matplotlib has infra for legends. That could be an easy option for clusters, happy to implement it if you think it'd be useful. |
|
I only have minimal experience with matplotlib so I can't judge if this should be built-in to igraph, or if matplotlib support has already made it much easier to add legends than it was with cairo (if yes, you might write an answer on StackOverflow?) Just wanted to bring it to your attention that there is demand for legends. You'll know better than me how to best satisfy that demand, or if anything needs to be done on igraph's side. |
|
Thanks, if there's a demand I'll try to include that in this PR. |
|
I now tried to restructure the I also injected backend info into the I also implemented the legend @szhorvat mentioned, only in matplotlib, and without documentation for now (see above picture) |
|
There is progress, now the import error for plotly doesn't happen anymore. We are left with two things to fix (I'll take care of both ASAP, hopefully):
I'll try a few more commits |
|
Alright, I'm getting painfully aware of the constraints here. Seems like matplotlib conversion PDF->PNG requires ghostscript 9.0. My laptop has 9.54, but of course cibuildwheel images have 8.70 so mpl refuses to acknowledge that installation. After all, gs 9.0 was released quite recently, just short of 12 years ago... 😭 But turns out cibuildwheels has options to specify the manylinux image to use, and manylinux2014, although a youngling at 8 years of age, might have ghostscrpt 9.0 which was already going to kindergarden at the time. I'm checking if that avenue is available, otherwise I'd be in favour of just using PNGs and debugging why the linux i686 platform produces large RMS differences compared to the expected outputs. edit: manylinux2014 indeed works... except for Python 3.6?? anyway, ensuring the right version of ghostscript is present across architechtures, oses, and Python versions seems like an expensive maintenence move compared to a few more kbs of images in the repo, so I'm going back to using PNG images as default now. |
|
Ok, it now works except for an installation issue on Pypy 3.6/3.7 across OSes which to me looks unrelated to matplotlib. @ntamas sorry for the verbose PR: do you have any idea what the pypy thing could be about? |
|
Ok, finally skipping matplotlib tests on Pypy since they require numpy and that doesn't seem to install well on some pypy versions. |
|
I think this is ready for (squash and?) merge. I'll open a separate PR later on to improve docs and tutorials, esp. with the new backends. |
|
@iosonofabio PyPy is mostly a mystery to me as well; I have never used it seriously. I wouldn't be surprised if someone told me that NumPy and/or Matplotlib is not compatible with PyPy at all, so I think it's okay to ignore the failure. |
|
Re-targeting to |
|
Okay, all tests passed even after rebasing; let me go through the PR once again tomorrow and then I'll merge. |
|
Fantastic than you |
…n 3.6 users from using matplotlib [ci skip]
|
🎉 ! |
|
Yaaay, revamp successful! |

While
matplotlibis supported for plotting now, the actual draw function is a hack.By comparison, the
Cairodrawing infrastructure is quite extensive - perhaps too much so.Here I'm trying to clone the good parts of the Cairo interface into a revamped matplotlib interface, to stop the hacking from proliferating. I generally keep the inheritance a little shallower than it used to be, for sake of readability.
There is one issue on the table: matplotlib usually groups objects of the same kind for efficiency reasons into "collections". For instance, ATM all vertices are a single "collection" under
ax.get_children. That grouping, which is reminescent of how SVG files work, is honestly convenient and enables changing color/sizes of all vertices at once. Ideally I'll try to have one collection for the vertices and one for the edges (not sure you can have one for the labels?), but that might fail.I'll keep you posted.
edit: for folks who are new to this thread: I've redesigned the matplotlib plotting interface to integrate properly with the rest of the library, including:
plotting.backendto switch btwcairoandmatplotlibNOTE: I could expand point 5. to include tests for Cairo. The testing infrastructure would rely on matplotlib anyway though.
The main entry point for plotting is now
ig.plot. For Cairo, that creates aCairoPlotclass as before (was calledPlot, that name is still available for API compatibility). For matplotlib, that uses or creates a matplotlibAxes, just like any other library on the planet that relies on matplotlib.Axesare abstractions that fulfill the role ofCairoPlotin a generic way.There's currently a few
#FIXMEtags across the codebase for small things, mostly deprecation warnings. After that's fixed, it'd be great to have one or two of you testing this and then merging.