-
Notifications
You must be signed in to change notification settings - Fork 4
PyBCSession06
Other Wiki Pages: PyBc, Session01, Session02, Session03, Session04, Session05, Session06, Session07, Session08, Session09, f2py, swig, Cpython, Cython, PyTables, PyTaps, PythonBots, Django, GIS, AdvancedPython, WxPython, standardlib,
Do this while I talk. If this is your first use of matplotlib (ever on this machine), it will take a little longer starting up.
#!CodeExample #!python from matplotlib import pyplot
The ability to visually represent data is one of the more important tools to the scientific user of python. The most popular and arguably most mature library for producing publication quality 2D plots matplotlib (MPL). In the developers own words, MPL is
"matplotlib is a python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms. matplotlib can be used in python scripts, the python and ipython shell (ala !MatLab or mathematica), web application servers, and six graphical user interface toolkits."
The underlying structure of MPL is very general and customizable. Thankfully, you don't actually need to interact with all that power. The way to generate plots and modify them is through the pyplot interface. The pyplot interface is much much inspired by !Matlab's plotting commands, so it should if you're familiar with that it should be easy to pick up.
- matplotlib - raw access to the plotting library. useful for extending matplotlib or doing very custom things
- pyplot - !MatLab-like interface to matplotlib (use this one!)
- pylab - pyplot + numpy
Aside: Multiple Interfaces to MPL
In addition to the object oriented pyplot interface, pyplot implements a clone of !Matlab's plotting interface. The main difference is that the !Matlab interface has the paradigm of the "current figure." So plotting and labeling commands implicitly refer to the current figure/subplot. This is find, so long as your plotting commands are simple and sequential. However, when your program requires that you modify a figure in multiple contexts the !Matlab interface becomes very difficult to keep up with.
In this session, we will use the object oriented interface exclusively. It is trivial to degrade to the !Matlab interface if you're feeling lazy, but the object oriented interface is powerful. It is also more explicit, and, in my opinion, a better teaching tool.
Aside: pyplot vs pylab
When you go searching around the web for MPL help, you will find people importing pylab rather than matplotlib.pyplot. pylab imports both pyplot and numpy (and a few other things) together.
#!python # <snip from pylab.py> from numpy import * from numpy.fft import * from numpy.random import * from numpy.linalg import * # provide the recommended module abbrevs in the pylab namespace import matplotlib.pyplot as plt import numpy as np import numpy as np import numpy.ma as ma # <end snip>
First things first. Let's plot something. Open up a file, copy, paste, execute.
#!CodeExample #!python from matplotlib import pyplot as plt x = range(0,10) y = [val**2 for val in x] ax = plt.subplot(111) ax.plot(x,y) plt.show()
Aside: Interactive plotting
MPL is ipython aware. If you interactively plot things from ipython, your figures will automatically re-render. This can be nice, but potentially slow. You can also get into trouble if you close figures an inappropriate times. You have been warned.
MPL plots are organized into figures, subplots and axes.
- figure
- subplot
- axes
Lets make a plot. Once we start generating data, plotting things, setting xlabels and ylabels and titles and such, retyping everything is not fun. Fire up your favorite text editor.
- Make a figure
- Add a subplot to the figure, get an axes
- Plot on the axes
- Show the figure
#!CodeExample #!python from matplotlib import pyplot as plt fig = plt.figure() ax = fig.add_subplot(111) ax.plot([1,2,3,4,5,6]) plt.show()
Aside: other, sometimes more convenient ways to get an axes.
#!CodeExample #!python # Useful for figures composed of multiple subplots fig = plt.figure() ax1 = fig.add_subplot(211) # Useful for figures with one subplot (ie the subplot is the figure) ax2 = subplot(111) # Useful for overlaying axes, sharing the x axis ax3 = plt.twinx(ax2)
Plot is pretty powerful, lets peruse the [http://matplotlib.sourceforge.net/ MPL documentation].
Plot something. Plot it again with a different character. Change the color. Attach a label and then display the label with a legend. (ax.legend())
- legend
- colors
- linestyles
- kwargs !
MPL as all the usual types of plots
#!CodeExample #!python ax.semilogx(x,y) ax.semilogy(x,y) ax.loglog(x,y)
Notice that for the z argument to pcolor is shorter by one in both directions. You should think of z as being defined zone centered. If you forget to do so, it will drop the last row and column.
#!CodeExample #!python from matplotlib import pyplot as plt import numpy as np r = np.linspace(0.1,2,200) t = np.linspace(np.pi/4,7*np.pi/4,200) r,t = np.meshgrid(r,t) x = r*np.cos(t) y = r*np.sin(t) z = (x+0.6)**2 + (y-1.)**2 ax = plt.subplot(111) ax.pcolor(x,y,z[1:,1:]) # ax.colorbar() con = ax.contour(x,y,z) ax.clabel(con) plt.show()
!Matlab converts may have used this function to look at the structure of sparse [http://www.merriam-webster.com/medical/matrixes matrixes].
#!python n = 20 f = diag(ones(n)) + diag(ones(n-1),1) + diag(ones(n-3),-3) spy(f)
Masked arrays are numpy ndarrays that have a mask to hide elements of the ndarray. They are really handy to use when plotting and you just want to make particular parts of the plot "go away." In theory you could modify the underlying arrays to do the right thing, but that's a lot of work. You shouldn't have to do it if you don't have to. Masked arrays are the answer. For more detailed information, check out the [http://docs.scipy.org/doc/numpy/reference/maskedarray.html masked arrray documentation] and [http://docs.scipy.org/doc/numpy/reference/maskedarray.generic.html#constructing-masked-arrays constructing masked arrays].
Starting from the pcolor plot, add the following code after defining z and before plotting things.
#!python z = np.ma.masked_where( np.abs(y-x) < 0.2, z )
If you're changing plots after they render (ie show()) you have to tell MPL to draw the plot. draw() forces a figure to redraw. If you're doing all your plotting from scripts, you probably won't have to deal with draw(). If you're programming figures such that they change over the course of your program, you will need to use draw to refresh the figure.
MPL has an extensive [http://matplotlib.sourceforge.net/gallery.html, gallery] of plots with source code. Take a look through those; they probably have something similar to what you want to do.
- keyboard/mouse bindings (interactive plots!)
- [http://matplotlib.sourceforge.net/users/image_tutorial.html image plotting]
- Shared Axes Across Subplots
MPL has a built in mechanism for handling keyboard/mouse events in a platform independent way. This can be useful when you want to toggle different data on and off in the same figure, or step through time dependent data, or something else crazy that you dream up. All you need a a function that draws the figure and some logic to identify events that your care about. MPL handles the rest. Lets walk through the following code.
- A MovingPlot instance remembers which plot and axes to modify (no current figure nonsense!)
- In the constructor, we draw() the initial frame
- The attribute cid listens for "key press events" and calls self.update(event) when something happens
- event is a 'key_press_event' object, with attributes like "key"
- update() takes one argument: an event object (other than the unavoidable self)
- Based on the event object passed, update() only does something on right and left arrow key presses
- MPL has several other [http://matplotlib.sourceforge.net/api/backend_bases_api.html?highlight=canvas#matplotlib.backend_bases.FigureCanvasBase.mpl_connect supported events]
#!CodeExample #!python from matplotlib import pyplot as plt import numpy as np class MovingPlot(object): """ MovingPlot plots a figure that flows across the screen. Right and left arrows move the figure one step""" def =init=(self, r0= [-0.8, 1.0], v0 = [0.1, 0.]): # set up grid r = np.linspace(0.2,2,20) t = np.linspace(np.pi/4, 7*np.pi/4, 20) r,t = np.meshgrid(r,t) self.x = r*np.cos(t) self.y = r*np.sin(t) self.x0 = r0[0] self.y0 = r0[1] self.vx = v0[0] self.vy = v0[1] # create figure and axes for reuse self.fig = plt.figure() self.ax = self.fig.add_subplot(111) # draw the initial frame self.frame = 0 self.draw(self.frame) # call self.update everytime a 'key_press_event' happens # bind this behavior to an object so it persists self.cid = self.fig.canvas.mpl_connect('key_press_event', self.update) plt.show() def update(self, event): """Changes the frame number based on event and draws a new frame if the frame changed""" if event.key == 'right': print 'forward' self.frame += 1 elif event.key == 'left': print 'backward' self.frame -= 1 else: return self.draw(self.frame) def draw(self, t): """Draws the frame occuring at time=t""" x = self.x - self.vx*t y = self.y - self.vy*t z = (x-self.x0)**2 + (y-self.y0)**2 self.ax.pcolor(self.x, self.y, z) self.fig.canvas.draw() if __name__ == "__main__": mplot = MovingPlot()
MPL has the ability to plot images. Check out the following code. It plots 4 subplots
- an image of a bunny
- an image of a bunny with stuff over plotted
- an image of a generated array
- a pcolor plot of the same data
#!CodeExample #!python from matplotlib import pyplot as plt import numpy as np import urllib # read png into img try: img = plt.imread('bunny.png') except RuntimeError: print "can't find bunny.png, trying to download" # the image probably doesn't exits url = 'http://hackerwithin.org/cgi-bin/hackerwithin.fcgi/raw-attachment/wiki/PyBCSession06/bunny.png' urllib.urlretrieve(url, filename='bunny.png') img = plt.imread('bunny.png') fig = plt.figure() # plot just the bunny ax = fig.add_subplot(221) ax.imshow(img) ax.set_title('just the bunny') # plot bunny with stuff on top ax = fig.add_subplot(223) ax.imshow(img) xmin,xmax = ax.get_xlim() # plot changes the plotting limits ymin,ymax = ax.get_ylim() # snag them here for future uses x = np.linspace(100, 700, 10) y = np.sin(x/100)*300 + 300 ax.plot(x,y) ax.set_xlim(xmin,xmax) # reset the limits to what imshow would have them be ax.set_ylim(ymin,ymax) ax.set_title('bunny with overlaid plots') # plot an array of doubles ax = fig.add_subplot(222) x = np.linspace(0,200, 10) y = np.linspace(0,200, 10) x,y = np.meshgrid(x,y) z = np.sin(x/200. * 2*np.pi) * np.sin(y/200. * 2*np.pi) * x/200 * y/200 image = ax.imshow(z) plt.colorbar(image) ax.set_title('imshow() of array') # plot pcolor of z ax = fig.add_subplot(224) image = ax.pcolor(z) plt.colorbar(image) ax.set_title('pcolor() of same array array') plt.show()
Take a look at the following plotting script. It plots 4 subplots. If the bottom left subplot is the anchor, 1 subplot shares the xaxis (top left). 1 subplot shares the yaxis (bottom right). 1 subplot shares both axes (top right).
#!CodeExample #!python from matplotlib import pyplot as plt import numpy as np t = np.arange(0.01, 5.0, 0.01) s1 = np.sin(2*np.pi*t) s2 = np.exp(-t) s3 = np.sin(4*np.pi*t) fig = plt.figure() ax1 = fig.add_subplot(223) ax1.plot(t,s1) ax1.axvline(2, color='k', lw=3) ax1.axhline(0, color='r', lw=3) ## share x only ax2= fig.add_subplot(221, sharex ax1) ax2.plot(t, s2) ax2.axvline(2, color='k', lw=3) ax2.axhline(0, color='r', lw= 3) # make x tick labels invisible plt.setp( ax2.get_xticklabels(), visible=False) ## share x and y ax3 = fig.add_subplot(222, sharex= ax1, sharey=ax1) ax3.plot(t, s3) ax3.axvline(2, color='k', lw=3) ax3.axhline(0, color= 'r', lw=3) ax3.set_xlim(0.01,5.0) # make tick labels small plt.setp( ax3.get_xticklabels(), fontsize=6) plt.setp( ax3.get_yticklabels(), fontsize=6) ## share y only ax4 = fig.add=subplot(224, sharey = ax1) ax4.plot(t, s3) ax4.axvline(2, color= 'k', lw = 3) ax4.axhline(0, color= 'r', lw = 3) # make y tick labels invisible plt.setp( ax4.get_yticklabels(), visible=False) plt.show()