%load_ext load_style
%load_style talk.css
import addutils.toc ; addutils.toc.js(ipy_notebook=True)
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)
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
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
HTML('<iframe src=http://matplotlib.org/users/colormaps.html width=1000 height=400></iframe>')
import matplotlib
from matplotlib import pyplot as plt # conventional way to import the plotting interface of matplotlib
import numpy as np
Matplotlib can use different backends
, in the first part of this notebook, we use the IPython inline backend, which is actually part of IPython
%matplotlib inline
print matplotlib.rcParams['backend']
matplotlib.rcsetup.all_backends
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
import matplotlib
matplotlib.use('nbAgg')
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
%matplotlib inline
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:
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')
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
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
# 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')
x = np.linspace(-5, 5, 10)
y = x ** 2
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
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
%matplotlib osx
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
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.
lines[0].set_color('red')
fig.canvas.draw()
lines[0].set_linewidth(6)
fig.canvas.draw()
Other plot elements can be manipulated as well. For example, text:
txt = plt.text(3.5, 0.5, "Hello Wellington")
txt.set_size(20)
txt.set_color('blue')
fig.canvas.draw()
You can also dynamically change the location of the text:
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:
lines[0].set_ydata(np.sin(2 * x))
fig.canvas.draw()
This sort of operation can be used to create simple animations -- for example:
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:
# back to the inline backend
%matplotlib inline
from matplotlib import style
style.available
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
Image('http://imgs.xkcd.com/comics/front_door.png')
with plt.xkcd():
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, np.sin(x))
ax.set_title('sine')
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
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()
plt.subplots()
¶x = np.linspace(0, 10, 1000)
f, axes = plt.subplots(nrows=2, ncols=1)
type(axes)
axes.shape
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))
plt.subplot2grid()
¶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')
from mpl_toolkits.basemap import Basemap as bm
from mpl_toolkits.basemap import supported_projections, projection_params
print supported_projections
projection_params['merc']
defines the parameters for the Mercator projection
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
m = bm(projection='merc',\
llcrnrlat=llcrnrlat,\
urcrnrlat=urcrnrlat,\
llcrnrlon=llcrnrlon,\
urcrnrlon=urcrnrlon,\
lat_ts=lat_ts,\
resolution='i', \
area_thresh=100)
and now plots
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()
url = 'http://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/interp_OLR/olr.mon.mean.nc'
from netCDF4 import Dataset
nc = Dataset(url, 'r')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
domain = [llcrnrlat,urcrnrlat,llcrnrlon,urcrnrlon]
ilat = ( (lat>=domain[0]) & (lat<=domain[1]) )
ilon = ( (lon>=domain[2]) & (lon<=domain[3]) )
lat = lat[ilat]
lon = lon[ilon]
olr = nc.variables['olr'][0, ilat, ilon]
nc.close()
if you installed xray (pip install xray
)
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()
plt.imshow(olr)
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()
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()
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();