- •Credits
- •About the Author
- •About the Reviewers
- •www.PacktPub.com
- •Preface
- •Getting started
- •More advanced graphics
- •Summary
- •Installing Sage
- •Starting Sage
- •Start Sage
- •Prerequisites
- •Installation
- •Summary
- •Command history
- •Working with code
- •Arithmetic operators
- •Strings
- •Functions
- •Functions with keyword arguments
- •Objects
- •Summary
- •Python 2 and Python 3
- •Running scripts
- •Strings
- •List comprehensions
- •Storing data in a dictionary
- •Summary
- •Vectors and vector spaces
- •Creating a vector space
- •Vector operators and methods
- •Decomposing matrices
- •Summary
- •Using graphics primitives
- •Summary
- •Substitutions
- •Finding roots
- •Derivatives
- •Integrals
- •Series and summations
- •Summary
- •Computing gradients
- •Constrained optimization
- •Probability
- •Summary
- •Making our tanks move
- •Unit testing
- •Summary
- •Introducing Python decorators
- •Making interactive graphics
- •Summary
- •Index
Chapter 6
To display the two plots, we introduced a new function called graphics_array, which uses the basic syntax:
graphics_array([plot_1, plot_2, ..., plot_n], num_rows, num_columns)
This function returns another graphics object, and we used the show method to display the plots. We used the keyword argument gridlines=["minor", False] to tell Sage to display vertical lines at each of the minor ticks on the x axis. The first item in the list specifies vertical grid lines, and the second specifies horizontal grid lines. The following options can be used for either element:
"major" |
Grid lines at major ticks |
"minor" |
Grid lines at major and minor ticks |
False |
No grid lines |
Try playing with these options in the previous example.
Using graphics primitives
We've already seen that Sage has graphics primitives such as lines and text annotations. Sage has other types of graphics primitives that can be used for plotting.
Timeforaction–plottingwithgraphicsprimitives
A class of mathematical models called random sequential adsorption (RSA) models deals with the patterns that result when two-dimensional shapes are randomly deposited onto a plane. The following method can be used to visualize these kinds of models:
#Since the circles are random, your plot will not
#look exactly like the example!
circle_list = [] for i in range(15):
x = -5 + 10 * random() y = -5 + 10 * random()
circle_list.append(circle((x, y), 1, facecolor='red', edgecolor=(0, 0, 1), thickness=2, fill=True))
gr = sum(circle_list) gr.axes(False)
gr.show(aspect_ratio=1, frame=True, gridlines=True, figsize=(4, 4))
[ 153 ]
Plotting with Sage
You should get a plot that resembles the one below. Because the positions of the circles are randomly generated, your plot will not look exactly like this one.
What just happened?
We created a list of graphics objects with a for loop and the circle function. The basic syntax for the circle function is as follows:
graphics_object = circle((center_x, center_y), radius)
In order to plot all the circles at once, we used the sum function to add up the list. We then prevented the axes from being drawn by calling the method gr.axes(False). Finally, when calling the show method we used the keyword argument frame=True to draw a frame, with ticks and labels, around the outside of the plotting area. The argument gridlines=True is a shortcut to activate both horizontal and vertical grid lines. Sage has many other types of primitives, including elliptical arcs, arrows, disks, ellipses, points, and polygons, that are described in the Sage reference manual.
[ 154 ]
Chapter 6
Usingmatplotlib
We can access matplotlib directly to do things that we can't do with Sage plotting functions. matplotlib has such a large number of options and features that it deserves a separate book. We will only touch on a few basic features. If you want to know more, the matplotlib website has excellent documentation:
http://matplotlib.sourceforge.net/contents.html
Timeforaction–plottingfunctionswithmatplotlib
To illustrate the similarities and differences between plotting with matplotlib and plotting with Sage, we will repeat the first example of this chapter using the Pyplot interface to matplotlib. Enter and evaluate the following code:
import numpy
import matplotlib.pyplot as plt
x = numpy.arange(-2 * numpy.pi, 2 * numpy.pi, 0.1) func1 = numpy.sin(x)
func2 = numpy.cos(x)
plt.figure(figsize=(5.5, 3.7)) # size in inches plt.plot(x, func1, linewidth=2.0, color=(0.5, 1,0), label='$f(x)=sin(x)$')
plt.plot(x, func2, linewidth=3.0, color='purple', alpha=0.5, label='$f(x)=cos(x)$')
plt.xlabel('$x$') plt.ylabel('$f(x)$') plt.title('Plotting with matplotlib') plt.legend(loc='lower left') plt.savefig('demo1.png')
plt.close()
[ 155 ]
Plotting with Sage
The result should be as follows:
What just happened?
We gained access to matplotlib functions and types with the following line:
import matplotlib.pyplot as plt
We can now access these functions as plt.function_name. We also import numpy so that we can use its numerical arrays.
There is an important difference between the Sage plot function and the matplotlib plot function. matplotlib always plots lists or arrays of discrete points, rather than callable symbolic expressions. This block of code creates an array of x values:
x = numpy.arange(-2*numpy.pi, 2*numpy.pi, 0.1) func1 = numpy.sin(x)
func2 = numpy.cos(x)
func1 and func2 are arrays of y values. Notice that we specify the NumPy version of sin and cos, since the Sage functions don't know what to do with NumPy arrays. The syntax for plotting with matplotlib is as follows:
plt.figure() plt.plot(x_values, y_values)
[ 156 ]
Chapter 6
The first line creates an empty figure. The optional argument figsize=(x_size, y_ size) sets the figure size in inches. The second line plots the data points that are specified in the arrays or lists. By default, the points are connected with straight line segments. If you specify points that are too far apart, you will see the line segments instead of a smooth curve. The options for the plt.plot function are very similar to the options for the Sage plot function. The following code labels the x and y axes, and places a title above the figure:
plt.xlabel('$x$') plt.ylabel('$f(x)$') plt.title('Plotting with matplotlib') plt.legend(loc='lower right')
The legend method places a legend on the figure. You can use TeX to format any text on the plot by wrapping the text in dollar signs. The following code actually displays the plot:
plt.savefig('demo1.png')
plt.close()
If you are running this example from the notebook interface, the graphic will automatically appear in the notebook. If you're running it in the interactive shell, you won't see any output. The savefig function will save an image file in your home directory. To specify a different path for saving files, pass a full or relative path in addition to the file name. For example:
sage: plt.savefig('Sage for Beginners/Chapter 6/Images/polar_plot.png')
matplotlib can save figures in a variety of image formats, such as png, pdf, ps (PostScript), eps (encapsulated PostScript), and svg (scalable vector graphics). It will automatically determine the correct format from the extension of the file name. You can also use the format keyword to specify the image format. PNG is a raster format, which is compatible with Microsoft Office and OpenOffice. For publications, it is best to use a vector format like PostScript, EPS, or PDF. The SVG format is used for displaying vector graphics on the Web.
Usingmatplotlibto"tweak"aSageplot
Every Sage plot is actually an encapsulated matplotlib figure. We can get the underlying figure and modify it.
[ 157 ]
Plotting with Sage
Timeforaction–gettingthematplotlibfigureobject
Let's say you've made a plot with Sage, but you want to fix one or two formatting details, and Sage doesn't give you enough control. In this example, we'll use the object-oriented interface of matplotlib:
# Create a Sage plot, as shown in the first example
p1 = plot(sin, (-2*pi, 2*pi), thickness=2.0, rgbcolor=(0.5,1,0))
p2 = plot(cos, (-2*pi, 2*pi), thickness=3.0, color='purple', alpha=0.5)
plt = p1 + p2
# Get the Matplotlib object fig = plt.matplotlib()
from matplotlib.backends.backend_agg import FigureCanvasAgg
fig.set_canvas(FigureCanvasAgg(fig)) |
# this line is critical |
|
ax = fig.gca() |
# get current axes |
|
#Add a legend and plot title ax.legend(['sin(x)', 'cos(x)']) ax.set_title('Modified with matplotlib')
#Add a y axis label in a custom location ymin, ymax = ax.get_ylim() ax.set_ylim(ymin, ymax*1.2) ax.set_ylabel('$f(x)$', y=ymax*0.9)
#Fancy annotation of a point of interest x_value = numerical_approx(-3*pi/4) y_value = numerical_approx(cos(-3*pi/4)) ax.annotate('Point', xy=(x_value, y_value),
xytext=(-6, -0.5), color='red', arrowprops=dict(arrowstyle="->", connectionstyle="angle3"))
#Show the matplotlib figure
fig.savefig('Sage_to_matplotlib.png')
[ 158 ]
Chapter 6
The output should look like this:
What just happened?
We modified a plot with matplotlib. First, we used Sage plotting commands to create a Sage graphics object. We then used the matplotlib method of the Sage graphics object to obtain the underlying matplotlib figure object. The rest of this example uses matplotlib functions, rather than Sage functions, to modify the figure. It is critical to use the following line of code:
fig.set_canvas(FigureCanvasAgg(fig))
Thecanvasisa"backend,"whichcontrolshowmatplotliboutputsthegraphicsitcreates.Once wesettherightcanvas,weusedthegca methodofthefigureobjecttogetanobjectthat representsthecurrentaxes.Wethenusedmethodsoftheaxesobjecttomodifytheplot.
Timeforaction–improvingpolarplots
In a previous example, we made polar pots with the Sage function polar_plot. However, polar plots in matplotlib look nicer because they are plotted on special axes. Let's use matplotlib to make these plots again. This example uses the Pyplot interface to matplotlib:
import numpy
import matplotlib.pyplot as plt
# Repeat the antenna pattern example with the Pyplot interface N = float(7)
theta = numpy.arange(0, numpy.pi, numpy.pi/100)
[ 159 ]
Plotting with Sage
def normalized_element_pattern(theta): return abs(numpy.sin(theta))
def array_factor(theta, N):
return abs(float(1 / N) * numpy.sin(float(N * pi / 2) * numpy.cos(theta))
/ numpy.sin(float(pi / 2) * numpy.cos(theta)))
plt.figure(figsize=(6, 4)) plt.subplot(121, polar=True)
plt.polar(theta, normalized_element_pattern(theta)) plt.title('Element factor')
plt.subplot(122, polar=True)
plt.polar(theta, array_factor(theta, N), color='red', label="Array factor")
plt.polar(theta, array_factor(theta, N) * normalized_element_pattern(theta), label="Pattern", color='blue')
plt.legend(loc='lower right', bbox_to_anchor = (1, 0))
plt.subplots_adjust(wspace=0.3) plt.savefig('Polar_plot.png') plt.close()
[ 160 ]
Chapter 6
What just happened?
We made polar plots on nicely formatted polar axes. We introduced a new Pyplot function called subplot, which creates multiple axes on a single figure, arranged as a grid. Subplot accepts three integer arguments that specify the arrangement of axes and chooses one of the axes as the current axes object. The first integer specifies the number of columns in the grid, the second integer specifies the number of rows in the grid, and the third integer selects the current axes object. The first subplot, which is number one, is located in the upper-left corner of the figure. Subplot numbers increase from left to right across a row, and
from top to bottom. It is possible, although not recommended, to omit the commas between arguments and pass a single three-digit integer value to subplot. This rather unusual syntax was chosen for compatibility with the subplot function in MATLAB. We used the polar keyword to choose polar axes for each of the subplots.
We also introduced a function called subplots_adjust, which we used to increase the amount of horizontal space between the plots. This command can be used to adjust the amount of space around and between subplots, using the following keyword arguments:
Keyword |
Default |
Meaning |
left |
0.125 |
Space to the left of the subplots |
right |
0.9 |
Space to the right of the subplots |
bottom |
0.1 |
Space below the subplots |
top |
0.9 |
Space above the subplots |
wspace |
0.2 |
Space between columns of subplots |
hspace |
0.2 |
Space between rows of subplots |
Plottingdatawithmatplotlib
Because matplotlib plots arrays of points, it is well suited to working with data. You can make many kinds of charts and publication-quality graphics with matplotlib.
Timeforaction–makingabarchart
Bar charts are often used to present experimental data in scientific papers. Let's make a chart with bars that represent the average value of some experimental data and add error bars to represent the standard deviation:
import numpy
import matplotlib.pyplot as plt
# Define experimental data cluster1_data = numpy.array([9.7, 3.2])
[ 161 ]
Plotting with Sage
cluster1_x = numpy.array([2,4]) cluster1_error = numpy.array([1.3, 0.52])
cluster2_data = numpy.array([6.8, 7.3]) cluster2_x = numpy.array([8,10]) cluster2_error = numpy.array([0.72, 0.97])
# Join data arrays for plotting
data = numpy.concatenate([cluster1_data, cluster2_data]) bar_centers = numpy.concatenate([cluster1_x, cluster2_x]) errors = numpy.concatenate([cluster1_error, cluster2_error])
# Plot
fig = plt.figure(figsize=(5,4)) # size in inches plt.bar(bar_centers, data, yerr=errors,
width=2.0, align='center', color='white', ecolor='black') plt.ylabel('outcome')
plt.text(4, 4, '*', fontsize=14)
# Label ticks on x axis axes = fig.gca() axes.set_xticks(bar_centers)
axes.set_xticklabels(['trial 1', 'trial 2', 'trial 3', 'trial 4'])
plt.savefig('Bar_Chart.png') plt.close()
The output should look like this:
[ 162 ]
Chapter 6
What just happened?
We created a publication-quality bar chart using the bar function from matplotlib. This function has two mandatory arguments. The first argument sets the horizontal location of each bar, and the second sets the height of each bar. Here is a summary of the optional arguments we used to customize the appearance of the plot:
Keyword |
Description |
yerr |
Sets the size of the error bars on top of each bar. |
width |
Width of each bar. |
align |
Determines how the first argument is interpreted. If set to 'center', |
|
the array sets the location of the centre of each bar; otherwise, it sets the |
|
location of the left edge. |
color |
The color of the fill inside the bars. |
ecolor |
The color of the edge (outline) of the bars. |
There are other optional arguments, which are described in the matplotlib documentation.
We used the text function to add text to the plot—in this case, an asterisk to indicate that one of the bars is statistically distinct from the others. The text function requires the x and y coordinates of the text, and a string containing the text to be displayed. The fontsize keyword allowed us to change the size of the text. In order to customize the tick labels on the x axis, we used the gca method of the figure object to get its axes object. We then passed an array of bar centres to the set_xticks method so that ticks will only be displayed at the centre of each bar. We used the set_xticklabels method to label the ticks with strings instead of numbers.
Timeforaction–makingapiechart
matplotlib can also make business graphics that are more commonly associated with spreadsheets. Let's try a pie chart:
import numpy
import matplotlib.pyplot as plt
data = [1.0, 10.0, 20.0, 30.0, 40.0] explode = numpy.zeros(len(data)) explode[3] = 0.1
plt.figure(figsize=(4, 4))
plt.pie(data, explode=explode, labels=['a', 'b', 'c', 'd', 'e']) plt.title('Revenue sources')
plt.savefig('Pie_chart.png') plt.close()
[ 163 ]
Plotting with Sage
The plot should look like this:
What just happened?
We made a pie chart. The pie function in matplotlib only requires one argument, which is a list of numbers that indicate the relative size of the slices. The explode option takes a list of numbers that show how far to offset each piece of the pie from the centre. In this case, we created an array of zeros and set the fourth element to 0.1, which offset the fourth slice of the pie. We used the keyword explode to pass this array to the pie function. We used the labels keyword to pass a list of strings to be used as labels for each slice.
Timeforaction–plottingahistogram
matplotlib has a built-in function for making histograms, which are used to visualize the distribution of values in a set of data. In this example, we will generate an array of random numbers that are drawn from a Gaussian distribution:
import numpy
import matplotlib.pyplot as plt
data = numpy.random.normal(0, 1, size=1000)
plt.figure(figsize=(4, 4))
plt.hist(data, normed=True, facecolor=(0.9, 0.9, 0.9))
plt.savefig('Histogram.png')
plt.close()
[ 164 ]
Chapter 6
The result should be similar to the following plot. Because we are generating random data, your plot will not look exactly like this one:
What just happened?
We used the hist function to visualize the distribution of values in an array of pseudorandom numbers. hist requires one argument, which is an array containing the data. In practice, the data would typically consist of experimental measurements, sensor data, or the results of a Monte Carlo simulation. We used the optional argument normed=True to indicate that the histogram should be normalized, which means that its integral is one. The facecolor keyword was used to specify the fill color of the bars as a tuple of R, G, B values.
Plottinginthreedimensions
Sage can make 3D plots for visualizing functions of two variables, as well as parametric plots that create three-dimensional surfaces. It also has a variety of tools for making twodimensional representations of three-dimensional surfaces.
[ 165 ]
Plotting with Sage
Timeforaction–makeaninteractive3Dplot
Let's make an interactive 3D plot.
var('x, y')
p3d = plot3d(y^2 + 1 - x^3 - x, (x, -pi, pi), (y, -pi, pi)) p3d.show()
Ifyourunthisexampleinthenotebookinterface,aJavaappletcalledJmolwillruninthe cellbelowthecode.Ifyourunitfromtheinteractiveshell,Jmolwilllaunchasastand-alone application.Clickinganddraggingonthefigurewiththeleftmousebuttonwillrotatetheplot in3Dspace.Clickinganddraggingwiththecentrebutton,ormovingthescrollwheel,zooms inandout.Right-clickingbringsupamenuthatallowsyoutosetvariousoptionsforJmol. SinceJmolisalsousedtovisualizethe3Dstructuresofmolecules,someoftheoptionsarenot relevantforplottingfunctions.Hereisascreenshotofthefunction,plottedwithJmol:
What just happened?
We made a cool 3D plot that allowed us to explore a function of two variables. When running Jmol as an applet in a worksheet, you can click on the "Get Image" link below the plot to save an image of the plot in its current state. However, the image quality is not particularly high because it is saved in JPEG format. When Jmol is called from the command line, it runs as a stand-alone application, and more options are available. You can save files in JPEG, GIF, PPM, PNG, or PDF format. Note that the PDF format is a bitmap embedded in a PDF file, rather than a true vector representation of the surface. The syntax for using plot3d is very simple:
plot3d(f(x,y), (x, x_min, x_max), (y, y_min, y_max))
[ 166 ]
Chapter 6
There are a few optional arguments to the show method that you can use to alter the appearance of the plot. Setting mesh=True plots a mesh on the surface, and setting dots=True plots a small sphere at each point. You can also use the transformation keyword argument to apply a transformation to the data—see the plot3d documentation for more information.
Higherqualityoutput
We can improve the quality of saved images using ray tracing, which is an algorithm for generating images that is based on optical principles. Sage comes with ray tracing software called Tachyon, which can be used to view 3D plots. To activate Tachyon, use the show method with the viewer keyword as shown below:
p3d.show(viewer='tachyon', frame=False, axes=True)
Depending on the speed of your computer, the ray tracing may require a few seconds to a few minutes.
The frame keyword selects whether or not to draw a box around the outer limits of the plot, while the axes keyword determines whether or not the axes are drawn.
[ 167 ]
Plotting with Sage
Parametric3Dplotting
Sage can also plot functions of two variables that are defined in terms of a parameter. You can make very complex surfaces in this way.
Timeforaction–parametricplotsin3D
We will plot two interlocking rings to demonstrate how complex surfaces are easily plotted using three functions of two parameters:
var('u, v')
f1 = (4 + (3 + cos(v)) * sin(u), 4 + (3 + cos(v)) * cos(u), 4 + sin(v))
f2 = (8 + (3 + cos(v)) * cos(u), 3 + sin(v), 4 + (3 + cos(v)) * sin(u))
p1 = parametric_plot3d(f1, (u, 0, 2 * pi), (v, 0, 2 * pi), texture="red")
p2 = parametric_plot3d(f2, (u, 0, 2 * pi), (v, 0, 2 * pi), texture="blue")
combination = p1 + p2 combination.show()
The result should look like this:
[ 168 ]
Chapter 6
What just happened?
We made a very complex 3D shape using the parametric_plot3d function. The optional arguments for this function are the same as the options for the plot3d function.
Contourplots
Sage can also make contour plots, which are 2-D representations of 3-D surfaces. While 3D plots are eye-catching, a 2D plot can be a more practical way to convey information about the function or data set.
Timeforaction–makingsomecontourplots
The following code will demonstrate four different ways to make a 2D plot of a 3D surface with Sage:
var('x, y')
text_coords = (2, -3.5)
cp = contour_plot(y^2 + 1 - x^3 - x, (x, -3, 3), (y, -3, 3), contours=8, linewidths=srange(0.5, 4.0, 0.5), fill=False, labels=True, label_colors='black', cmap='gray', colorbar=False)
cp += text("Contour", text_coords)
ip = implicit_plot(y^2 + 1 - x^3 - x, (x, -3, 3), (y, -3, 3)) ip += text("Implicit", text_coords)
rp = region_plot(y^2 + 1 - x^3 - x < 0, (x, -3, 3), (y, -3, 3), incol=(0.8, 0.8, 0.8)) # color is an (R,G,B) tuple
rp += text("Region", text_coords)
dp = density_plot(y^2 + 1 - x^3 - x, (x, -3, 3), (y, -3, 3)) dp += text("Density", text_coords)
show(graphics_array([cp, ip, rp, dp], 2, 2), aspect_ratio=1, figsize=(6, 6))
[ 169 ]
Plotting with Sage
The output should be as follows:
What just happened?
The plots we made demonstrate four different ways of visualizing the function we plotted in the previous example. All four functions follow the same syntax as plot3d:
contour_plot(f(x,y), (x, x_min, x_max), (y, y_min, y_max))
[ 170 ]
