Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add title and legend to igraph plots

Tags:

python

igraph

In igraph for python, can you add a legend and title to a plot? Neither is mentioned in the manual or the tutorial as far as I can see. It is possible in R however.

like image 399
graffe Avatar asked Aug 15 '13 10:08

graffe


1 Answers

R provides a pretty advanced plotting system on its own and the R interface simply makes use of this so that's why you can simply create plot titles and legends in R. Python does not provide any plotting by default, so igraph uses the Cairo library to draw graph plots. However, Cairo is "just" a generic vector graphics library. That's why you don't get the same advanced plotting capabilities in Python.

The plot function of igraph creates a Plot object in the background, adds the graph being plotted to the plot itself, creates an appropriate Cairo surface for it, and then starts drawing the graph on the Cairo surface. All this happens behind the scenes if you simply call plot with a graph as an argument. However, you can create a Plot object manually and then add labels to it before it is being plotted, like this:

>>> plot = Plot("plot.png", bbox=(600, 600), background="white")

At this point, you have a plot variable, which is an instance of igraph.drawing.Plot. The plot is backed by a Cairo image surface which is 600 pixels wide and 600 pixels high, and which will eventually be saved into a filed named plot.png. (You can also supply a Cairo surface directly in the first argument of the Plot constructor). Calling plot.redraw() would draw the plot but not save it yet. Calling plot.save() would draw the plot if it has not been drawn yet and then save it to the given filename.

You can then do two things with a plot:

  1. Add an arbitrary object to the plot that has a __draw__ method. Graph objects have such a method so you can add a graph to the plot as follows:

    >>> g = Graph.GRG(100, 0.2)
    >>> plot.add(g, bbox=(20, 20, 580, 580))
    
  2. Grab its surface property to access the Cairo surface on which the drawing is done, construct a Cairo drawing context with this surface, and then draw on the plot directly with Cairo using the drawing context.

The second option is how we are going to add labels to the plot. Luckily igraph provides a class named TextDrawer in the igraph.drawing.text package that helps us a bit with wrapping and alignment issues. We simply have to create a TextDrawer and then call its draw_at method to add a label to the plot at a given location:

>>> import cairo
>>> context = cairo.Context(plot.surface)
>>> text_drawer = TextDrawer(context, text="Test label", halign=TextDrawer.LEFT)
>>> text_drawer.draw_at(x=100, y=100)

The TextDrawer will draw the label with the current font of the Cairo context, so you have to use the set_font_face, set_font_size and related methods of the Cairo context to adjust the font that is used for drawing.

Putting it all together, the example goes like this:

from igraph import Graph, Plot
from igraph.drawing.text import TextDrawer
import cairo

# Construct the plot
plot = Plot("plot.png", bbox=(600, 650), background="white")

# Create the graph and add it to the plot
g = Graph.GRG(100, 0.2)
plot.add(g, bbox=(20, 70, 580, 630))

# Make the plot draw itself on the Cairo surface
plot.redraw()

# Grab the surface, construct a drawing context and a TextDrawer
ctx = cairo.Context(plot.surface)
ctx.set_font_size(36)
drawer = TextDrawer(ctx, "Test title", halign=TextDrawer.CENTER)
drawer.draw_at(0, 40, width=600)

# Save the plot
plot.save()

The example will add a title to the plot. Constructing a legend is more involved but I hope you can proceed further based on this idea. The labels of the legend can be constructed with repeatedly calling the draw or draw_at method of a TextDrawer (after adjusting the text property of the TextDrawer between calls of course). You can draw a box around the legend using standard Cairo calls. You can also use the node drawer classes in igraph.drawing.shapes if you want to draw node shapes similar to those that igraph uses when it draws a graph.

like image 157
Tamás Avatar answered Oct 13 '22 12:10

Tamás