Contour Maps in Python

This activity requires that the following software is installed on your system:

The simplest way to get a Python environment with all the packages needed for a successful scientific environment is to download (free for academics) the Enthought Python Distribution, available for Windows, Mac OS X, and Linux. Simply visit the following URL:

and download the version compatible with your operating system. Installation is a breeze; it's double-click and accept the defaults. Some caveats:

Once you have the EPD distribution installed, you can continue with this level curve activity. One of the packages included in the EPD distribution is Matplotlib, which allows the user to make 2D plots with commands that are almost identical to those used in Matlab.

Level Curves

In this activity we will introduce Matplotlib's contour command, which is used to plot the level curves of a multivariable function. Let's begin with a short discussion of the level curve concept.

Hikers and backpackers are likely to take along a copy of a topographical map when verturing into the wilderness (see Figure 1).

An image of a topo map.

A topographical map has lines of constant height.

If you walk along one of the contours shown in Figure 1, you will neither gain nor lose elevation. You're walking along a curve of constant elevation. If you walk directly perpendicular to a contour, then you are either walking directly downhill or uphill. When the contours are far apart, the gain or loss in elevation is gradual. When the contours are close together, the gain or loss in elevation is quite rapid.

The level curves of a multivariate function are analogous to the contours in the topographical map. They are curves of constant elevation. Let's look at an example.

Sketch several level curves of the function `f(x,y)=x^2+y^2`.

Solution: We are interested in finding points of constant elevation, that is, solutions of the equation


where `c` is a constant. Equivalently, we wish to sketch solutions of


where `c` is a constant. Of course, these "level curves" are circles, centered at the origin, with radius `sqrt(c)`. These level curves are drawn in Figure 2 for constants `c=0`, 1, 2, 3, and 4`.


Level curves of `f(x,y)=x^2+y^2` lie in the `xy`-plane.

Matplotlib: It's a simple task to draw the level curves of Figure 2 using Matplotlib's contour command. First, use the cd command to change to the directory in which you want to work. For example,

In [100]: cd \math50c\activities\python\contours

will change directories to \math50c\activities\python\contours, provided this folder exists on this path. From this point onwards, any saved images will wind up in the folder contours on this path.

To sketch contours, we begin as if we were going to draw a surface, creating a grid of `(x,y)` pairs with the meshgrid command.

In [105]: x=linspace(-3,3,40)

In [106]: y=linspace(-3,3,40)

In [107]: x,y=meshgrid(x,y)

We then use the function `f(x,y)=x^2+y^2`, or equivalently, `z=x^2+y^2`, to calculate the `z`-values.

In [108]: z=x**2+y**2

Use the contour command to draw the level curves.

In [109]: contour(x,y,z)

Add a grid, then equalize the axes.

In [120]: grid(True)

In [121]: axis('scaled')

Annotate the plot.

In [122]: xlabel('x-axis')

In [123]: ylabel('y-axis')

In [124]: title('Level curves of the function f(x,y) = x^2 + y^2.')

The above sequence of commands will produce the level curves shown in Figure 3.

Level curves of `f(x,y)=x^2+y^2` drawn with Matlab's contour command.

Level curves of `f(x,y)=x^2+y^2` drawn with Matlab's contour command.

By default, Matplotlib draws a few more level curves than the number shown in Figure 2.

Adding Labels to the Contours: It would be nice if we could label each contour with its height. As one might expect, Matplotlib has this capability. First, clear the figure window.

In [130]: clf()

Next, draw the set of contours again, capturing the output in a return label for later use with the clabel command.

In [131]: cs=contour(x,y,z)

Without getting too technical, information on the level curves is stored in the output variable cs. We then feed the output as input to Matlplotlib's clabel command.

In [132]: clabel(cs,inline=1,fontsize=10)

Using the same formatting as above (grid, scaling, and annotations), this produces the image shown in Figure 4.

Label each contour with its height.

Label each contour with its height.

Forcing Contours

Sometimes you'd like to do one of two things:

  1. Force more contours than the default number provided by the contour command.
  2. Force contours at particular heights.

Forcing More Contours: You can force more contours by adding an additional argument to the contour command. First, close the current figure window with the following command.

In [141]: close()

To force 20 contours, execute the following command.

In [201]: cs=contour(x,y,z,20)

Adding the formatting commands (grid, scale, and annotate) produces the additional contours shown in Figure 6.

Forcing additional contours.

Forcing additional contours.

We wouldn't want to label all of these contours as it would be a bit crowded. It is possible to label specific contours. First, let's use routines from the numpy package (which is installed by the EPD distribution and used by Matplotlib) to find the maximum and minimum `z`-values.

In [206]: z.min()
Out[206]: 0.011834319526627135

In [207]: z.max()
Out[207]: 18.0

Create a vector with the levels you wish to label.

In [208]: levels=arange(2,18,2)

Numpy's arange(start,stop,increment) syntax creates a of value ranging from start through stop in increments of increment. Thus, it is no surprise that levels contains the following entries.

In [209]: levels
Out[209]: array([ 2,  4,  6,  8, 10, 12, 14, 16])

Now, let's label level curves at the heights contained in levels.

In [211]: clabel(cs,levels,inline=1,fontsize=9)

The result is shown in Figure 6.

Labeling contours at particular heights.

Labeling contours at particular heights.

Forcing Specific Contours: You can also force contours at specific heights. To reproduce the level curves of Figure 1, at the heights `c=0`, 1, 2, 3, and 4, we pass the specific heights we wish to see in a vector to the contour command. First, clear the current figure window.

In [155]: clf()

Next, list the specific heights in a vector.

In [157]: levels=[0,1,2,3,4]

Pass the vector levels to the contour command as follows:

In [158]: cs=contour(x,y,z,levels)

Labeling the contours shows that our contours have the heights requested.

In [159]: clabel(cs,inline=1,fontsize=9)

These commands, plus the formatting commands (grid, scale, and annotate) produce the result shown in Figure 7.

Forcing contours at particular heights.

Forcing contours at particular heights.

Note the strong resemblance of Figure 7 to Figure 1

Miscellaneous Extras

Implicit Plotting: Sometimes you want to draw a single contour. For example, suppose you wish to draw the graph of the implict relation `x^2+2xy+y2-2x=3`. One way to proceed would be to first define the function


then plot the level curve `F(x,y)=3`. Start by closing the current figure window.

In [166]: close()

Create a grid of `(x,y)` pairs.

In [167]: x=linspace(-3,3,40)

In [168]: y=linspace(-3,3,40)

In [169]: x,y=meshgrid(x,y)

Calculate `z=f(x,y)=x^2+2xy+y^2-2x`.

In [170]: z=x**2-2*x*y+y**2-2*x

Now, we wish to draw the single contour `z=f(x,y)=3`. Create a vector with this height. Matplotlib's contour command has a different response, depending on whether a number of an array is entered in the fourth position. If we go with contour(x,y,z,3), then three level curves will be drawn. Instead, we set the levels in a vector.

In [171]: levels=[3]

The following command will plot the single contour at height 3.

In [178]: contour(x,y,z,levels)

Add a grid, scale, and add annotations.

In [180]: grid(True)

In [181]: axis('scaled')

Finally, add appropriate annotations.

In [182]: xlabel('x-axis')

In [183]: ylabel('y-axis')

In [189]: title('The implicit curve x^2 + 2xy + y^2 - 2x = 3.')

The result of the above sequence of commands is captured in Figure 8.

Plotting an implicit equation.

Plotting an implicit equation.

Saving Files

Matplotlib allows the user to save files in a variety of formats (pdf, ps, eps, png, and svg). For example, to save the last image as a pdf (Portable Document Format) file, enter the following command.

In [224]: savefig('junk.pdf')

To save the file in svg (Scalable Vector Graphics) format, enter the following command.

In [227]: savefig('junk.svg')

Python Files

The following file features Python code. We include it here for those interested in discovering how we generated the images for this activity. You can download the Python file at the following link. Download the file to a directory or folder on your system.

The file is designed to be run from a terminal window or DOS window. Open a terminal window or a DOS window, then use the cd command to change to the directory in which you downloaded the file levels.m. Type the command python and all of the files used in this activity will be generated. If you want to see a file before it is save, uncomment the show() command for the image you'd like to see. When you close the figure window, the file will be save and the rest of the code will be executed.


In each of the following exercises, obtain a printout of your result and the Python that produced the result. You are encouraged to write a small Python file to generate the result for each exercise. Follow the lead shown in the downloadable file shown in the section above.

  1. Use contour to sketch default level curves for the function `f(x,y)=sqrt(1-x-y)`. Use the clabel command to automatically label the level curves.
  2. Use contour to sketch the level curves `f(x,y)=c` for `f(x,y)=x^2+4y^2` for the following values of `c`: 1,2,3,4, and 5.
  3. Use the contour command to force 20 level curves for the function `f(x,y)=2+3x-2y`.
  4. Use the contour to sketch the graph of the implicit equation `x^3+y^3=3xy`. This curve is known as the Folium of Descartes.