# Mathematical Plotting with Matplotlib

Plotting is a fundamental tool in all of mathematics. A good plot can reveal hidden details, suggest future directions, verify results, or reinforce an argument. It is no surprise, then, that the scientific Python stack features a powerful and flexible plotting library called Matplotlib.

In this chapter, we will plot functions and data in a variety of styles and create figures that are fully labeled and annotated. We will create three-dimensional plots, customize the appearance of figures, create figures that contain multiple plots using subplots, and save figures directly to files for applications that are not running in an interactive environment.

Plotting is one of the most important aspects covered in this book. Plotting data, functions, or solutions can often help you gain an understanding of a problem that can really help to reason about your methods. We will see plotting again in every chapter of this book.

In this chapter, we will cover the following recipes:

- Basic plotting with Matplotlib
- Adding subplots
- Plotting with error bars
- Saving Matplotlib figures
- Surface and contour plots
- Customizing three-dimensional plots
- Plotting vector fields with quiver plots

# Technical requirements

The main plotting package for Python is Matplotlib, which can be installed using your favorite package manager, such as `pip`

:

python3.10 -m pip install matplotlib

This will install the most recent version of Matplotlib, which, at the time of writing this book, is version 3.5.2.

Matplotlib contains numerous sub-packages, but the main **user interface** (**UI**) is the `matplotlib.pyplot`

package, which, by convention, is imported under the `plt`

alias. This is achieved using the following `import`

statement:

import matplotlib.pyplot as plt

Many of the recipes in this chapter also require NumPy, which, as usual, is imported under the `np`

alias.

The code for this chapter can be found in the `Chapter 02`

folder of the GitHub repository at https://github.com/PacktPublishing/Applying-Math-with-Python-2nd-Edition/tree/main/Chapter%2002.

# Basic plotting with Matplotlib

Plotting is an important part of understanding behavior. So much can be learned by simply plotting a function or data that would otherwise be hidden. In this recipe, we will walk through how to plot simple functions or data using Matplotlib, set the plotting style, and add labels to a plot.

Matplotlib is a very powerful plotting library, which means it can be rather intimidating to perform simple tasks with it. For users who are used to working with MATLAB and other mathematical software packages, there is a state-based interface called `pyplot`

. There is also an **object-oriented interface** (**OOI**), which might be more appropriate for more complex plots. In either case, the `pyplot`

interface is a convenient way to create basic objects.

## Getting ready

Most commonly, the data that you wish to plot will be stored in two separate NumPy arrays, which we will label `x`

and `y`

for clarity (although this naming does not matter in practice). We will demonstrate plotting the graph of a function, so we will generate an array of `x`

values and use the function to generate the corresponding `y`

values. We’re going to plot three different functions over the range on the same axes:

def f(x): return x*(x - 2)*np.exp(3 – x) def g(x): return x**2 def h(x): return 1 - x

Let’s plot these three functions in Python using Matplotlib.

## How to do it...

Before we can plot the function, we must generate `x`

and `y`

data to be plotted. If you are plotting existing data, you can skip these commands. We need to create a set of `x`

values that cover the desired range, and then use the function to create `y`

values:

- The
`linspace`

routine from NumPy is ideal for creating arrays of numbers for plotting. By default, it will create 50 equally spaced points between the specified arguments. The number of points can be customized by providing an additional argument, but 50 is sufficient for most cases:x = np.linspace(-0.5, 3.0) # 50 values between -0.5 and 3.0

- Once we have created
`x`

values, we can generate`y`

values:y1 = f(x) # evaluate f on the x points

y2 = g(x) # evaluate g on the x points

y3 = h(x) # evaluate h on the x points

- To plot the data, we first need to create a new figure and attach axes objects, which can be achieved by calling the
`plt.subplots`

routine without any arguments:fig, ax = plt.subplots()

Now, we use the `plot`

method on the `ax`

object to plot the first function. The first two arguments are and coordinates to be plotted, and the third (optional) argument specifies that the line color should be black:

ax.plot(x, y1, "k") # black solid line style

To help distinguish the plots for the other functions, we plot those with a dashed line and a dot-dash line:

ax.plot(x, y2, "k--") # black dashed line style ax.plot(x, y3, "k.-") # black dot-dashed line style

Every plot should have a title and axis labels. In this case, there isn’t anything interesting to label the axes with, so we just label them `"x"`

and `"y"`

:

ax.set_title("Plot of the functions f, g, and h") ax.set_xlabel("x") ax.set_ylabel("y")

Let’s also add a legend to help you distinguish between the different function plots without having to look elsewhere to see which line is which:

ax.legend(["f", "g", "h"])

Finally, let’s annotate the plot to mark the intersection between the functions and with text:

ax.text(0.4, 2.0, "Intersection")

This will plot the `y`

values against the `x`

values on a new figure. If you are working within IPython or with a Jupyter notebook, then the plot should automatically appear at this point; otherwise, you might need to call the `plt.show`

function to make the plot appear:

plt.show()

If you use `plt.show`

, the figure should appear in a new window. We won’t add this command to any further recipes in this chapter, but you should be aware that you will need to use it if you are not working in an environment where plots will be rendered automatically, such as an IPython console or a Jupyter Notebook. The resulting plot should look something like the plot in *Figure 2**.1*:

Figure 2.1 – Three functions on a single set of axes, each with a different style, with labels, legend, and an annotation

Note

If you are using a Jupyter notebook and the `subplots`

command, you must include the call to `subplots`

within the same cell as the plotting commands or the figure will not be produced.

## How it works…

Here, we’re using the OOI because it allows us to keep track of exactly which figure and axes object we’re plotting on. This isn’t so important here where we have only a single `figure`

and `axes`

, but one can easily envisage situations where you might have two or more figures and axes concurrently. Another reason to follow this pattern is to be consistent when you add multiple subplots—see the *Adding **subplots* recipe.

You can produce the same plot as in the recipe via the state-based interface by using the following sequence of commands:

plt.plot(x, y1, "k", x, y2, "k--", x, y3, "k.-") plt.title("Plot of the functions f, g, and h") plt.xlabel("x") plt.ylabel("y") plt.legend(["f", "g", "h"]) plt.text(0.4, 2.0, "Intersection")

If there are currently no `Figure`

or `Axes`

objects, the `plt.plot`

routine creates a new `Figure`

object, adds a new `Axes`

object to the figure, and populates this `Axes`

object with the plotted data. A list of handles to the plotted lines is returned. Each of these handles is a `Lines2D`

object. In this case, this list will contain a single `Lines2D`

object. We could use this `Lines2D`

object to further customize the appearance of the line later.

Notice that in the preceding code, we combined all the calls to the `plot`

routine together. This is also possible if you use the OOI; the state-based interface is passing the arguments to the axes method on the set of axes that it either retrieves or creates.

The object layer of Matplotlib interacts with a lower-level *backend*, which does the heavy lifting of producing the graphical plot. The `plt.show`

function issues an instruction to the backend to render the current figure. There are a number of backends that can be used with Matplotlib, which can be customized by setting the `MPLBACKEND`

environment variable, modifying the `matplotlibrc`

file, or by calling `matplotlib.use`

from within Python with the name of an alternative backend. By default, Matplotlib picks a backend that is appropriate for the platform (Windows, macOS, Linux) and purpose (interactive or non-interactive), based on which backends are available. For example, on the author’s system, the `QtAgg`

backend is the default. This is an interactive backend based on the **Anti-Grain Geometry** (**AGG**) library. Alternatively, one might want to use the `QtCairo`

backend, which uses the Cairo library for rendering.

Note

The `plt.show`

function does more than simply call the `show`

method on a figure. It also hooks into an event loop to correctly display the figure. The `plt.show`

routine should be used to display a figure, rather than the `show`

method on a `Figure`

object.

The **format string** used to quickly specify the line style has three optional parts, each consisting of one or more characters. The first part controls the marker style, which is the symbol that is printed at each data point; the second controls the style of the line that connects the data points; the third controls the color of the plot. In this recipe, we only specified the line style. However, one could specify both line style and marker style or just marker style. If you only provide the marker style, no connecting lines are drawn between the points. This is useful for plotting discrete data where no interpolation between points is necessary.

Four line-style parameters are available: a solid line (`-`

), a dashed line (`--`

), a dash-dot line (`-.`

), or a dotted line (`:`

). Only a limited number of colors can be specified in the format string; they are red, green, blue, cyan, yellow, magenta, black, and white. The character used in the format string is the first letter of each color (with the exception of black), so the corresponding characters are `r`

, `g`

, `b`

, `c`

, `y`

, `m`

, `k`

, and `w`

, respectively.

In the recipe, we saw three examples of these format strings: the single `k`

format string only changed the color of the line and kept the other settings at default (small point markers and unbroken blue line); the `k--`

and `k.-`

format strings both changed the color and the line style. For an example of changing the point style, see the *There’s more...* section and *Figure 2**.2*:

Figure 2.2 - Plot of three sets of data, each plotted using a different marker style

The `set_title`

, `set_xlabel`

, and `set_ylabel`

methods simply add the text argument to the corresponding position of the `Axes`

object. The `legend`

method, as called in the preceding code, adds the labels to the datasets in the order that they were added to the plot—in this case, `y1`

, `y2`

, and then `y3`

.

There are a number of keyword arguments that can be supplied to the `set_title`

, `set_xlabel`

, and `set_ylabel`

routines to control the style of the text. For example, the `fontsize`

keyword can be used to specify the size of the label font in the usual `pt`

point measure.

The `annotate`

method on the `Axes`

object adds arbitrary text to a specific position on the plot. This routine takes two arguments—the text to display as a string and the coordinates of the point at which the annotation should be placed. This routine also accepts keyword arguments that can be used to customize the style of the annotation.

## There’s more…

The `plt.plot`

routine accepts a variable number of positional inputs. In the preceding code, we supplied two positional arguments that were interpreted as `x`

values and `y`

values (in that order). If we had instead provided only a single array, the `plot`

routine would have plotted the values against their position in the array; that is, the `x`

values are taken to be `0`

, `1`

, `2`

, and so on.

The `plot`

method also accepts a number of keyword arguments that can also be used to control the style of a plot. Keyword arguments take precedence over format string parameters if both are present, and they apply to all sets of data plotted by the call. The keyword to control the marker style is `marker`

, the keyword for the line style is `linestyle`

, and the keyword for color is `color`

. The `color`

keyword argument accepts a number of different formats to specify a color, which includes RGB values as a `(r, g, b)`

tuple, where each character is a float between `0`

and `1`

or is a hex string. The width of the line plotted can be controlled using the `linewidth`

keyword, which should be provided with a `float`

value. Many other keyword arguments can be passed to `plot`

; a list is given in the Matplotlib documentation. Many of these keyword arguments have a shorter version, such as `c`

for `color`

and `lw`

for `linewidth`

.

In the this recipe, we plotted a large number of coordinates generated by evaluating functions on a selection of values. In other applications, one might have data sampled from the real world (as opposed to generated). In these situations, it might be better to leave out the connecting lines and simply plot the markers at the points. Here is an example of how this might be done:

y1 = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) y2 = np.array([1.2, 1.6, 3.1, 4.2, 4.8]) y3 = np.array([3.2, 1.1, 2.0, 4.9, 2.5]) fig, ax = plt.subplots() ax.plot(y1, 'o', y2, 'x', y3, '*', color="k")

The result of these commands is shown in *Figure 2**.2*. Matplotlib has a specialized method for producing scatter plots such as this, called `scatter`

.

Other aspects of the plot can be customized by using methods on the `Axes`

object. The axes ticks can be modified using the `set_xticks`

and `set_yticks`

methods on the `Axes`

object, and the grid appearance can be configured using the `grid`

method. There are also convenient methods in the `pyplot`

interface that apply these modifications to the current axes (if they exist).

For example, we modify the axis limits, set the ticks at every multiple of `0.5`

in both the and direction, and add a grid to the plot by using the following commands:

ax.axis([-0.5, 5.5, 0, 5.5]) # set axes ax.set_xticks([0.5*i for i in range(9)]) # set xticks ax.set_yticks([0.5*i for i in range(11)]) # set yticks ax.grid() # add a grid

Notice how we set the limits slightly larger than the extent of the plot. This is to avoid markers being placed on the boundary of the plot window.

Matplotlib has many other plotting routines besides the `plot`

routine described here. For example, there are plotting methods that use a different scale for the axes, including the logarithmic or axes separately (`semilogx`

or `semilogy`

, respectively) or together (`loglog`

). These are explained in the Matplotlib documentation. The `scatter`

plotting routine may be useful if you wish to plot discrete data on axes without connecting the points with a line. This allows more control over the style of the marker. For example, you can scale the marker according to some additional information.

We can use a different font by using the `fontfamily`

keyword, the value of which can be the name of a font or `serif`

, `sans-serif`

, or `monospace`

, which will choose the appropriate built-in font. A complete list of modifiers can be found in the Matplotlib documentation for the `matplotlib.text.Text`

class.

Text arguments can also be rendered using TeX for additional formatting by supplying `usetex=True`

to the routine. We’ll demonstrate the use of TeX formatting of labels in *Figure 2**.3* in the following recipe. This is especially useful if the title or axis label contains a mathematical formula. Unfortunately, the `usetex`

keyword argument cannot be used if TeX is not installed on the system—it will cause an error in this case. However, it is still possible to use the TeX syntax for formatting mathematical text within labels, but this will be typeset by Matplotlib, rather than by TeX.

# Adding subplots

Occasionally, it is useful to place multiple related plots within the same figure side by side but not on the same axes. Subplots allow us to produce a grid of individual plots within a single figure. In this recipe, we will see how to create two plots side by side on a single figure using subplots.

## Getting ready

You will need the data to be plotted on each subplot. As an example, we will plot the first five iterates of Newton’s method applied to the function with an initial value of on the first subplot, and for the second, we will plot the error of the iterate. We first define a generator function to get the iterates:

def generate_newton_iters(x0, number): iterates = [x0] errors = [abs(x0 - 1.)] for _ in range(number): x0 = x0 - (x0*x0 - 1.)/(2*x0) iterates.append(x0) errors.append(abs(x0 - 1.)) return iterates, errors

This routine generates two lists. The first list contains iterates of Newton’s method applied to the function, and the second contains the error in the approximation:

iterates, errors = generate_newton_iters(2.0, 5)

## How to do it...

The following steps show how to create a figure that contains multiple subplots:

- We use the
`subplots`

routine to create a new figure and references to all of the`Axes`

objects in each subplot, arranged in a grid with one row and two columns. We also set the`tight_layout`

keyword argument to`True`

to fix the layout of the resulting plots. This isn’t strictly necessary, but it is in this case as it produces a better result than the default:fig, (ax1, ax2) = plt.subplots(1, 2,

tight_layout=True)

#1 row, 2 columns

- Once
`Figure`

and`Axes`

objects are created, we can populate the figure by calling the relevant plotting method on each`Axes`

object. For the first plot (displayed on the left), we use the`plot`

method on the`ax1`

object, which has the same signature as the standard`plt.plot`

routine. We can then call the`set_title`

,`set_xlabel`

, and`set_ylabel`

methods on`ax1`

to set the title and the`x`

and`y`

labels. We also use TeX formatting for the axes labels by providing the`usetex`

keyword argument; you can ignore this if you don’t have TeX installed on your system:ax1.plot(iterates, "kx")

ax1.set_title("Iterates")

ax1.set_xlabel("$i$", usetex=True)

ax1.set_ylabel("$x_i$", usetex=True)

- Now, we can plot the error values on the second plot (displayed on the right) using the
`ax2`

object. We use an alternative plotting method that uses a logarithmic scale on the axis, called`semilogy`

. The signature for this method is the same as the standard`plot`

method. Again, we set the axes labels and the title. Again, the use of`usetex`

can be left out if you don’t have TeX installed:ax2.semilogy(errors, "kx") # plot y on logarithmic scale

ax2.set_title("Error")

ax2.set_xlabel("$i$", usetex=True)

ax2.set_ylabel("Error")

The result of this sequence of commands is shown here:

Figure 2.3 - Multiple subplots on the same Matplotlib figure

The left-hand side plots the first five iterates of Newton’s method, and the right-hand side is the approximation error plotted on a logarithmic scale.

## How it works...

A `Figure`

object in Matplotlib is simply a container for plot elements, such as `Axes`

, of a certain size. A `Figure`

object will usually only hold a single `Axes`

object, which occupies the entire figure area, but it can contain any number of `Axes`

objects in the same area. The `subplots`

routine does several things. It first creates a new figure and then creates a grid with the specified shape in the figure area. Then, a new `Axes`

object is added to each position of the grid. The new `Figure`

object and one or more `Axes`

objects are then returned to the user. If a single subplot is requested (one row and one column, with no arguments) then a plain `Axes`

object is returned. If a single row or column is requested (with more than one column or row, respectively), then a list of `Axes`

objects is returned. If more than one row and column are requested, a list of lists, with rows represented by inner lists filled with `Axes`

objects, will be returned. We can then use the plotting methods on each of the `Axes`

objects to populate the figure with the desired plots.

In this recipe, we used the standard `plot`

method for the left-hand side plot, as we have seen in previous recipes. However, for the right-hand side plot, we used a plot where the axis had been changed to a logarithmic scale. This means that each unit on the axis represents a change of a power of 10 rather than a change of one unit so that `0`

represents , `1`

represents 10, `2`

represents 100, and so on. The axes labels are automatically changed to reflect this change in scale. This type of scaling is useful when the values change by an order of magnitude, such as the error in an approximation, as we use more and more iterations. We can also plot with a logarithmic scale for only by using the `semilogx`

method, or both axes on a logarithmic scale by using the `loglog`

method.

## There’s more...

There are several ways to create subplots in Matplotlib. If you have already created a `Figure`

object, then subplots can be added using the `add_subplot`

method of the `Figure`

object. Alternatively, you can use the `subplot`

routine from `matplotlib.pyplot`

to add subplots to the current figure. If one does not yet exist, it will be created when this routine is called. The `subplot`

routine is a convenience wrapper of the `add_subplot`

method on the `Figure`

object.

In the preceding example, we created two plots with differently scaled axes. This demonstrates one of the many possible uses of subplots. Another common use is for plotting data in a matrix where columns have a common `x`

label and rows have a common `y`

label, which is especially common in multivariate statistics when investigating the correlation between various sets of data. The `plt.subplots`

routine for creating subplots accepts the `sharex`

and `sharey`

keyword parameters, which allows the axes to be shared among all subplots or among a row or column. This setting affects the scale and ticks of the axes.

## See also

Matplotlib supports more advanced layouts by providing the `gridspec_kw`

keyword arguments to the `subplots`

routine. See the documentation for `matplotlib.gridspec`

for more information.

# Plotting with error bars

It is quite common that the values that we gather from the real world carry some uncertainty; no measurement of a real-world quantity is perfectly accurate. For example, if we measure a distance with a tape measure, there is a certain amount of accuracy that we can assume in our results, but beyond this accuracy, we cannot be sure that our measurement is valid. For such a situation, we can probably be confident of our accuracy up to about 1 millimeter or a little less than 1/16 inch. (This is, of course, assuming that we are measuring perfectly.) These values are the smallest subdivisions on typical tape measures. Let’s assume that we have collected such a set of 10 measurements (in centimeters) and we wish to plot these values along with the accuracy that we are confident about. (The range of values that lie above or below the measurement by the accuracy amount is called the *error*.) This is what we address in this recipe.

## Getting ready

As usual, we have the Matplotlib `pyplot`

interface imported under the alias `plt`

. We first need to generate our hypothetical data and the assumed accuracy in NumPy arrays:

measurement_id = np.arange(1, 11) measurements = np.array([2.3, 1.9, 4.4, 1.5, 3.0, 3.3, 2.9, 2.6, 4.1, 3.6]) # cm err = np.array([0.1]*10) # 1mm

Let’s see how to use plotting routines in Matplotlib to plot these measurements with error bars to indicate the uncertainty in each measurement.

## How to do it…

The following steps show how to plot measurements along with accuracy information on a figure.

First, we need to generate a new `figure`

and `axis`

object as usual:

fig, ax = plt.subplots()

Next, we use the `errorbar`

method on the axis object to plot the data along with the error bars. The accuracy information (the error) is passed as the `yerr`

argument:

ax.errorbar(measurement_id, measurements, yerr=err, fmt="kx", capsize=2.0)

As usual, we should always add meaningful labels to the axes and a title to the plot:

ax.set_title("Plot of measurements and their estimated error") ax.set_xlabel("Measurement ID") ax.set_ylabel("Measurement(cm)")

Since Matplotlib will not produce `xlabel`

ticks at every value by default, we set the x-tick values to the measurement IDs so that they are all displayed on the plot:

ax.set_xticks(measurement_id)

The resulting plot is shown in *Figure 2**.4*. The recorded value is shown at the `x`

markers, and the error bar extends above and below that value by an accuracy of 0.1 cm (1 mm):

Figure 2.4 - Plot of a set of 10 sample measurements (in centimeters) with their measurement error shown

We can see here that each of the markers has a vertical bar that indicates the range in which we expect the true measurement (-value) to lie.

## How it works…

The `errorbar`

method works in a similar way to other plotting methods. The first two arguments are the and coordinates of the points to be plotted. (Note that both must be provided, which is not the case for other plotting methods.) The `yerr`

argument indicates the size of the error bars to be added to the plot and should all be positive values. The form of the value(s) passed to this argument determines the nature of the error bars. In the recipe, we provided a flat NumPy array with 10 entries—one for each measurement—which leads to error bars above and below each point with the same size (the corresponding value from the argument). Alternatively, we could have specified a 2-by-10 array, where the first row contains the lower error and the second row contains the upper error. (Since all our errors are the same, we could also have provided a single float containing the common error for all measurements.)

In addition to the data arguments, there are the usual format arguments, including the `fmt`

format string. (We used this here as a keyword argument because we named the `yerr`

argument that precedes it.) In addition to the formatting of lines and points found in other plotting methods, there are special arguments for customizing the look of error bars. In the recipe, we used the `capsize`

argument to add “caps” to either end of the error bars so that we could easily identify the ends of those bars; the default style is a simple line.

## There’s more...

In the recipe, we only plotted errors in the axis because the values were simply ID values. If both sets of values have uncertainty, you can also specify the error values using the `xerr`

argument. This argument functions in the same way as the `yerr`

argument used previously.

If you are plotting a very large number of points that follow some kind of trend, you might wish to plot error bars more selectively. For this, you can use the `errorevery`

keyword argument to instruct Matplotlib to add error bars at every `n`

th data point rather than at all of them. This can be either a positive integer—indicating the “stride” to use to select points that will have errors—or a tuple containing an offset from the first value and a stride. For example, `errorevery=(2, 5)`

would place error bars every five data points, starting from the second entry.

You can also add error bars to bar charts in the same way (except here, the `xerr`

and `yerr`

arguments are keywords only). We could have plotted the data from the recipe as a bar chart using the following commands:

ax.bar(measurement_id, measurements, yerr=err, capsize=2.0, alpha=0.4)

If this line is used instead of the call to `errorbar`

in the recipe, then we would get a bar chart, as shown in *Figure 2**.5*:

Figure 2.5 - Bar chart of measurements with error bars

As before, the measurement bar is capped with an indicator of the range in which we expect the true measurement to lie.

# Saving Matplotlib figures

When you work in an interactive environment, such as an IPython console or a Jupyter notebook, displaying a figure at runtime is perfectly normal. However, there are plenty of situations where it would be more appropriate to store a figure directly to a file, rather than rendering it on screen. In this recipe, we will see how to save a figure directly to a file, rather than displaying it on screen.

## Getting ready

You will need the data to be plotted and the path or file object in which you wish to store the output. We store the result in `savingfigs.png`

in the current directory. In this example, we will plot the following data:

x = np.arange(1, 5, 0.1) y = x*x

Let’s see how to plot this curve using Matplotlib and save the resulting plot to a file (without needing to interact with the plot GUI).

## How to do it...

The following steps show how to save a Matplotlib plot directly to a file:

- The first step is to create a figure, as usual, and add any labels, titles, and annotations that are necessary. The figure will be written to the file in its current state, so any changes to the figure should be made before saving:
fig, ax = plt.subplots()

ax.plot(x, y)

ax.set_title("Graph of $y = x^2$", usetex=True)

ax.set_xlabel("$x$", usetex=True)

ax.set_ylabel("$y$", usetex=True)

- Then, we use the
`savefig`

method on`fig`

to save this figure to a file. The only required argument is the path to output to or a file-like object that the figure can be written to. We can adjust various settings for the output format, such as the resolution, by providing the appropriate keyword arguments. We’ll set the**Dots per Inch**(**DPI**) of the output figure to`300`

, which is a reasonable resolution for most applications:fig.savefig("savingfigs.png", dpi=300)

Matplotlib will infer that we wish to save the image in the **Portable Network Graphics** (**PNG**) format from the extension of the file given. Alternatively, a format can be explicitly provided as a keyword argument (by using the `format`

keyword), or it will fall back to the default from the configuration file.

## How it works...

The `savefig`

method chooses the appropriate backend for the output format and then renders the current figure in that format. The resulting image data is written to the specified path or file-like object. If you have manually created a `Figure`

instance, the same effect can be achieved by calling the `savefig`

method on that instance.

## There’s more...

The `savefig`

routine takes a number of additional optional keyword arguments to customize the output image. For example, the resolution of the image can be specified using the `dpi`

keyword. The plots in this chapter have been produced by saving the Matplotlib figures to the file.

The output formats available include PNG, **Scalable Vector Graphics** (**SVG**), **PostScript** (**PS**), **Encapsulated PostScript** (**EPS**), and **Portable Document Format** (**PDF**). You can also save to JPEG format if the Pillow package is installed, but Matplotlib does not support this natively since version 3.1. There are additional customization keyword arguments for JPEG images, such as `quality`

and `optimize`

. A dictionary of image metadata can be passed to the `metadata`

keyword, which will be written as image metadata when saving.

## See also

The examples gallery on the Matplotlib website includes examples of embedding Matplotlib figures into a GUI application using several common Python GUI frameworks.

# Surface and contour plots

Matplotlib can also plot three-dimensional data in a variety of ways. Two common choices for displaying data such as this are using **surface plots** or **contour plots** (think of contour lines on a map). In this recipe, we will see a method for plotting surfaces from three-dimensional data and how to plot contours of three-dimensional data.

## Getting ready

To plot three-dimensional data, it needs to be arranged into two-dimensional arrays for the , , and components, where both the and components must be of the same shape as the component. For the sake of this demonstration, we will plot the surface corresponding to the following function:

For 3D data, we can’t just use the routines from the `pyplot`

interface. We need to import some extra functionality from another part of Matplotlib. We’ll see how to do this next.

## How to do it...

We want to plot the function on the and range. The first task is to create a suitable grid of pairs on which to evaluate this function:

- We first use
`np.linspace`

to generate a reasonable number of points in these ranges:X = np.linspace(-5, 5)

Y = np.linspace(-5, 5)

- Now, we need to create a grid on which to create our values. For this, we use the
`np.meshgrid`

routine:grid_x, grid_y = np.meshgrid(X, Y)

- Now, we can create values to plot, which hold the value of the function at each of the grid points:
z = np.exp(-((grid_x-2.)**2 + (

grid_y-3.)**2)/4) - np.exp(-(

(grid_x+3.)**2 + (grid_y+2.)**2)/3)

- To plot three-dimensional surfaces, we need to load a Matplotlib toolbox,
`mplot3d`

, which comes with the Matplotlib package. This won’t be used explicitly in the code, but behind the scenes, it makes the three-dimensional plotting utilities available to Matplotlib:from mpl_toolkits import mplot3d

- Next, we create a new figure and a set of three-dimensional axes for the figure:
fig = plt.figure()

# declare 3d plot

ax = fig.add_subplot(projection="3d")

- Now, we can call the
`plot_surface`

method on these axes to plot the data (we set the colormap to gray for better visibility in print; see the next recipe for a more detailed discussion):ax.plot_surface(grid_x, grid_y, z, cmap="gray")

- It is extra important to add axis labels to three-dimensional plots because it might not be clear which axis is which on the displayed plot. We also set the title at this point:
ax.set_xlabel("x")

ax.set_ylabel("y")

ax.set_zlabel("z")

ax.set_title("Graph of the function f(x, y)")

You can use the `plt.show`

routine to display the figure in a new window (if you are using Python interactively and not in a Jupyter notebook or on an IPython console) or `plt.savefig`

to save the figure to a file. The result of the preceding sequence is shown here:

Figure 2.6 - A three-dimensional surface plot produced with Matplotlib

- Contour plots do not require the
`mplot3d`

toolkit, and there is a`contour`

routine in the`pyplot`

interface that produces contour plots. However, unlike the usual (two-dimensional) plotting routines, the`contour`

routine requires the same arguments as the`plot_surface`

method. We use the following sequence to produce a plot:fig = plt.figure() # Force a new figure

plt.contour(grid_x, grid_y, z, cmap="gray")

plt.title("Contours of f(x, y)")

plt.xlabel("x")

plt.ylabel("y")

The result is shown in the following plot:

Figure 2.7 - Contour plot produced using Matplotlib with the default settings

The peak and basin of the function are shown clearly here by the rings of concentric circles. In the top right, the shading is lighter, indicating that the function is increasing, and in the bottom left, the shade is darker, indicating that the function is decreasing. The curve that separates the regions in which the function is increasing and decreasing is shown between them.

## How it works...

The `mplot3d`

toolkit provides an `Axes3D`

object, which is a three-dimensional version of the `Axes`

object in the core Matplotlib package. This is made available to the `axes`

method on a `Figure`

object when the `projection="3d"`

keyword argument is given. A surface plot is obtained by drawing quadrilaterals in the three-dimensional projection between nearby points in the same way that a two-dimensional curve is approximated by straight lines joining adjacent points.

The `plot_surface`

method needs the values to be provided as a two-dimensional array that encodes the values on a grid of pairs. We created a range of and values that we are interested in, but if we simply evaluate our function on the pairs of corresponding values from these arrays, we will get the values along a line and not over a grid. Instead, we use the `meshgrid`

routine, which takes the two `X`

and `Y`

arrays and creates from them a grid consisting of all the possible combinations of values in `X`

and `Y`

. The output is a pair of two-dimensional arrays on which we can evaluate our function. We can then provide all three of these two-dimensional arrays to the `plot_surface`

method.

## There’s more...

The routines described in the preceding section, `contour`

and `plot_surface`

, only work with highly structured data where the , , and components are arranged into grids. Unfortunately, real-life data is rarely so structured. In this case, you need to perform some kind of interpolation between known points to approximate the value on a uniform grid, which can then be plotted. A common method for performing this interpolation is by triangulating the collection of pairs and then using the values of the function on the vertices of each triangle to estimate the value on the grid points. Fortunately, Matplotlib has a method that does all of these steps and then plots the result, which is the `plot_trisurf`

routine. We briefly explain how this can be used here:

- To illustrate the use of
`plot_trisurf`

, we will plot a surface and contours from the following data:x = np.array([ 0.19, -0.82, 0.8 , 0.95, 0.46, 0.71,

-0.86, -0.55, 0.75,-0.98, 0.55, -0.17, -0.89,

-0.4 , 0.48, -0.09, 1., -0.03, -0.87, -0.43])

y = np.array([-0.25, -0.71, -0.88, 0.55, -0.88, 0.23,

0.18,-0.06, 0.95, 0.04, -0.59, -0.21, 0.14, 0.94,

0.51, 0.47, 0.79, 0.33, -0.85, 0.19])

z = np.array([-0.04, 0.44, -0.53, 0.4, -0.31,

0.13,-0.12, 0.03, 0.53, -0.03, -0.25, 0.03,

-0.1 ,-0.29, 0.19, -0.03, 0.58, -0.01, 0.55,

-0.06])

- This time, we will plot both the surface and contour (approximations) on the same figure as two separate subplots. For this, we supply the
`projection="3d"`

keyword argument to the subplot that will contain the surface. We use the`plot_trisurf`

method on the three-dimensional axes to plot the approximated surface, and the`tricontour`

method on the two-dimensional axes to plot the approximated contours:fig = plt.figure(tight_layout=True) # force new figure

ax1 = fig.add_subplot(1, 2, 1, projection="3d") # 3d axes

ax1.plot_trisurf(x, y, z)

ax1.set_xlabel("x")

ax1.set_ylabel("y")

ax1.set_zlabel("z")

ax1.set_title("Approximate surface")

- We can now plot the contours for the triangulated surface using the following command:
ax2 = fig.add_subplot(1, 2, 2) # 2d axes

ax2.tricontour(x, y, z)

ax2.set_xlabel("x")

ax2.set_ylabel("y")

ax2.set_title("Approximate contours")

We include the `tight_layout=True`

keyword argument with the figure to save a call to the `plt.tight_layout`

routine later. The result is shown here:

Figure 2.8 - Approximate surface and contour plots generated from unstructured data using triangulation

In addition to surface plotting routines, the `Axes3D`

object has a `plot`

(or `plot3D`

) routine for simple three-dimensional plotting, which works exactly as the usual `plot`

routine but on three-dimensional axes. This method can also be used to plot two-dimensional data on one of the axes.

## See also

Matplotlib is the go-to plotting library for Python, but other options do exist. We’ll see the Bokeh library in *Chapter 6*. There are other libraries, such as Plotly (https://plotly.com/python/), that simplify the process of creating certain types of plots and adding more features, such as interactive plots.

# Customizing three-dimensional plots

Contour plots can hide some detail of the surface that they represent since they only show where the “height” is similar and not what the value is, even in relation to the surrounding values. On a map, this is remedied by printing the height onto certain contours. Surface plots are more revealing, but the problem of projecting three-dimensional objects into 2D to be displayed on a screen can itself obscure some details. To address these issues, we can customize the appearance of a three-dimensional plot (or contour plot) to enhance the plot and make sure the detail that we wish to highlight is clear. The easiest way to do this is by changing the colormap of the plot, as we saw in the previous recipe. (By default, Matplotlib will produce surface plots with a single color, which makes details difficult to see in printed media.) In this recipe, we look at some other ways we can customize 3D surface plots, including changing the initial angle of the display and changing the normalization applied for the colormap.

## Getting ready

In this recipe, we will further customize the function we plotted in the previous recipe:

We generate points at which this should be plotted, as in the previous recipe:

t = np.linspace(-5, 5) x, y = np.meshgrid(t, t) z = np.exp(-((x-2.)**2 + (y-3.)**2)/4) - np.exp( -((x+3.)**2 + (y+2.)**2)/3)

Let’s see how to customize a three-dimensional plot of these values.

## How to do it...

The following steps show how to customize the appearance of a 3D plot:

As usual, our first task is to create a new figure and axes on which we will plot. Since we’re going to customize the properties of the `Axes3D`

object, we’ll just create a new figure first:

fig = plt.figure()

Now, we need to add a new `Axes3D`

object to this figure and change the initial viewing angle by setting the `azim`

and `elev`

keyword arguments along with the `projection=`

`"3d"`

keyword argument that we have seen before:

ax = fig.add_subplot(projection="3d", azim=-80, elev=22)

With this done, we can now plot the surface. We’re going to change the bounds of the normalization so that the maximum value and minimum value are not at the extreme ends of our colormap. We do this by changing the `vmin`

and `vmax`

arguments:

ax.plot_surface(x, y, z, cmap="gray", vmin=-1.2, vmax=1.2)

Finally, we can set up the axes labels and the title as usual:

ax.set_title("Customized 3D surface plot") ax.set_xlabel("x") ax.set_ylabel("y") ax.set_zlabel("z")

The resulting plot is shown in *Figure 2**.9*:

Figure 2.9 - Customized 3D surface plot with modified normalization and an initial viewing angle

Comparing *Figure 2**.6* with *Figure 2**.9*, we can see that the latter generally contains darker shades compared to the former, and the viewing angle offers a better view of the basin where the function is minimized. The darker shade is due to the normalization applied to the values for the colormap, which we altered using the `vmin`

and `vmax`

keyword arguments.

## How it works...

Color mapping works by assigning an RGB value according to a scale—the **colormap**. First, the values are normalized so that they lie between `0`

and `1`

, which is typically done by a linear transformation that takes the minimum value to `0`

and the maximum value to `1`

. The appropriate color is then applied to each face of the surface plot (or line, in another kind of plot).

In the recipe, we used the `vmin`

and `vmax`

keyword arguments to artificially change the value that is mapped to `0`

and `1`

, respectively, for the purposes of fitting the colormap. In effect, we changed the ends of the color range applied to the plot.

Matplotlib comes with a number of built-in colormaps that can be applied by simply passing the name to the `cmap`

keyword argument. A list of these colormaps is given in the documentation (https://matplotlib.org/tutorials/colors/colormaps.html) and also comes with a reversed variant, which is obtained by adding the `_r`

suffix to the name of the chosen colormap.

The viewing angle for a 3D plot is described by two angles: the **Azimuthal** angle, measured within the reference plane (here, the --plane), and the **elevation** angle, measured as the angle from the reference plane. The default viewing angle for `Axes3D`

is -60 Azimuthal and 30 elevation. In the recipe, we used the `azim`

keyword argument of `plot_surface`

to change the initial Azimuthal angle to -80 degrees (almost from the direction of the negative axis) and the `elev`

argument to change the initial elevation to 22 degrees.

## There’s more...

The normalization step in applying a colormap is performed by an object derived from the `Normalize`

class. Matplotlib provides a number of standard normalization routines, including `LogNorm`

and `PowerNorm`

. Of course, you can also create your own subclass of `Normalize`

to perform the normalization. An alternative `Normalize`

subclass can be added using the `norm`

keyword of `plot_surface`

or other plotting functions.

For more advanced uses, Matplotlib provides an interface for creating custom shading using light sources. This is done by importing the `LightSource`

class from the `matplotlib.colors`

package, and then using an instance of this class to shade the surface elements according to the value. This is done using the `shade`

method on the `LightSource`

object:

from matplotlib.colors import LightSource light_source = LightSource(0, 45) # angles of lightsource cmap = plt.get_cmap("binary_r") vals = light_source.shade(z, cmap) surf = ax.plot_surface(x, y, z, facecolors=vals)

Complete examples are shown in the Matplotlib gallery should you wish to learn more about how this.

In addition to the viewing angle, we can also change the type of projection used to represent 3D data as a 2D image. The default is a perspective projection, but we can also use an orthogonal projection by setting the `proj_type`

keyword argument to `"ortho"`

.

# Plotting vector fields with quiver plots

A vector field is a function that assigns to each point in a region a vector—it is a vector-valued function defined on a space. These are especially common in the study of (systems of) differential equations, where a vector field typically appears as the right-hand side of the equation. (See the *Solving systems of differential equations* recipe from *Chapter 3* for more details.) For this reason, it is often useful to visualize a vector field and understand how the function will evolve over space. For now, we’re simply going to produce a plot of a vector field using a quiver plot, which takes a set of and coordinates and a set of and vectors, and produces a plot on which each point has an arrow in the direction and whose length is the length of this vector. (Hopefully, this will become more clear when we actually create the said plot.)

## Getting ready

As usual, we import the Matplotlib `pyplot`

interface under the alias `plt`

. Before we start, we need to define a function that takes a point and produces a vector; we’ll use this later to generate and data that will be passed to the plotting function.

For this example, we’re going to plot the following vector field:

For this example, we’ll plot the vector field over the region where and .

## How to do it…

The following steps show how to visualize the aforementioned vector field over the specified region.

First, we need to define a Python function that evaluates our vector field at points:

def f(x, y): v = x**2 +y**2 return np.exp(-2*v)*(x+y), np.exp( -2*v)*(x-y)

Next, we need to create our grid of points covering the region. For this, we first create a temporary `linspace`

routine with values between `-1`

and `1`

. Then, we use `meshgrid`

to generate a grid of points:

t = np.linspace(-1., 1.) x, y = np.meshgrid(t, t)

Next, we use our function to generate `dx`

and `dy`

values that describe the vectors at each grid point:

dx, dy = f(x, y)

Now, we can create a new figure and axis and use the `quiver`

method to generate a plot:

fig, ax = plt.subplots() ax.quiver(x, y, dx, dy)

The resulting plot is shown in *Figure 2**.10*:

Figure 2.10 - Visualization of a vector field using a quiver plot

In *Figure 2**.10*, we can see the value represented as an arrow based at each coordinate. The size of the arrow is determined by the magnitude of the vector field. At the origin, the vector field has , so the arrows nearby are very small.

## How it works…

Our example from the recipe is a mathematical construction rather than something that might arise from real data. For this particular case, the arrows describe how some quantity might evolve if it flows according to the vector field we specified.

Each point in the grid is the base of an arrow. The direction of the arrow is given by the corresponding value, and the length of the arrow is normalized by length (so, a vector with smaller components produces a shorter arrow). This can be customized by changing the `scale`

keyword argument. Many other aspects of the plot can be customized too.

## There’s more…

If you want to plot a set of trajectories that follow a vector field, you can use the `streamplot`

method. This will plot trajectories starting at various points to indicate the general flow in different parts of the domain. Each streamline has an arrow to indicate the direction of flow. For example, *Figure 2**.11* shows the result of using the `streamplot`

method with the vector field in the recipe:

Figure 2.11 – Plot of the trajectories described by the vector field from the recipe

In a different scenario, you might have data about wind speed (or similar quantities) at a number of coordinates—on a map, say—and you want to plot these quantities in the standard style for weather charts. Then, we can use the `barbs`

plotting method. The arguments are similar to the `quiver`

method.

# Further reading

The Matplotlib package is extensive, and we can scarcely do it justice in such a short space. The documentation contains far more detail than is provided here. Moreover, there is a large gallery of examples (https://matplotlib.org/gallery/index.html#) covering many more of the capabilities of the package than in this book.

Other packages build on top of Matplotlib to offer high-level plotting methods for specific applications. For example, the Seaborn libraries provide routines for visualizing data (https://seaborn.pydata.org/).