When using python-igraph in a Jupyter notebook, I find the default size of plots to be much too large.
How can I change the default size?
I can control the size of each plot using the bbox argument, but I am looking to set the default size once, for the entire session.
Another, related question:
The default size is (600, 600), but this is not in pixels, as the figure is much bigger than 600 pixel wide. Is it possible to proportionally downscale all figures? Ideally, I would prefer to keep the (600, 600) size, as this provides a nice balance between the figure size and the default vertex size, and simply downscale everything proportionally to, say, 50%.
EDIT: @tamas, @vtraag, any tips on this? Or is this more of a Jupyter question than an igraph one?
First, let’s start with the size. This is passed intact to the underlying Cairo plotting backend, which treats it differently depending on what surface you are plotting on. PDF and PS surfaces will treat it as points (1 point = 1/72 inch). Image surfaces will treat it as pixels. SVG surfaces – well, I have no idea, I think it depends on the SVG viewer. Years ago it seemed that Firefox will treat the size as pixels, but I’m not that sure any more.
Next, about setting the default size. I believe this is not possible at the moment; years ago I have started to implement a rudimentary configuration mechanism for python-igraph (see the igraph.configuration module), which takes sensible defaults from ~/.igraphrc, and it works, but the default bounding box size is not among the configurable parameters yet. I guess it wouldn’t be too difficult to add it - then we would also need to update Plot.__init__() to look for the defaults in the configuration before falling back to (600, 600).
You could create your own version of the plot() function as well that uses a different default bounding box. It is fairly easy with functools.partial:
from functools import partial
from igraph import plot as _plot
plot = partial(_plot, bbox=(400, 400))
This may not be an issue with igraph, but it is still a concern for us, as all that a user sees is that matplotlib output is not affected, but igraph output is a pain to deal with.
One thing that could be complicating things is that the Matplotlib output might simply be “inlined” in the main document (the <svg> tag is added to the DOM of the HTML page directly), while in case of igraph we need to isolate each embedded <svg> in its own <iframe>. (This is because the SVG output generated by Cairo contains IDs that refer to different elements within the SVG representation, and multiple igraph plots may have SVG representations that share some of these IDs – we can do nothing about that, it’s Cairo that is generating the IDs. Matplotlib might be using a different plotting backend). The problem with <iframe> isolation is that the size of the <iframe> has to be specified explicitly, and I’m not sure what algorithm Jupyter uses to decide the size of the <iframe>.
Let’s ignore Safari for the time being, my general experience is that most web stuff works similarly (and nicely) in Chrome and Firefox (and, more recently, in Edge as well), but Safari always needs extra hacks and workarounds.
My suspicion: Jupyter somehow leaves the height of the iframe unspecified, leaving it up to the browser to decide. The width, however, is specified by Jupyter to fill the entire viewport. In Firefox, the browser then decides to use a default value for the height, while in Chrome, the browser seems to enforce a height that keeps the aspect ratio of the embedded SVG, and since the iframe was forced to fill the full width of the page, this yields a large height.
Okay, so the problem stems from the fact that we need to put SVGs in isolated iframes. We need to do that because of the way Cairo generates IDs for SVG elements – if you have more than one igraph plot in the same Jupyter notebook, the two SVGs could share some IDs, so they cannot co-exist in the same HTML page. The workaround is to put the SVGs in iframes, because each iframe creates an SVG “namespace”.
We could try the following, though. We take the SVG representation as it comes out of Cairo (as a string), parse it with an XML parser and manually re-write the IDs, prefixing each ID with a unique identifier that is generated on-the-fly for the plot. Then we would not need iframes, and Jupyter would not need to specify a width and a height for the iframe – problem solved. Assuming that we can parse the generated SVG and find a foolproof way to replace the IDs everywhere.