Lecture 18. Drawing Graphics in Sage using matplotlib

 

Though Sage provides its own functions (e.g,. plot, line, point, text, circle, etc.) for drawing 2d graphics, they are all very oriented toward visualizing the sorts of mathematical objects that come up in more pure mathematics (so more like Mathematica).  For the sort of scientific visualizing that comes up in applications, the matplotlib library provides functionality that is very similar to Matlab for plotting.     Also, matplotlib can be used on any major operating system without using Sage; it only depends on numpy, and has a very open license (BSD-compatible). 

Also, if you're drawing an image that involves a huge amount of data points, directly using matplotlib can be more efficient than using Sage's plotting, since Sage's plotting is built on top of matplotlib -- using matplotlib directly gets you closer to the metal.

Important Caveat

There are two absolutely critical things to remember when using matplotlib from Sage:

  1. Instead of plt.show() use plt.savefig('a.png').  Memorize this now.  This will make a nice smooth antialised png image of the plot appear in the Sage notebook.   Using plt.show() may just do nothing in Sage, depending on your setup (it might also popup a window).   You can also do plt.savefig('a.pdf') and plt.savefig('a.svg').
  2. You might have to put your input in a %python cell or turn off the preparser (by typing preparser(False)).

With these two hints, you should be able to to try out the examples at http://matplotlib.sourceforge.net/gallery.html.   

In fact, try it now [in class, go to the above website, scroll, and let students choose an example]:

  1. Click on the thumbnail image.
  2. Click source code in the upper left
  3. Paste the code into a notebook cell.
  4. Put %python as the first line of the cell.
  5. Change any .show() to .savefig('a.png')

Note: There are some images in the gallery that require some external data file (e.g, the brain image), so those won't work.

{{{id=5| /// }}}

For example, if students choose the first example, we get:

{{{id=4| %hide %python """ Show examples of matplotlib artists http://matplotlib.sourceforge.net/api/artist_api.html Several examples of standard matplotlib graphics primitives (artists) are drawn using matplotlib API. Full list of artists and the documentation is available at http://matplotlib.sourceforge.net/api/artist_api.html Copyright (c) 2010, Bartosz Telenczuk License: This work is licensed under the BSD. A copy should be included with this source code, and is also available at http://www.opensource.org/licenses/bsd-license.php """ import numpy as np import matplotlib.pyplot as plt import matplotlib from matplotlib.collections import PatchCollection import matplotlib.path as mpath import matplotlib.patches as mpatches import matplotlib.lines as mlines font = "sans-serif" fig = plt.figure(figsize=(5,5)) ax = plt.axes([0,0,1,1]) # create 3x3 grid to plot the artists pos = np.mgrid[0.2:0.8:3j, 0.2:0.8:3j].reshape(2, -1) patches = [] # add a circle art = mpatches.Circle(pos[:,0], 0.1,ec="none") patches.append(art) plt.text(pos[0,0], pos[1,0]-0.15, "Circle", ha="center", family=font, size=14) # add a rectangle art = mpatches.Rectangle(pos[:,1] - np.array([0.025, 0.05]), 0.05, 0.1, ec="none") patches.append(art) plt.text(pos[0,1], pos[1,1]-0.15, "Rectangle", ha="center", family=font, size=14) # add a wedge wedge = mpatches.Wedge(pos[:,2], 0.1, 30, 270, ec="none") patches.append(wedge) plt.text(pos[0,2], pos[1,2]-0.15, "Wedge", ha="center", family=font, size=14) # add a Polygon polygon = mpatches.RegularPolygon(pos[:,3], 5, 0.1) patches.append(polygon) plt.text(pos[0,3], pos[1,3]-0.15, "Polygon", ha="center", family=font, size=14) #add an ellipse ellipse = mpatches.Ellipse(pos[:,4], 0.2, 0.1) patches.append(ellipse) plt.text(pos[0,4], pos[1,4]-0.15, "Ellipse", ha="center", family=font, size=14) #add an arrow arrow = mpatches.Arrow(pos[0,5]-0.05, pos[1,5]-0.05, 0.1, 0.1, width=0.1) patches.append(arrow) plt.text(pos[0,5], pos[1,5]-0.15, "Arrow", ha="center", family=font, size=14) # add a path patch Path = mpath.Path verts = np.array([ (0.158, -0.257), (0.035, -0.11), (-0.175, 0.20), (0.0375, 0.20), (0.085, 0.115), (0.22, 0.32), (0.3, 0.005), (0.20, -0.05), (0.158, -0.257), ]) verts = verts-verts.mean(0) codes = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4, Path.LINETO, Path.CURVE4, Path.CURVE4, Path.CURVE4, Path.CLOSEPOLY] path = mpath.Path(verts/2.5+pos[:,6], codes) patch = mpatches.PathPatch(path) patches.append(patch) plt.text(pos[0,6], pos[1,6]-0.15, "PathPatch", ha="center", family=font, size=14) # add a fancy box fancybox = mpatches.FancyBboxPatch( pos[:,7]-np.array([0.025, 0.05]), 0.05, 0.1, boxstyle=mpatches.BoxStyle("Round", pad=0.02)) patches.append(fancybox) plt.text(pos[0,7], pos[1,7]-0.15, "FancyBoxPatch", ha="center", family=font, size=14) # add a line x,y = np.array([[-0.06, 0.0, 0.1], [0.05,-0.05, 0.05]]) line = mlines.Line2D(x+pos[0,8], y+pos[1,8], lw=5., alpha=0.4) plt.text(pos[0,8], pos[1,8]-0.15, "Line2D", ha="center", family=font, size=14) colors = 100*np.random.rand(len(patches)) collection = PatchCollection(patches, cmap=matplotlib.cm.jet, alpha=0.4) collection.set_array(np.array(colors)) ax.add_collection(collection) ax.add_line(line) ax.set_xticks([]) ax.set_yticks([]) plt.savefig('a.png') /// }}} {{{id=8| /// }}}

Pyplot

Matplotlib has an interface that works much like Matlab.  This will be very helpful if you know Matlab, and of some value otherwise since there is a lot of Matlab code and documentation out there. This mode is called "pyplot", and there is a now tutorial for it at http://matplotlib.sourceforge.net/users/pyplot_tutorial.html.  

Below we replicate several examples from this tutorial in Sage, and you should read this tutorial.  The main point you should realize when looking at these examples is how easily one can express scientific data visualization in terms of this interface; many equivalent plots are possibly directly with Sage's plotting commands, but they are less natural.

{{{id=18| import matplotlib.pyplot as plt plt.clf() plt.plot([1,2,3,4]) plt.ylabel('some numbers') plt.savefig('a.png', dpi=70) /// }}} {{{id=20| plt.clf() plt.plot([1,2,3,4], [1,4,9,16]) plt.savefig('a.png', dpi=70) /// }}} {{{id=19| plt.clf() # 'ro' = red circles, like in MATLAB; 'bx' = blue crosses. plt.plot([1,2,3,4], [1,4,9,16], 'ro', [5,5.5], [2,2], 'bx') plt.axis([0, 6, 0, 20]) plt.savefig('a.png', dpi=70) /// }}}

Use Numpy instead of Python lists:

{{{id=17| import numpy as np plt.clf() # evenly sampled time at 200ms intervals t = np.arange(0., 5., 0.2) # red dashes, blue squares and green triangles plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^') plt.savefig('a.png', dpi=70) /// }}}

Multiple figures and axis all at once:

{{{id=16| import numpy as np import matplotlib.pyplot as plt def f(t): return np.exp(-t) * np.cos(2*np.pi*t) t1 = np.arange(0.0, 5.0, 0.1) t2 = np.arange(0.0, 5.0, 0.02) plt.clf() plt.figure(1) plt.subplot(121) plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k') plt.subplot(122) plt.plot(t2, np.cos(2*np.pi*t2), 'r--') plt.savefig('a.png') /// }}}

An example involving text

{{{id=7| import numpy as np import matplotlib.pyplot as plt plt.clf() mu, sigma = 100, 15 x = mu + sigma * np.random.randn(10000) # the histogram of the data n, bins, patches = plt.hist(x, 50, normed=1, facecolor='g', alpha=0.75) plt.xlabel('Smarts') plt.ylabel('Probability') # bug -- gets chopped out below :-( plt.title('Histogram of IQ') plt.text(60, .025, r'$\mu=100,\ \sigma=15$') plt.axis([40, 160, 0, 0.03]) plt.grid(True) plt.savefig('a.png', dpi=70) /// }}}

Incidentally, you can of course combine matplotlib graphics with @interact

{{{id=25| import numpy as np import matplotlib.pyplot as plt plt.clf() mu, sigma = 100, 15 x = mu + sigma * np.random.randn(10000) @interact def f(bins=(5..150)): plt.clf() n, bins, patches = plt.hist(x, bins, normed=1, facecolor='g', alpha=0.75) plt.xlabel('Smarts', fontsize=18, color='red') plt.ylabel('Probability') # bug -- gets chopped out below plt.title('Histogram of IQ') plt.text(60, .025, r'$\mu=100,\ \sigma=15$') # latex! plt.axis([40, 160, 0, 0.03]) plt.grid(True) plt.savefig('a.png', dpi=70) ///
bins 
}}} {{{id=28| /// }}}

Annotation Example

{{{id=27| import numpy as np import matplotlib.pyplot as plt plt.clf() ax = plt.subplot(111) t = np.arange(0.0, 5.0, 0.01) s = np.cos(2*np.pi*t) line, = plt.plot(t, s, lw=2) plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5), arrowprops=dict(facecolor='black', shrink=0.07)) plt.ylim(-2,2) plt.savefig('a.png') /// }}}

There are tons of other examples of pyplot at the matplotlib website here: http://matplotlib.sourceforge.net/examples/pylab_examples/

For example we have the following economics example:

{{{id=31| """ make a scatter plot with varying color and size arguments """ import matplotlib import numpy as np import matplotlib.pyplot as plt import matplotlib.mlab as mlab import matplotlib.cbook as cbook # load a numpy record array from yahoo csv data with fields date, # open, close, volume, adj_close from the mpl-data/example directory. # The record array stores python datetime.date as an object array in # the date column datafile = cbook.get_sample_data('goog .npy') r = np.load(datafile).view(np.recarray) r = r[-250:] # get the most recent 250 trading days delta1 = np.diff(r.adj_close)/r.adj_close[:-1] # size in points ^2 volume = (15*r.volume[:-2]/r.volume[0])**2 close = 0.003*r.close[:-2]/0.003*r.open[:-2] fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(delta1[:-1], delta1[1:], c=close, s=volume, alpha=0.75) #ticks = arange(-0.06, 0.061, 0.02) #xticks(ticks) #yticks(ticks) ax.set_xlabel(r'$\Delta_i$', fontsize=20) ax.set_ylabel(r'$\Delta_{i+1}$', fontsize=20) ax.set_title('Volume and percent change') ax.grid(True) plt.savefig('a.png') /// }}} {{{id=30| /// }}} {{{id=24| /// }}} {{{id=3| /// }}}

There is more to matplotlib than just pyplot...

There is more to matplotlib than just a Matlab like interface.    Matplotlib has its own library interface, primitives, etc., which are documented here: http://matplotlib.sourceforge.net/contents.html.  Also, there is an strong community with much momentum behind matplotlib development.  It is the de facto standard for 2d plotting in Python, and it keeps getting better.

{{{id=11| /// }}} {{{id=10| /// }}}

A 3d Example

This is basically this example: http://matplotlib.sourceforge.net/examples/mplot3d/surface3d_demo.html

{{{id=14| %python from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm from matplotlib.ticker import LinearLocator, FixedLocator, FormatStrFormatter import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.gca(projection='3d') X = np.arange(-7, 7, 0.25) Y = np.arange(-7, 7, 0.25) X, Y = np.meshgrid(X, Y) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet, linewidth=0, antialiased=False) ax.set_zlim3d(-1.01, 1.01) ax.w_zaxis.set_major_locator(LinearLocator(10)) ax.w_zaxis.set_major_formatter(FormatStrFormatter('%.03f')) fig.colorbar(surf, shrink=0.5, aspect=5) plt.savefig('a.png') /// }}} {{{id=1| /// }}}