Differences between revisions 7 and 8

Deletions are marked like this. Additions are marked like this.
Line 28: Line 28:
To manually apply a transformation t to coordinates x,y, you can use t.xy_tup(x,y). To reverse this transformation, use t.inverse_xy_tup(x,y). It is very important to use these functions and not to convert coordinates by hand. Only this way you can be sure that your calculations work for non-linear coordinate systems as well (e.g. logarithmic or polar plots). xy_tup() is no more. Please see the official Matplotlib documentation at http://matplotlib.sourceforge.net/users/transforms_tutorial.html for further reference.

Whenever you pass coordinates to matplotlib, the question arises, what kind of coordinates you mean. Consider the following example

axes.text(x,y, "my label")

A label 'my label' is added to the axes at the coordinates x,y, or stated more clearly: The text is placed at the theoretical position of a data point (x,y). Thus we would speak of "data coords". There are however other coordinates one can think of. You might e.g. want to put a label in the exact middle of your graph. If you specified this by the method above, then you would need to determine the minimum and maximum values of x and y to determine the middle. However, using transforms, you can simply use

axes.text(0.5, 0.5, "middle of graph", transform=axes.transAxes)

There are four built-in transforms that you should be aware of (let ax be an Axes instance and fig a Figure instance):

   1 matplotlib.transforms.identity_transform()  # display coords
   2 ax.transData     # data coords
   3 ax.transAxes     # 0,0 is bottom,left of axes and 1,1 is top,right
   4 fig.transFigure  # 0,0 is bottom,left of figure and 1,1 is top,right

These transformations can be used for any kind of Artist, not just for text objects.

The default transformation for ax.text is ax.transData and the default transformation for fig.text is fig.transFigure.

Of course, you can define more general transformations, e.g. matplotlib.transforms.Affine, but the four listed above arise in a lot of applications.

xy_tup() is no more. Please see the official Matplotlib documentation at http://matplotlib.sourceforge.net/users/transforms_tutorial.html for further reference.

Example: tick label like annotations

If you find that the built-in tick labels of Matplotlib are not enough for you, you can use transformations to implement something similar. Here is an example that draws annotations below the tick labels, and uses a transformation to guarantee that the x coordinates of the annotation correspond to the x coordinates of the plot, but the y coordinates are at a fixed position, independent of the scale of the plot:

   1 import matplotlib as M
   2 import Numeric as N
   3 import pylab as P
   4 blend = M.transforms.blend_xy_sep_transform
   5 
   6 def doplot(fig, subplot, function):
   7     ax = fig.add_subplot(subplot)
   8     x = N.arange(0, 2*N.pi, 0.05)
   9     ax.plot(x, function(x))
  10 
  11     trans = blend(ax.transData, ax.transAxes)
  12 
  13     for x,text in [(0.0, '|'), (N.pi/2, r'$\rm{zero\ to\ }\pi$'),
  14                    (N.pi, '|'), (N.pi*1.5, r'$\pi\rm{\ to\ }2\pi$'),
  15                    (2*N.pi, '|')]:
  16         ax.text(x, -0.1, text, transform=trans,
  17                 horizontalalignment='center')
  18 
  19 fig = P.figure()
  20 doplot(fig, 121, N.sin)
  21 doplot(fig, 122, lambda x: 10*N.sin(x))
  22 P.show()

Example: adding a pixel offset to data coords

Sometimes you want to specify that a label is shown a fixed pixel offset from the corresponding data point, regardless of zooming. Here is one way to do it; try running this in an interactive backend, and zooming and panning the figure.

The way this works is by first taking a shallow copy of transData and then adding an offset to it. All transformations can have an offset which can be modified with set_offset, and the copying is necessary to avoid modifying the transform of the data itself. New enough versions of matplotlib (currently only the svn version) have an offset_copy function which does this automatically.

   1 import matplotlib
   2 import matplotlib.transforms
   3 from pylab import figure, show
   4 
   5 # New enough versions have offset_copy by Eric Firing:
   6 if 'offset_copy' in dir(matplotlib.transforms):
   7     from matplotlib.transforms import offset_copy
   8     def offset(ax, x, y):
   9         return offset_copy(ax.transData, x=x, y=y, units='dots')
  10 else: # Without offset_copy we have to do some black transform magic
  11     from matplotlib.transforms import blend_xy_sep_transform, identity_transform
  12     def offset(ax, x, y):
  13         # This trick makes a shallow copy of ax.transData (but fails for polar plots):
  14         trans = blend_xy_sep_transform(ax.transData, ax.transData)
  15         # Now we set the offset in pixels
  16         trans.set_offset((x,y), identity_transform())
  17         return trans
  18 
  19 fig=figure()
  20 ax=fig.add_subplot(111)
  21 
  22 # plot some data
  23 x = (3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3)
  24 y = (2,7,1,8,2,8,1,8,2,8,4,5,9,0,4,5)
  25 ax.plot(x,y,'.')
  26 
  27 # add labels
  28 trans=offset(ax, 10, 5)
  29 for a,b in zip(x,y):
  30     ax.text(a, b, '(%d,%d)'%(a,b), transform=trans)
  31 
  32 show()


CategoryCookbookMatplotlib

Cookbook/Matplotlib/Transformations (last edited 2010-06-16 16:48:14 by MalteDik)