Skip to content

Commit 337efa7

Browse files
committed
Merged revisions 8769-8770 via svnmerge from
https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v1_0_maint ........ r8769 | jdh2358 | 2010-11-04 11:28:03 -0700 (Thu, 04 Nov 2010) | 1 line added recipes ........ r8770 | jdh2358 | 2010-11-04 13:50:49 -0700 (Thu, 04 Nov 2010) | 1 line some updates to recipes ........ svn path=/trunk/matplotlib/; revision=8771
2 parents 5d3d277 + 312096c commit 337efa7

File tree

2 files changed

+349
-0
lines changed

2 files changed

+349
-0
lines changed

doc/users/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ User's Guide
2727
transforms_tutorial.rst
2828
path_tutorial.rst
2929
annotations_guide.rst
30+
recipes.rst
3031
toolkits.rst
3132
screenshots.rst
3233
whats_new.rst

doc/users/recipes.rst

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
.. _recipes:
2+
3+
********************
4+
Our Favorite Recipes
5+
********************
6+
7+
Here is a collection of short tutorials, examples and code snippets
8+
that illustrate some of the useful idioms and tricks to make snazzier
9+
figures and overcome some matplotlib warts.
10+
11+
12+
Sharing axis limits and views
13+
=============================
14+
15+
It's common to make two or more plots which share an axis, eg two
16+
subplots with time as a common axis. When you pan and zoom around on
17+
one, you want the other to move around with you. To facilitate this,
18+
matplotlib Axes support a ``sharex`` and ``sharey`` attribute. When
19+
you create a :func:`~matplotlib.pyplot.subplot` or
20+
:func:`~matplotlib.pyplot.axes` instance, you can pass in a keyword
21+
indicating what axes you want to share with
22+
23+
.. sourcecode:: ipython
24+
25+
In [96]: t = np.arange(0, 10, 0.01)
26+
27+
In [97]: ax1 = plt.subplot(211)
28+
29+
In [98]: ax1.plot(t, np.sin(2*np.pi*t))
30+
Out[98]: [<matplotlib.lines.Line2D object at 0x98719ec>]
31+
32+
In [99]: ax2 = plt.subplot(212, sharex=ax1)
33+
34+
In [100]: ax2.plot(t, np.sin(4*np.pi*t))
35+
Out[100]: [<matplotlib.lines.Line2D object at 0xb7d8fec>]
36+
37+
Easily creating subplots
38+
========================
39+
40+
In early versions of matplotlib, if you wanted to use the pythonic API
41+
and create a figure instance and from that create a grid of subplots,
42+
possibly with shared axes, it involved a fair amount of boilerplate
43+
code. Eg
44+
45+
.. sourcecode:: python
46+
47+
# old style
48+
fig = plt.figure()
49+
ax1 = fig.add_subplot(221)
50+
ax2 = fig.add_subplot(222, sharex=ax1, sharey=ax1)
51+
ax3 = fig.add_subplot(223, sharex=ax1, sharey=ax1)
52+
ax3 = fig.add_subplot(224, sharex=ax1, sharey=ax1)
53+
54+
Fernando Perez has provided a nice top level method to create in
55+
:func:`~matplotlib.pyplots.subplots` (note the "s" at the end)
56+
everything at once, and turn off x and y sharing for the whole bunch.
57+
You can either unpack the axes individually::
58+
59+
# new style method 1
60+
fig, (ax1, ax2, ax3, ax4) = plt.subplots(2, 2, sharex=True, sharey=True)
61+
ax1.plot(x)
62+
63+
or get them back as a numrows x numcolumns object array which supports
64+
numpy indexing::
65+
66+
# new style method 2
67+
fig, axs = plt.subplots(2, 2, sharex=True, sharey=True)
68+
axs[0,0].plot(x)
69+
70+
71+
Fixing common date annoyances
72+
=============================
73+
74+
matplotlib allows you to natively plots python datetime instances, and
75+
for the most part does a good job picking tick locations and string
76+
formats. There are a couple of things it does not handle so
77+
gracefully, and here are some tricks to help you work around them.
78+
We'll load up some sample date data which contains datetime.date
79+
objects in a numpy record array::
80+
81+
In [63]: datafile = cbook.get_sample_data('goog.npy')
82+
83+
In [64]: r = np.load(datafile).view(np.recarray)
84+
85+
In [65]: r.dtype
86+
Out[65]: dtype([('date', '|O4'), ('', '|V4'), ('open', '<f8'),
87+
('high', '<f8'), ('low', '<f8'), ('close', '<f8'),
88+
('volume', '<i8'), ('adj_close', '<f8')])
89+
90+
In [66]: r.date
91+
Out[66]:
92+
array([2004-08-19, 2004-08-20, 2004-08-23, ..., 2008-10-10, 2008-10-13,
93+
2008-10-14], dtype=object)
94+
95+
The dtype of the numpy record array for the field 'date' is '|O4'
96+
which means it is a 4-byte python object pointer; in this case the
97+
objects are datetime.date instances, which we can see when we print
98+
some samples in the ipython terminal window.
99+
100+
If you plot the data, you will see that the x tick labels are all
101+
squashed together::
102+
103+
In [67]: plot(r.date, r.close)
104+
Out[67]: [<matplotlib.lines.Line2D object at 0x92a6b6c>]
105+
106+
.. plot::
107+
108+
import matplotlib.cbook as cbook
109+
datafile = cbook.get_sample_data('goog.npy')
110+
r = np.load(datafile).view(np.recarray)
111+
plt.figure()
112+
plt.plot(r.date, r.close)
113+
plt.title('Default date handling can cause overlapping labels')
114+
115+
Another annoyance is that if you hover the mouse over a the window and
116+
look in the lower right corner of the matplotlib toolbar at the x and
117+
y coordinates, you see that the x locations are formatted the same way
118+
the tick labels are, eg "Dec 2004". What we'd like is for the
119+
location in the toolbar to have a higher degree of precision, eg
120+
giving us the exact date out mouse is hovering over. To fix the first
121+
problem, we can use method:`matplotlib.figure.Figure.autofmt_xdate()`
122+
and to fix the second problem we can use the ``ax.fmt_xdata``
123+
attribute which can be set to any function that takes a position and
124+
returns a string. matplotlib has a number of date formatters built
125+
im, so we'll use one of those.
126+
127+
.. plot::
128+
:include-source:
129+
130+
import matplotlib.cbook as cbook
131+
datafile = cbook.get_sample_data('goog.npy')
132+
r = np.load(datafile).view(np.recarray)
133+
fig, ax = plt.subplots(1)
134+
ax.plot(r.date, r.close)
135+
136+
# rotate and align the tick labels so they look better
137+
fig.autofmt_xdate()
138+
139+
# use a more precise date string for the x axis locations in the
140+
# toolbar
141+
import matplotlib.dates as mdates
142+
ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d')
143+
plt.title('autfmt_xdate fixes the labels')
144+
145+
Now when you hover your mouse over the plotted data, you'll see date
146+
format strings like 2004-12-01.
147+
148+
Fill Between and Alpha
149+
======================
150+
151+
The :meth:`~matplotlib.axes.Axes.fill_between` function generates a
152+
shaded region between a min and max boundary that is useful for
153+
illustrating ranges. It has a very handy ``where`` argument to
154+
combine filling with logical ranges, eg to just fill in a curve over
155+
some threshold value.
156+
157+
At it's most basic level, ``fill_between`` can be use to enhance a
158+
graphs visual appearance. Let's compare two graphs of a financial
159+
times with a simple line plot on the left and a filled line on the
160+
right.
161+
162+
.. plot::
163+
:include-source:
164+
165+
import matplotlib.cbook as cbook
166+
167+
# load up some sample financial data
168+
datafile = cbook.get_sample_data('goog.npy')
169+
r = np.load(datafile).view(np.recarray)
170+
171+
# create two subplots with the shared x and y axes
172+
fig, (ax1, ax2) = plt.subplots(1,2, sharex=True, sharey=True)
173+
174+
pricemin = r.close.min()
175+
176+
ax1.plot(r.date, r.close, lw=2)
177+
ax2.fill_between(r.date, pricemin, r.close, facecolor='blue', alpha=0.5)
178+
179+
for ax in ax1, ax2:
180+
ax.grid(True)
181+
182+
ax1.set_ylabel('price')
183+
fig.suptitle('Google (GOOG) daily closing price')
184+
fig.autofmt_xdate()
185+
186+
The alpha channel is not necessary here, but it can be used to soften
187+
colors for more visually appealing plots. In other examples, as we'll
188+
see below, the alpha channel is functionally useful as the shaded
189+
regions can overlap and alpha allows you to see both. Note that the
190+
postscript format does not support alpha (this is a postscript
191+
limitation, not a matplotlib limitation), so when using alpha save
192+
your figures in PNG, PDF or SVG.
193+
194+
Our next example computes two populations of random walkers with a
195+
different mean and standard deviation of the normal distributions from
196+
which there steps are drawn. We use shared regions to plot +/- one
197+
standard deviation of the mean position of the population. Here the
198+
alpha channel is useful, not just aesthetic.
199+
200+
.. plot::
201+
:include-source:
202+
203+
Nsteps, Nwalkers = 100, 250
204+
t = np.arange(Nsteps)
205+
206+
# an Nsteps x Nwalkers array of random walk steps
207+
S1 = 0.002 + 0.01*np.random.randn(Nsteps, Nwalkers)
208+
S2 = 0.004 + 0.02*np.random.randn(Nsteps, Nwalkers)
209+
210+
# an Nsteps x Nwalkers array of random walker positions
211+
X1 = S1.cumsum(axis=0)
212+
X2 = S2.cumsum(axis=0)
213+
214+
215+
# Nsteps length arrays empirical means and standard deviations of both
216+
# populations over time
217+
mu1 = X1.mean(axis=1)
218+
sigma1 = X1.std(axis=1)
219+
mu2 = X2.mean(axis=1)
220+
sigma2 = X2.std(axis=1)
221+
222+
# plot it!
223+
fig, ax = plt.subplots(1)
224+
ax.plot(t, mu1, lw=2, label='mean population 1', color='blue')
225+
ax.plot(t, mu1, lw=2, label='mean population 2', color='yellow')
226+
ax.fill_between(t, mu1+sigma1, mu1-sigma1, facecolor='blue', alpha=0.5)
227+
ax.fill_between(t, mu2+sigma2, mu2-sigma2, facecolor='yellow', alpha=0.5)
228+
ax.set_title('random walkers empirical $\mu$ and $\pm \sigma$ interval')
229+
ax.legend(loc='upper left')
230+
ax.set_xlabel('num steps')
231+
ax.set_ylabel('position')
232+
ax.grid()
233+
234+
235+
The where keyword argument is very handy for highlighting certain
236+
regions of the graph. Where takes a boolean mask the same length as
237+
the x, ymin and ymax arguments, and only fills in the region where the
238+
boolean mask is True. In the example below, we take a a single random
239+
walker and compute the analytic mean and standard deviation of the
240+
population positions. The population mean is shown as the black
241+
dashed line, and the plus/minus one sigma deviation from the mean is
242+
showsn as the yellow filled region. We use the where mask
243+
``X>upper_bound`` to find the region where the walker is above the
244+
one sigma boundary, and shade that region blue.
245+
246+
.. plot::
247+
:include-source:
248+
249+
np.random.seed(1234)
250+
251+
Nsteps = 500
252+
t = np.arange(Nsteps)
253+
254+
mu = 0.002
255+
sigma = 0.01
256+
257+
# the steps and position
258+
S = mu + sigma*np.random.randn(Nsteps)
259+
X = S.cumsum()
260+
261+
# the 1 sigma upper and lower population bounds
262+
lower_bound = mu*t - sigma*np.sqrt(t)
263+
upper_bound = mu*t + sigma*np.sqrt(t)
264+
265+
fig, ax = plt.subplots(1)
266+
ax.plot(t, X, lw=2, label='walker position', color='blue')
267+
ax.plot(t, mu*t, lw=1, label='population mean', color='black', ls='--')
268+
ax.fill_between(t, lower_bound, upper_bound, facecolor='yellow', alpha=0.5,
269+
label='1 sigma range')
270+
ax.legend(loc='upper left')
271+
272+
# here we use the where argument to only fill the region where the
273+
# walker is above the population 1 sigma boundary
274+
ax.fill_between(t, upper_bound, X, where=X>upper_bound, facecolor='blue', alpha=0.5)
275+
ax.set_xlabel('num steps')
276+
ax.set_ylabel('position')
277+
ax.grid()
278+
279+
280+
Another handy use of filled regions is to highlight horizontal or
281+
vertical spans of an axes -- for that matplotlib has some helper
282+
functions :meth:`~matplotlib.axes.Axes.axhspan` and
283+
:meth:`~matplotlib.axes.Axes.axvspan` and example
284+
:ref:`pylab_examples-axhspan_demo`.
285+
286+
287+
Transparent, fancy legends
288+
==========================
289+
290+
Sometimes you know what your data looks like before you plot it, and
291+
mak know for instance that there won't be much data in the upper right
292+
hand corner. Then you can safely create a legend that doesn't overlay
293+
your data::
294+
295+
ax.legend(loc='upper right')
296+
297+
Other times you don't know where your data is, and loc='best' will try
298+
and place the legend::
299+
300+
ax.legend(loc='upper right')
301+
302+
but still, your legend may overlap your data, and in these cases it's
303+
nice to make the legend frame transparent.
304+
305+
306+
.. plot::
307+
:include-source:
308+
309+
np.random.seed(1234)
310+
fig, ax = plt.subplots(1)
311+
ax.plot(np.random.randn(300), 'o-', label='normal distribution')
312+
ax.plot(np.random.rand(300), 's-', label='uniform distribution')
313+
ax.set_ylim(-3, 3)
314+
leg = ax.legend(loc='best', fancybox=True)
315+
leg.get_frame().set_alpha(0.5)
316+
317+
ax.set_title('fancy, transparent legends')
318+
319+
320+
Placing text boxes
321+
==================
322+
323+
When decorating axes with text boxes, two useful tricks are to place
324+
the text in axes coordinates (see :ref:`transforms_tutorial`), so the
325+
text doesn't move around with changes in x or y limits. You can also
326+
use the bbox property of text to surround the text with a
327+
:class:`~matplotlib.patches.Patch` instance -- the boox keyword argument
328+
takes a dictionary with keys that are Patch properties.
329+
330+
.. plot::
331+
:include-source:
332+
333+
np.random.seed(1234)
334+
fig, ax = plt.subplots(1)
335+
x = 30*np.random.randn(10000)
336+
mu = x.mean()
337+
median = np.median(x)
338+
sigma = x.std()
339+
textstr = '$\mu=%.2f$\n$\mathrm{median}=%.2f$\n$\sigma=%.2f$'%(mu, median, sigma)
340+
341+
ax.hist(x, 50)
342+
# these are matplotlib.patch.Patch properies
343+
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
344+
345+
# place a text box in upper left in axes coords
346+
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14,
347+
verticalalignment='top', bbox=props)
348+

0 commit comments

Comments
 (0)