In [ ]:
%load_ext load_style
%load_style talk.css

Matplotlib and Basemap

In [ ]:
import addutils.toc ; addutils.toc.js(ipy_notebook=True)
In [ ]:
from IPython.display import Image, HTML

Matplotlib is the main (static) graphic library for Scientific Python, it allows to create complex and publication-ready plots in a variety of formats (png, ps, pdf ...). It has a Matlab - like plotting as well as object-oriented API, you have very fine-grained control on everything on your graph (at the expense of being a bit verbose), relatively smooth learning curve if you come from Matlab, not so much from R.


basemap is a Matplotlib toolkit that allows to draw geographical maps and overlay data on these maps, it supports a large number of projections (see http://matplotlib.org/basemap/users/mapsetup.html for a list and later on in the present notebook)

In [ ]:
HTML('<iframe src=http://matplotlib.org width=1000 height=400></iframe>')

The Gallery is your goto place when you are looking at creating a brand new graph using Matplotlib

In [ ]:
HTML('<iframe src=http://matplotlib.org/gallery.html width=1000 height=400></iframe>')

Colormaps can be tricky, here are some examples and advice for using colormaps with Matplotlib

In [ ]:
HTML('<iframe src=http://matplotlib.org/users/colormaps.html width=1000 height=400></iframe>')
In [ ]:
import matplotlib
from matplotlib import pyplot as plt # conventional way to import the plotting interface of matplotlib
import numpy as np

backends in matplotlib

Matplotlib can use different backends, in the first part of this notebook, we use the IPython inline backend, which is actually part of IPython

In [ ]:
%matplotlib inline
In [ ]:
print matplotlib.rcParams['backend']
In [ ]:
matplotlib.rcsetup.all_backends
In [ ]:
x = np.linspace(0, 10, 1000)
plt.plot(x, np.sin(x))

we can switch backends, but need to restart the kernel

In the following we use the notebook backend, which provides some degree of control on the figure in the notebook itself

In [ ]:
import matplotlib
matplotlib.use('nbAgg')
In [ ]:
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(0, 10, 1000)
plt.plot(x, np.sin(x))
plt.show()

we go back to inline plotting via the %matplotlib inline magic command

In [ ]:
%matplotlib inline

Matlab-like interactive plotting

Matplotlib was originally designed to have an interface similar to that of matlab. For this reason, the library maintains pointers to the currently active figure and axes, and makes simple plots straightforward to create. For example:

In [ ]:
plt.figure()  # this is optional: if you just call plt.plot(), a figure will be created
plt.plot(x, np.sin(x))
plt.title('plot 1: sine')

plt.figure()
plt.plot(x, np.cos(x))
plt.title('plot 2: cosine')

Object-oriented Matplotlib

This is fine for simple plots, but when you start wanting to do more powerful things, it can get a little bit tricky. A better way to approach it is to use the Object-Oriented API of Matplotlib

In [ ]:
fig1 = plt.figure() # creates a figure object
ax1 = fig1.add_subplot(1, 1, 1) # creates an 'axes' object in this figure
ax1.plot(x, np.sin(x)) # create plot in ax
ax1.set_title('sine') # set properties of the axes

fig2 = plt.figure()
ax2 = fig2.add_subplot(1, 1, 1)
ax2.plot(x, np.cos(x))
ax2.set_title('cosine')

The big advantage of using the Object-Oriented API is more fined grained control, and also the fact that you can organise your code more (IMHO) logically, making for more readable code

In [ ]:
# creates the figure objects
fig1 = plt.figure()
fig2 = plt.figure()

# axes
ax1 = fig1.add_subplot(1, 1, 1)
ax2 = fig2.add_subplot(1, 1, 1)

# plot
ax1.plot(x, np.sin(x))
ax2.plot(x, np.cos(x))

# set attributes
ax1.set_title('sine')
ax2.set_title('cosine')
In [ ]:
x = np.linspace(-5, 5, 10)

y = x ** 2
In [ ]:
fig = plt.figure(figsize=[10,6])
ax = fig.add_subplot(111)

ax.plot(x, x**2, 'g--', label="y = x**2")  
ax.plot(x, x**3, 'bo', label="$y = x^3$") # can use LaTex notation  
ax.plot(x, x**2 - x**3, 
        color='red',    # can also abbrev to c='g'
        linestyle='-',     # also ls=
        marker='o',        # can also be specified in the linespec
        markersize=8,     # note this is different from the scatter sizes
        markeredgecolor='#01A9DB', # can use html color codes (see http://html-color-codes.info/)
        markeredgewidth=1,
        alpha=0.5,         # sets the transparency
        zorder=-1,         # sets its position obove or below other items layering
        label="$y = x^3 - x^2$") # gives the legend name. When using legend() you don't have to give names

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('title', fontsize=16)
ax.grid()
ax.legend(loc=0); # upper left corner

Other Object Types

all plot elements in a matplotlib figure is associated to an object that can be passed around, manipulated, updated ...

To illustrate this , we will go back to the MacOSX backend (qt for Windows): the plots should be displayed in a separate window

In [ ]:
%matplotlib osx 
In [ ]:
print matplotlib.rcParams['backend']

In a script you generally don't need to specify the backend, if you need to you can call:

import matplotlib 
matplotlib.use('MacOSX ')

At the beginning of the script, before you import the pyplot interface with

from matplotlib import pyplot as plt

See the list of available backends (interactive and non-interactive) at:

http://matplotlib.org/faq/usage_faq.html#what-is-a-backend

TIP: if running a python + matplotlib script using cron on a Linux / Mac machine, you my need to use the 'Agg' backend

In [ ]:
x = np.linspace(0, 10, 1000)

fig, ax = plt.subplots()
lines = ax.plot(x, np.sin(x))
fig.show()

Now that we've created the plot, we can adjust the attributes of the Line2D instance. In general, any keyword in the plot command has an associated set_ command.

After setting the different parameters, we can call the draw() method of the figure canvas to make the plot reflect the changes.

In [ ]:
lines[0].set_color('red')
fig.canvas.draw()
In [ ]:
lines[0].set_linewidth(6)
fig.canvas.draw()

Other plot elements can be manipulated as well. For example, text:

In [ ]:
txt = plt.text(3.5, 0.5, "Hello Wellington")
In [ ]:
txt.set_size(20)
txt.set_color('blue')
fig.canvas.draw()

You can also dynamically change the location of the text:

In [ ]:
txt.set_x(6)
txt.set_y(-0.5)
fig.canvas.draw()

Similarly, you can even dynamically change the locations of each point in the line! For example, changing the y data looks like this:

In [ ]:
lines[0].set_ydata(np.sin(2 * x))
fig.canvas.draw()

This sort of operation can be used to create simple animations -- for example:

In [ ]:
for i in range(100):
    lines[0].set_ydata(np.sin(x + 0.1 * i))
    fig.canvas.draw()

Please note that this is NOT a good way to create animations in matplotlib. For that rather use the purpose-build animation submodule.

see examples:

http://matplotlib.org/examples/animation/

In [ ]:
# back to the inline backend 
%matplotlib inline 

styles in matplotlib (available from version 1.4)

In [ ]:
from matplotlib import style
In [ ]:
style.available
In [ ]:
with style.context('ggplot'): # use context rather than style.use(), and use with as a context manager
    plt.figure(figsize=(6,6))
    plt.hist(np.random.randn(1000), bins=20, normed=True)
    plt.title('histogram')
    plt.grid()

xkcd-style plots in Matplotlib

In [ ]:
Image('http://imgs.xkcd.com/comics/front_door.png')
In [ ]:
with plt.xkcd():
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(x, np.sin(x))
    ax.set_title('sine')
In [ ]:
with plt.xkcd():
    plt.figure(figsize=(6,6))
    plt.hist(np.random.randn(1000), bins=20, normed=True, color='b', alpha=0.5)
    plt.title('histogram')
    plt.grid()

you can combine xkcd plots and styles

In [ ]:
with plt.xkcd():
    with style.context('ggplot'): 
        plt.figure(figsize=(6,6))
        plt.hist(np.random.randn(1000), bins=20, normed=True, color='b', alpha=0.5)
        plt.title('histogram')
        plt.grid()

subplots and layouts

simple subplots: plt.subplots()

In [ ]:
x = np.linspace(0, 10, 1000)
In [ ]:
f, axes = plt.subplots(nrows=2, ncols=1)
In [ ]:
type(axes)
In [ ]:
axes.shape
In [ ]:
f, axes = plt.subplots(nrows=2, ncols=1)
axes = axes.flatten()
axes[0].plot(x, np.sin(x))
axes[1].plot(x, np.cos(x))

more complex layouts: plt.subplot2grid()

In [ ]:
f = plt.figure(figsize=[10,10])
ax1 = plt.subplot2grid((3,3), (0,0), colspan=1)
ax2 = plt.subplot2grid((3,3), (1,0), colspan=2)
ax3 = plt.subplot2grid((3,3), (0,2), rowspan=3)
ax4 = plt.subplot2grid((3,3), (2,0))
ax5 = plt.subplot2grid((3,3), (2,1))

f.subplots_adjust(wspace=0.8)
f.tight_layout() # some nice spacing!

# adding data to each plot
ax1.plot(np.random.rand(10))
ax2.plot(np.random.rand(100), np.random.rand(100), '.r')
ax3.hist(np.random.normal(size=1000), bins=20)
ax4.fill_between(x, x**2, x**3, color="orange", alpha=0.5)
ax5.plot(np.random.rand(10), c='g', lw=4, marker='o', ms=10, mec='none')
ax5.set_ylabel('x axis')

Basemap

In [ ]:
from mpl_toolkits.basemap import Basemap as bm
from mpl_toolkits.basemap import supported_projections, projection_params 
In [ ]:
print supported_projections
In [ ]:
projection_params['merc']

Draw basemap of a region with a bathymetric and topographic overlay

defines the parameters for the Mercator projection

In [ ]:
llcrnrlat = -50 # lower left corner latitude 
urcrnrlat = 20 # upper right corner latitude 

llcrnrlon = 150 # lower left corner longitude
urcrnrlon = 290 # upper right corner longitude

lat_ts = -40 # true scale latitude

initialises the projection

In [ ]:
m = bm(projection='merc',\
            llcrnrlat=llcrnrlat,\
            urcrnrlat=urcrnrlat,\
            llcrnrlon=llcrnrlon,\
            urcrnrlon=urcrnrlon,\
            lat_ts=lat_ts,\
            resolution='i', \
            area_thresh=100)

and now plots

In [ ]:
f, ax = plt.subplots(figsize=(14,10))

m.ax = ax # the axes in which the map needs to be embedded

m.etopo() # draw bathymetry and topography

m.drawcoastlines()

parallels = np.arange(llcrnrlat, urcrnrlat, 20)

m.drawparallels(parallels, labels=[False,True,False,False]) # left, right, top, bottom

meridians = np.arange(llcrnrlon, urcrnrlon, 30)

m.drawmeridians(meridians,  labels=[False,False,True,True]) # left, right, top, bottom

plt.show()

overlay data on a basemap

In [ ]:
url = 'http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/interp_OLR/olr.mon.mean.nc'
In [ ]:
from netCDF4 import Dataset
nc = Dataset(url, 'r')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
In [ ]:
domain = [llcrnrlat,urcrnrlat,llcrnrlon,urcrnrlon]

ilat = ( (lat>=domain[0]) & (lat<=domain[1]) )
ilon = ( (lon>=domain[2]) & (lon<=domain[3]) )
In [ ]:
lat = lat[ilat]
lon = lon[ilon]
In [ ]:
olr = nc.variables['olr'][0, ilat, ilon]
In [ ]:
nc.close()

if you installed xray (pip install xray)

In [ ]:
import xray
dset = xray.open_dataset(url)
sub = dset.sel(lat=slice(20, -50), lon=slice(150, 290), time='1998-1')
olr = sub['olr'].squeeze()
In [ ]:
plt.imshow(olr)

using pcolormesh (gridded faceted plot)

In [ ]:
from mpl_toolkits.basemap import Basemap

f, ax = plt.subplots(figsize=(12,8))

m.ax = ax

mlon,mlat = np.meshgrid(lon,lat) # turns lat and lon vectors into grids

im = m.pcolormesh(mlon, mlat, olr, latlon=True)

m.drawcoastlines()

parallels = np.arange(llcrnrlat, urcrnrlat, 20)

m.drawparallels(parallels, labels=[False,True,False,False]) # left, right, top, bottom

meridians = np.arange(llcrnrlon, urcrnrlon, 30)

m.drawmeridians(meridians,  labels=[False,False,True,True]) # left, right, top, bottom

cb = m.colorbar(im, pad='6%')
cb.set_label(r'$W.m^{-2}$', fontsize=18)

plt.show()

using contourf (filled contours)

In [ ]:
from mpl_toolkits.basemap import Basemap

f, ax = plt.subplots(figsize=(12,8))

m.ax = ax

im = m.contourf(mlon, mlat, olr, np.arange(180, 290, 5), latlon=True, extend='both')

# Choosing another colormap (see http://matplotlib.org/users/colormaps.html) is easy:
# im = m.contourf(mlon, mlat, olr, np.arange(180, 290, 5), latlon=True, extend='both', cmap=plt.get_cmap('cubehelix_r'))

m.drawcoastlines()

parallels = np.arange(llcrnrlat, urcrnrlat, 20)

m.drawparallels(parallels, labels=[False,True,False,False]) # left, right, top, bottom

meridians = np.arange(llcrnrlon, urcrnrlon, 30)

m.drawmeridians(meridians,  labels=[False,False,True,True]) # left, right, top, bottom

cb = m.colorbar(im, pad='6%')
cb.set_label(r'$W.m^{-2}$', fontsize=18)

plt.show()
In [ ]:
from mpl_toolkits.basemap import Basemap

f, ax = plt.subplots(figsize=(12,8))

m.ax = ax

im = m.contourf(mlon, mlat, olr, np.arange(180, 290, 5), latlon=True, extend='both')

cs = m.contour(mlon, mlat, olr, np.arange(180, 290, 10), latlon=True, colors='k')

plt.clabel(cs, colors='k', fmt="%4i")

# Choosing another colormap (see http://matplotlib.org/users/colormaps.html) is easy:
# im = m.contourf(mlon, mlat, olr, np.arange(180, 290, 5), latlon=True, cmap=plt.get_cmap('cubehelix_r'))

m.drawcoastlines()

parallels = np.arange(llcrnrlat, urcrnrlat, 20)

m.drawparallels(parallels, labels=[False,True,False,False]) # left, right, top, bottom

meridians = np.arange(llcrnrlon, urcrnrlon, 30)

m.drawmeridians(meridians,  labels=[False,False,True,True]) # left, right, top, bottom

cb = m.colorbar(im, pad='6%')
cb.set_label(r'$W.m^{-2}$', fontsize=18)

plt.show();