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.
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:
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))
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.
Related
I am using Matplotlib 1.5.3 in Python 3. I have a 3x3 subplot structure, or more generically an unspecified subplot structure that I'm trying to add a color bar to. As per this thread, an apparently good way to do this is to distort the subplots with subplots_adjust(), and add the colorbar as a new axes. Except, I have tight_layout() enabled, and that totally messes with things. Here is the function that, based on what I have read about subplots_adjust(), should work:
import matplotlib.pyplot as plt
def add_colorbar(last_im):
SPACE = 0.2 # portion of final width reserved for colorbar and its padding
PADDING = 0.5 # portion of reserved space reserved for padding
fig = plt.gcf()
# expand image to make room for colorbar
w,h = fig.get_size_inches()
fig.set_size_inches((w/(1-SPACE), h))
# shrink right side of subplot to create empty space on
# right hand side
fig.subplots_adjust(right=0.9*(1-SPACE)) # 0.9 being the original value
# create colorbar axes, place in empty space with padding
cbax = fig.add_axes([1-SPACE*(1-PADDING/2), 0.15,
SPACE*(1-PADDING), 0.7])
fig.colorbar(last_im, cax=cbax)
But the subplot configuration is kept centered, so this creates basically no space, and the color bar is drawn straight over the subplots. I have also tried using plt.tight_layout(rect=[0, 0, 1-SPACE, 1]) instead of subplots_adjust(), but this seems to do even less than the subplots_adjust() statement, and messes with basically just the sizes of the individual subplots. It seems neither of these functions work as advertised for me. What am I missing? Faulty plot shown below, with plot titles censored to be on the safe side.
Alternatively, I'd be fine with a solution for adding a colorbar that will generically work for a figure with any subplot configuration, but I'd prefer to understand the baffling behavior of subplots_adjust() and the tight_layout() rect.
EDIT: Problem ended up being that I made tight_layout() calls erroneously after running add_colorbar(). Correct behavior is observed now that I have removed the calls.
How can we increase the line width of plots produced by plot_implicit from Sympy? Neither I can find a parameter designated to this in the API, nor does Google give any expedient cues.
I've read that, e.g., plot_implicit(Eq(x1**2 + x2**2 - 1))._backend.ax references an Axis object of Matplotlib, so I tried changing the default line width of Matplotlib prior to invoking plot_implicit from Sympy, but it doesn't make any change:
from matplotlib import rcParams
rcParams['lines.linewidth'] = 10
In case that this isn't possible (which is hard to believe), what other options are there to produce implicit plots of functions in Python, such that the line width can be adapted?
From the plot_implicit documentation:
plot_implicit, by default, uses interval arithmetic to plot functions. If the expression cannot be plotted using interval arithmetic, it defaults to a generating a contour using a mesh grid of fixed number of points.
This means that the resulting plot is not a line plot, for which the line width can be set in any way.
The mesh gridding effect can also be observed when zooming into the plot:
I have a polar axes in matplotlib that has text which extends outside of the range of the axes. I would like to remove the border for the axis -- or set it to the color of the background so that the text is more legible. How can I do this?
Simply increasing the size of the axes is not an acceptable solution (because the figure is embeddable in a GUI and it becomes too small if this is done). Changing the color of the background to be black so that the border is not visible is also not an acceptable solution.
A considerable amount of code that does various parts of plotting things is omitted, but here is the generation of the figure and axes itself:
import pylab as pl
fig = pl.figure(figsize=(5,5), facecolor='white')
axes = pl.subplot(111, polar=True, axisbg='white')
pl.xticks([])
pl.yticks([])
pl.ylim(0,10)
# ... draw lots of things
Just add this line: axes.spines['polar'].set_visible(False) and it should go away!
eewh, all the anatomy terms.
A more general way (independent of coordinate systems) is:
axes.axis("off")
Simple question, but I've tried a few things and nothing seems to work.
I want to overlay some statistics onto a 3d VTK scene, using 2D vtkTextActors. This works fine, but the text is at times difficult to see, depending on what appears behind it in the 3D scene.
For this reason, I'd like to add a 2d, semi-transparent "box" behind my text actors to provide a darker background.
Which VTK object is appropriate for this? I've tried so far:
vtkLegendBoxActor: Not what I want, but I can use this with no text to display a semi-transparent box on screen. I cannot size it directly and I get warnings about not initialising some of the content.
vtkImageData: Tried manually creating image data and adding it to the scene; I believe it was placed within the 3d scene and not used as an overlay. If that's not the case then I couldn't get it to show at all.
vtkCornerAnnotation: Scales with window size, is fixed to a corner and the background opacity cannot be set AFAIK.
vtkTextActor: Cannot set a background color or opacity
Can anyone tell me how they might achieve what I'm after in VTK?
I've found a way to do this with vtkPolyMapper2D which seems to work okay. It seems to be a very stupid way to do this. If there is something more elegant, I'm all ears.
import vtk
extents = [[0,0],[620,0],[620,220],[0,220]]
polyPoints = vtk.vtkPoints()
for x, y in extents:
polyPoints.InsertNextPoint(x, y, 0)
num_corners = len(extents)
polyCells = vtk.vtkCellArray()
polyCells.InsertNextCell(num_corners + 1)
for i in range(0, num_corners):
polyCells.InsertCellPoint(i)
polyCells.InsertCellPoint(0) ## Rejoin at the end
poly_profile = vtk.vtkPolyData()
poly_profile.SetPoints(polyPoints)
poly_profile.SetPolys(polyCells) ## Goes solid
cut_triangles = vtk.vtkTriangleFilter()
cut_triangles.SetInput(poly_profile)
_poly_mapper = vtk.vtkPolyDataMapper2D()
_poly_mapper.SetInput(poly_profile)
_poly_mapper.SetInputConnection(cut_triangles.GetOutputPort())
_actor = vtk.vtkActor2D()
_actor.SetMapper(_poly_mapper)
_actor.GetProperty().SetColor([0.1,0.1,0.1])
_actor.GetProperty().SetOpacity(0.5)
#Add to renderer as normal
just use vtktexture, vtkimagedata & add your own image as texture background to the vtkrenderer by reducing the opacity like a watermark. thats it
I have an application in which I'd like to draw counties from a shapefile using Basemap. Drawing the county polygons is the bottleneck in the rendering, and since I'll be drawing the same region of the US (a bunch of times), I'd rather not have to draw all the polygons any more than I need to. So I had the idea to draw the counties to a figure with a transparent background, copy the axes to a pixel buffer using copy_from_bbox(), and restore the buffer using restore_region() when I need to draw the counties.
The basic code goes like this:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
map = Basemap(...) # Create Basemap object
map.readshapefile("countyp020", 'counties', linewidth=0.5) # Draws the county lines
plt.gcf().patch.set_alpha(0.0)
plt.gca().patch.set_alpha(0.0)
# Copy to the pixel buffer (county_buffer is of type BufferRegion)
county_buffer = plt.gcf().canvas.copy_from_bbox(plt.gca().bbox)
plt.clf() # This line is problematic (see below)
# Plot my data here ...
# Restore the pixel buffer
plt.gcf().canvas.restore_region(county_buffer)
plt.gcf().canvas.blit(plt.gca().bbox) # Not sure if this line is necessary
plt.gcf().canvas.draw()
It works like a charm ... except for the line where I clear the figure. Clearing the figure between renderings apparently clears the BufferRegion object as well, and since I update the title and colorbar, I'd also like to clear the figure between renderings.
So my question is does anybody know a way to clear the figure and keep the pixel buffer intact? I haven't been able to find much documentation on BufferRegion, copy_from_bbox(), or restore_region(), so it's been a bit difficult to debug this. If there's no easy way around it, then does anybody know another way to do basically what I'm trying to do?
Thanks in advance!