Python usage notes - Matplotlib, pylab

From Helpful
Jump to navigation Jump to search
Syntaxish: syntax and language · changes and py2/3 · decorators · importing, modules, packages · iterable stuff · concurrency

IO: networking and web · filesystem

Data: Numpy, scipy · pandas, dask · struct, buffer, array, bytes, memoryview · Python database notes

Image, Visualization: PIL · Matplotlib, pylab · seaborn · bokeh · plotly


Tasky: Concurrency (threads, processes, more) · joblib · pty and pexpect

Stringy: strings, unicode, encodings · regexp · command line argument parsing · XML

date and time


Notebooks

speed, memory, debugging, profiling · Python extensions · semi-sorted


Intro

Matplotlib is a graphing package that creates various types of graphs relatively easily.

It is modelled after Matlab's plotting, hence the name.


To get an idea of what kind of plots you can make:


To get an idea of how code looks like - the simplest may be something like

from matplotlib import pyplot

fig, axis = pyplot.subplots(1)
axis.plot([1,5,2,3,3,4,2,1,2,3,5,3,4])
pyplot.show()

...but look below for slightly more realistic tweaking you will often do.


It's also specifically integrated a bit into jupyter notebooks, which is nice for interactive work.



Other software you may prefer in other contexts

  • seaborn - easier to visualize analyses (so useful if you e.g. use pandas or similar), in more interesting graphs than just lines and bars, in particular making it easier to show uncertainties in statistical analysies
  • Mayavi - if you want interactive 3D plots, this is often much faster than matplotlib, and with a similar interface.
See also http://code.enthought.com/projects/mayavi/


GUI side


API choice

There are, broadly, two distinct ways to call into matplotlib:


  • the object-oriented interface
keeps "current state of your graph" in objects.
which is cleaner and isolated when embedding, around threading
the more complex the tweaking, the more controlled/cleaner it tends to be
avoids problems when generating distinct plots at the same time (which as a user you almost never o)
This page uses only this


  • pylab
a more direct imitation of matlab, so if you're a matlab person this will be obvious for you.
simple graphs can be done in fewer lines
the "current state of your graph" is stored in the module itself
also means
that you can't make more than one plot at a time
threading/subinterpreters will bite you


If you're new and want to learn just one style, I strongly recommend the OO interface.

Yes, pylab is a little shorter or the same (see below) for simple graphs.
Yet once you want fancier things you'll probably want the OO style.


Examples (minus the imports, and 'actually show me it' command.)

OO style:

fig, ax = pyplot.subplots()     # actually a shorthand, see below
ax.plot([1,2,3,4], [5,1,3,2])   # plot some data (that's an x and y array)
ax.set_title('some data')

Pylab equivalent:

pylab.figure() # omittable, the first plot() implies its creation. Later plots should clear it. 
pylab.plot([1,2,3,4], [5,1,3,2])
pylab.title('some data')

The difference becomes more pronounced when you have multiple axes, or produce more graphs on a screen and/or over time, because the "I am currently drawing on this" is an evolving part of the module state, not the calls or something you can refer to.


See also:


Layout

Just one plot

Probably shortest:

import matplotlib.pyplot as plt
fig, axis = plt.subplots()  # its default is 1 row, 1 column.  
                            # If either is >1, the second thing returned will be a _list_ of axes rather than a single one

Adding same-sized figures on a grid

Use figure() and figure.add_subplot() if you want to create and use axes objects as you go:

import numpy, matplotlib, matplotlib.pyplot as plt
 
fig = plt.figure(figsize=(12, 6))  # figsize in inches (with the default dpi)
axis2 = fig.add_subplot(2,3, 2)  # same as 232 (matlab style): Lay out 2-row, 3-column grid,
                                 # create and select the 2nd one in that grid, i.e. top middle
axis4 = fig.add_subplot(2,3, 4)  # ...4th plot in the same grid, i.e. bottom left
axis6 = fig.add_subplot(2,3, 6)  # ...6th, bottom right



There is also pyplot.subplots(), which creates axes objects for you, which can save typing if your lotting commands vary very little within a grid. To steal a fragment from here, the same data with a different parameter:

# where interpolation_methods is a list of 18 different methods by name
fig, axes = plt.subplots(3, 6, figsize=(12, 6)) # 3 rows, 6 cols 
   # axes is an array (except if rows==cols==1, the default)


# This makes it short to make plots that are variations on a theme, e.g.:
for ax, interp_method in zip(axes.flat, interpolation_methods):
    ax.imshow(grid, interpolation=interp_method)
    ax.set_title(interp_method)


Notes:

  • add_subplot does little more than using those numbers to calculate position and size to end up drawing in
which each call does independently, based only on the given parameters - if you mix grid sizes can easily make a mess (rather than give errors)
  • The object representing a subplot is Axes and so many examples call their subplots 'axis' or such.
(it can get confusing Axes object in a variable named axis has an Axis attribute (the last as in the label-and-tick thing) -- so you may prefer your own naming.

Spanning cells

There are a few different ways of doing this.


I prefer the gridspec style, which seems the more flexible while still being fairly short.

# the plan below is to have 3x3 for granularity, and span them like:
#
#  1-1-1
#  2-2 3
#  4 5 3




import matplotlib.pyplot as plt, matplotlib.gridspec
fig = plt.figure(figsize=(8,7))
gs = matplotlib.gridspec.GridSpec(3, 3) 
ax1 = fig.add_subplot( gs[0,  : ] )  # first row, all columns
ax2 = fig.add_subplot( gs[1, 0:2] )  # second row, left and middle cells
ax3 = fig.add_subplot( gs[1:, 2 ] )  # second and third row, last-column
ax4 = fig.add_subplot( gs[2,  0 ] )  # third row, first column              
ax5 = fig.add_subplot( gs[2,  1 ] )  # third row, second column             
plt.show()


Others include pyplot.subplot2grid(), which which works something like:

fig = plt.figure()
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
ax2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
ax4 = plt.subplot2grid((3, 3), (2, 0))
ax5 = plt.subplot2grid((3, 3), (2, 1))

free-style axes

Figure.add_axes( (left,bottom, width,height) ) will let you anchor axis edges at arbitrary fractions of the overall figure width/height.

I've seen this used mainly for inlaid plots.

Spacing

Subplots are added with spacing.


You can control spacing around plots and between subplots (but little else) via subplots_adjust.

The default spacing is apparently equivalent to:

fig.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.9,  wspace=0.2, hspace=0.2)


...which is spacious for single plots, but may be a little cramped for grids, so you may care to use subplots_adjust yourself.


tight_layout

You might also first try fig.tight_layout(), which tries to reduce whitespace while being aware of specific components (artists(verify)).

It's a convenient call when it does what you want - though also also a bit of a hack that you may not want to rely on too much. Say, it considers ticklabels, axis labels, and titles overlap - but no other components that may be there.

See also:


Various backends lack a direct implementation of tight_layout, which is why if you ask for it you may see:

tight_layout : falling back to Agg renderer

It seems this isn't a warning or a change - it seems to mean matplotlib is redoing the plot in the Agg renderer purely to calculate the new positions (it needs to call get_renderer(), which not all backends provide), then and uses those positions in your actual requested backend(verify). So you can ignore this warning if you don't care about a little extra CPUwork(verify)



...and sometimes you want no space, e.g. I abuse imshow to visualize matrices, where axes are irrelevant.

Different kinds of plots

This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)

Often, it pays to search for galleries of examples and find someone who has tweaked something nicely for a specific purpose.


But to start listing things....

Basics

pyplot.plot - line plots, mostly example

pyplot.step - stepped lines example

pyplot.stem - stem/impulse-like example

pyplot.bar - bar graph example

barh vertial and horizontal bar graphs example

scatter - scatterplot example

pie - pie chart example


Statistics:

hist - histograms example

boxplot example

violinplot example


matshow example


Dense data or images:

imshow example

pyplot.acorr example

angle_spectrum, magnitude_spectrum, phase_spectrum example


specgram - STFT of a signal(verify) example


More specialized:

contour and contourf (filled) example example

streamplot streaming vector fields example


eventplot (a.k.a. spike raster, dot raster, or raster plot) example



Logarithmic

The often-more-flexible variant is to, after axis creation, is to use set_xscale and/or set_yscale, e.g.

import matplotlib, matplotlib.pyplot as plt


fig = plt.figure(figsize=(12, 6))


axis1 = fig.add_subplot(2,2, 1) 


axis2 = fig.add_subplot(2,2, 2) 
axis2.set_xscale('log')


axis3 = fig.add_subplot(2,2, 3) 
axis3.set_yscale('log')


axis4 = fig.add_subplot(2,2, 4) 
axis4.set_xscale('log')
axis4.set_yscale('log')


Various other plots allow this, but may have their own footnotes, like a polar allows it on the y axis (outwards) but sensibly refuses on the x axis (what would that even mean?)


There are also semilogx, semilogy, and loglog, but they mostly are log variants specifically of line plots - in fact they just seem to be thin wrappers around plot() that call set_xscale and set_yscale (verify)

Polar

polar plot [1]

polar scatter [2]

Polar areas (bar/pie-like) [3]


3D plots

...or other slightly atypical projections will probably also need this somewhat longer style, e.g.

fig = plt.figure()
from mpl_toolkits.mplot3d import Axes3D # without this, '3d' below will be an unknown projection
ax = fig.add_subplot(111, projection='3d')


Some plots (e.g. scatterplot, quiver) translate well to 3D without much extra thought.


Others need care, or specific alternatives, or are 3D-only in the first place

scatter https://matplotlib.org/stable/gallery/mplot3d/scatter3d.html

quiver https://matplotlib.org/stable/gallery/mplot3d/quiver3d.html

wireframe: https://matplotlib.org/stable/gallery/mplot3d/wire3d.html

surface: https://matplotlib.org/stable/gallery/mplot3d/surface3d.html

voxels: https://matplotlib.org/stable/gallery/mplot3d/voxels.html


Or wider, more creatively:

See also: https://matplotlib.org/2.0.2/mpl_toolkits/mplot3d/tutorial.html#d-plots-in-3d

Returning questions

How do I set Y range?

ax.set_ylim(minval, maxval)

e.g.

ax.set_ylim( min(ary), 1.01*max(ary) )


In general you may have use for:

ax.set_xlim
ax.set_ylim
ax.set_zlim (for 3D plots)

Masking out data, missing data

How do I deal with dates?

Style notes

⚠ Hint
First check whether prettyplotlib does most of what you want


Drawing more

Legend

A legend will be added if you do

ax.legend()

Notes:

  • legend names come from the name= parameter on the command that settles what kind of plot the axis contains (e.g. plot())
  • legend() has a parameter called loc, to force the legend to a corner I figure is least likely to contain data.
default seems to be 'best' for axes, 'upper right' for figures(verify)
'best' tries to avoid drawing over data but isn't always clever, and is slow on things with lots of data
you can also use 'upper right', 'upper left', 'lower left', 'lower right', 'right', 'center left', 'center right', 'lower center', 'upper center', 'center'
  • You can also control how legend rows are formatted.
  • This picks up names from the label argument handed to functions like plot(),

though you can override this if you wish.

See https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.legend.html

Grid

Showninga grid at all:

ax.grid(True) 


Optional keyword arguments:

  • which: 'major', 'minor', or 'both'
  • various style, e.g.
color=’r’
linestyle=’-‘
linewidth=2
zorder=0

You can style major and minor differently like:

ax.grid(b=True, which='major', color='b', linestyle='-')   # solid blue
ax.grid(b=True, which='minor', color='r', linestyle='--')  # dashed red


You could also style the lines later, by fetching the objects via calls like

ax.get_xgridlines()
ax.get_ygridlines()


To change the interval of the grid, see the next section (tick labels)


See also:


for more control, see http://matplotlib.org/examples/axes_grid/demo_axes_grid.html

Custom tick labels

Amount, interval, and content of ticklabels


You can tell matplotlib the amount of ticks you want to see (minimum, maximum) and have it figure out how much exactly that means for your particular x and y range.

You can change that behaviour.

For example, if only integer labels make sense, you may care to do:

ax.xaxis.set_major_locator( matplotlib.ticker.MaxNLocator(integer=True) )


You can also change the minor tick behaviour

See also:



You can also force them at specific points. Note that this is only useful if the ranges are basically fixed.

ax.set_xticks( range(0, 100, 20) ) 
ax.set_xticks( range(0, 100, 5), minor=True  )


You can also change the way they are formatted.

For most types of data it's smart enough and you don't need to.

For one example where I used it: I e.g. made a data collection report PDF thing that by nature was never longer than a few days, so the default date formatting is overly verbose, and I prefer the still-flexible:

adf = matplotlib.dates.AutoDateFormatter( 
  matplotlib.dates.AutoDateLocator(minticks=5, maxticks=15), defaultfmt='%a %d\n%k:%M' )
adf.scaled = {  # if major tick interval is more than <key> days, call strftime with <value>
    1.0    : '%a %d\n%k:%M',          # > 1 day
    1./24. : '%a %d\n%k:%M',          # > 1 hour
    1. / (24. * 60.): '%H:%M:%S.%f',  # > 1 minute
}

axis.get_xaxis().set_major_formatter( adf )
# the day/hour one which prints something like:  Thu 17
#                                                 22:30





Size of tick labels

To set the default for all axes, see #Font_size

To alter for just one axis, try fontsize= on the axis call (often simpler), OR:

for tick in ax.xaxis.get_major_ticks():
    tick.label.set_fontsize(14)  # possibly make that mybasesize-2 or such


Rotation of tick labels

When you have a bunch of labels, or long labels, you may care to rotate them.

  • rotation='vertical' may be simplest
  • you can also use arbitrary rotation, like rotation=45
  • with tightly packed labels (and particularly with arbitrary rotation) you may also care about where the label/rotation is anchored. Basically:
rotation_mode=anchor means it is first align according to ha and va, then rotate
rotation_mode=None (the default) means first rotate, then align [4]
anchor is sometimes nicer with tigher-packed labels, the default sometimes looks more regular/spacious
see also [5]

For example, compare the following (and try swapping these ha values to see what goes wrong):

rotation=-45, rotation_mode='anchor', ha='left',  va='center'
rotation= 45, rotation_mode='anchor', ha='right', va='center'

Lines for indicating

This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)

Sometimes you want to draw on a plot, to point out thresholds, boundaries, specific values, etc.

e.g.

...and various others, since a lot of drawing can work in data coordinates.

http://stackoverflow.com/questions/16930328/vertical-horizontal-lines-in-matplotlib


Note that for more arbitrary lines, you could do:

plot((x1, x2), (y1, y2), 'k-')

...but this starts to fall under #Annotations

Plot markers (to distinguish series)

http://matplotlib.org/examples/lines_bars_and_markers/marker_reference.html


Annotations

This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)

In its basic form, annotations are placed text, optionally using arrows.


You can specify coordinates as

'data': same as the data
'figure fraction': fraction within the figure
'axes fraction': fraction within the axis
axisobj.transData: a fraction in another axis
'offset points': absolute offset in (verify)
figure points, figure pixels, axes points, axes pixels
polar
a 2-tuple, specifying x and y coordinate systems differently, e.g. ("data", "axes fraction")
matplotlib.patches.ConnectionPatch is sometimes easier to use, though
matplotlib.text.OffsetFrom(obj, (x,y))

See also things like [6]


Boxes around text happen when you specify bbox. The value to bbox is a dict that specifies how to draw it, a dict including things like:

boxstyle:"rarrow,pad=0.3"
fc:"cyan"
ec:"b"
lw:2

Note that one alternative is instantiating AnchoredText, which is how legends are made. You can get anchored things more generic than that.



Arrows (other than a bbox with larrow/rarrow/darrow for its shape) happen when you have an xy and an xytext: an arrow will be drawn from the latter to the former (and the text that is the first, non-keyword argument will also be drawn at the latter).

You can have them in different coordinate systems, even:

annotate('look at that',
  xy=(x1, y1),     xycoords='data',
  xytext=(x2, y2), textcoords='offset points' )


See also:

Second axis

hostaxis.twinx() gives you a new axis based on the host.

If you want a single legend, it seems better to have two derived axes and not use the host.


http://stackoverflow.com/questions/5484922/secondary-axis-with-twinx-how-to-add-to-legend

Drawing less

Removing a plot's text and tickmarks

Sometimes the values on an an axis are more distracting than useful, and you can

ax.set_xticks([])
ax.set_yticks([])

to remove just the ticks and labels, or

ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)

to also remove an axis label even if it was set

Removing the plot's box, background

To remove only the lines of the main plot box: these are called the spines, so you can e.g.:

axisobj.spines['top'].set_visible(False)
axisobj.spines['right'].set_visible(False)


The background color is the frame.

You can remove it with frameon=False to the figure
save() settings can have its own overriding defaults, so sometimes you may need to force e.g. transparent=True on that call

Lines and markers

https://matplotlib.org/stable/gallery/lines_bars_and_markers/linestyles.html

https://matplotlib.org/stable/gallery/lines_bars_and_markers/marker_reference.html


Colors

Can be one of:

  • Single letter:
'b' blue
'g' green
'r' red
'c' cyan
'm' magenta
'y' yellow
'k' black
'w' white
  • names (from these, e.g. 'forestgreen', seems to be the CSS set(verify))
  • float-as-string: gray shades / colormap, e.g '0.4'
  • CSS style: e.g. '#eeefff'
  • RGB tuple, e.g. (0.5, 0.5, 0.8)
  • RGBA tuple, e.g. (0.5, 0.5, 0.8, 0.2)


Changing line color

Basics:

  • Hand it into plot (c= or color=)
  • You can also alter the Line2D object returned by plt.plot()


Multi-colored lines:

Most solutions come down to plotting separate parts of the line, each with their own color.


There's:

not very readable, but more flexible in theory


  • figure out start and end indices in the data, and do a series of plot() calls, like:
plot(x[start:end], y[start:end], lw=2, c=segment_color[i]) 


  • numpy mask arrays, separate plot() calls
more readable if it's a single split based on x or y value.
The below example colors differently based on a threshold - so two segments. You'ld need to generalize it to be more flexibe
from matplotlib import pyplot
import numpy

x = numpy.array(range(50))
y = numpy.sin(x)
threshold=20
if threshold:
    before = (x <= threshold)   # note: assumes numpy, won't work when they're python arrays
    after  = (x >= threshold)
else:
    before = numpy.zeros(x.shape[0], dtype=bool)
    after  = numpy.ones(x.shape[0], dtype=bool)

fig, ax = pyplot.subplots()
ax.plot(x[before],y[before],  lw=2, color=(0.0,1.0,0, 1.0), label='data (avrot)')
ax.plot(x[after], y[after],   lw=1, color=(0.4,1.0,0, 0.5))
pyplot.show()

Font

This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)

Font size

Overall, and per type of thing

You can change de default (which defaults to 12) like:

ourfontsize = 10
matplotlib.pyplot.rcParams["font.size"] = ourfontsize

# The following are by default relative to font.size, but can be set to a fixed size
matplotlib.pyplot.rcParams['axes.titlesize']   = 'large'
matplotlib.pyplot.rcParams['axes.labelsize']   = 'medium'
matplotlib.pyplot.rcParams['figure.titlesize'] = 'medium'
matplotlib.pyplot.rcParams['legend.fontsize']  = 'large'
matplotlib.pyplot.rcParams['xtick.labelsize']  = 0.6*ourfontsize # if you want more control
matplotlib.pyplot.rcParams['ytick.labelsize']  = 0.6*ourfontsize
# (the last because you cannot hand in a scale, only a specific size)

These are factor scales. For reference (taken from font_manager.py):

'xx-small' : 0.579,
'x-small'  : 0.694,
'small'    : 0.833,
'smaller'  : 0.833,
'medium'   : 1.0,
'large'    : 1.200,
'larger'   : 1.2,
'x-large'  : 1.440,
'xx-large' : 1.728,


For more structural reuse, see styles: http://matplotlib.org/users/style_sheets.html#style-sheets


For the finest-grained (and fully-OO) control, either

  • use the fontsize argument on axis.set_title(), axis.set_xlabel(), axis.legend(), etc.
  • use setters, like
for tick in axis.get_xaxis().get_major_ticks():
    tick.label.set_fontsize(5)


http://matplotlib.org/examples/pylab_examples/fonts_demo.html

Custom font

This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)


See also:

Interaction

Changing the status bar

This is updated by the current axis's format_coord function. (The default function formats these vaues via the axis formatters)

It gets x and y, which are coordinates within the actual plot area, and should return a string.

def format_coord(x, y):
    centx,centy = 100,100 # assuming you know them for your data
    rad = math.sqrt( (centx-x)**2 + (centy-y)**2 )
    return ' radius=%.3f x=%4d y=%4d'%( rad, x,y )

axis.format_coord( show_txt )

I regularly find I want to use state beyond x,y -- e.g. fetching a value from the data. You can hardcode a reference to a global -- or put your plotting into classes to cleanly keep the state.

Performance

Picking

Interactive backends let you do things like

def onclick(event):
    print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
          (event.button, event.x, event.y, event.xdata, event.ydata))

cid = fig.canvas.mpl_connect('button_press_event', onclick)


You can make arbitrary artist objects report events when you click near enough.


See also: https://matplotlib.org/stable/users/explain/event_handling.html

On figure/axis reuse

If you create a figure, then display it, then create a figure, etc., you'll probably have run into:

RuntimeWarning: More than 20 figures have been opened.


If you went the pyplot route, you can use:

pyplot.close([figure]) # the current figure (default), by reference, index, name, or 'all'


However, you can be faster and more resource-efficient:

(pyplot.clf clears the current figure)
(pyplot.cla clears the current axis)
  • set_data() on an axis (faster - often fast enough for realtime animation)


Blocking and non-blocking plot windows

The following is irrelevant for the non-interactive backends, because they just create a file and move on.


The sensible cases

Use from an ipython/jupyter notebook

Entering %matplotlib inline within a notebook means from then on it will pick up on created plot objects, and render them as an inline image (static image in interactive context)

To get interaction with the plot, use:

  • %matplotlib nbagg (ipython 2)
  • %matplotlib notebook (since ipython 3)


From an interactive python prompt

Use ipython rather than plain python.

After a %matplotlib [7] it will notice when you produce a figure object(verify) and fires off a window for it. (Without an argument to the magic function it will choose a GUI backend for you. You can specify a specific one if you want.)


from one-off CLI scripts

If you have a script of the "gather data, show the results, done" sort, then it is actually useful to block program flow while the plots are shown. (in that it means you don't have to ensure some sort of threading, only to have to listen for the figure close to postpone the program's exit)

...which is more or less the default when you use just pyplot.show() with non-embedded interactive GUI backends: They'll create their own window with their own event loop, which is the thing that blocks until the plot window(s) it created are closed.


Plots with their own interaction

Everything you want to happen can be hooked into interactive events (or a timer), and thereby just the figure's mainloop.

See e.g. some examples here: http://matplotlib.org/examples/widgets/

You There are inbetweens with more control, if you don't mind coding for a specific backend, see e.g. the example here


Preprogrammed animations

Matplotlib has some built-in, on-a-timer style animation.

These are not interactive (although you can combine this, see the previous point).

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


Embedded in an existing GUI window

That is, you already created a GUI, and happen to want to put a plot somewhere in a window.

You would want to integrate it into your GUI program's event loop. There are examples, see http://matplotlib.org/examples/user_interfaces/

The interesting cases

from mostly-CLI programs (or the basic python shell)


I frequently write a script that gathers some data, shows it, and updates it later (Be it iterations done within the same program, or watching files, a database, or whatnot)

This means a few different details

  • leaving the originating program interactive
  • leaving the plot window interactive
  • ensuring draws-to-screen happen
  • having the originating program wait for the window to close before quitting itself

The exact solution depends on what exactly you are doing. (TODO: examples)


Interactive mode is a good part of the answer (but also a bit confusing, details vary a bit with backend, and parts are a bit experimental). Interactive mode refer more to how it leaves your shell than to the plot itself.(verify)

means a show() will not block
means things will not draw() on every state change (verify)

It does not directly imply that a GUI window will be independent and drawing. Says http://matplotlib.org/faq/usage_faq.html#what-is-interactive-mode

Use of an interactive backend (see What is a backend?) 
permits–but does not by itself require or ensure–plotting to the screen. 

Whether and when plotting to the screen occurs, 
and whether a script or shell session continues after a plot is drawn on the screen, 
depends on the functions and methods that are called,
and on a state variable that determines whether matplotlib is in “interactive mode”.

The default Boolean value is set by the matplotlibrc file, 
and may be customized like any other configuration parameter (see Customizing matplotlib). 
It may also be set via matplotlib.interactive(), 
and its value may be queried via matplotlib.is_interactive().

pyplot.pause:

If there is an active figure it will be updated and displayed,
and the GUI event loop will run during the pause.

If there is no active figure, or if a non-interactive backend
is in use, this executes time.sleep(interval).

This can be used for crude animation.


Notes so far:

  • setting interactive mode:
matplotlib.interactive(True) and/or pyplot.ion()
matplotlib.interactive(False) and/or pyplot.ioff()
toggling it in the middle of your program is probably more confusing than useful
  • in interactive mode, OO-style (or altering plot state manually) will not imply a draw() (pylab-style calls still do)
(seems to be for performance(verify))
so do an explicit pause() or draw()
  • calling draw() not actually force a draw on-screen (when and because the backend is separate from us), so...
  • pyplot.pause(0.0001) roughtly makes sure the redraw actually happens nowish
(apparently can be used instead of draw())
was meant to pace animations that are non-interactive, it just turns out we can abuse it.
is basically draw(), show(block=False), and runs a matplotlib-side event loop so that our interaction can make it to the GUI-side event loop(verify) (see also canvas.start_event_loop())
  • to avoid script-exit meaning closing everything
in quick and dirty plotting it's close enough to do pyplot.pause(10000000) (three months. Add more zeroes if you care)
show(block=True) will probably effectively be the same (verify)
  • (there are many footnotes that I don't necessarily mention here)


The problem cases

GUI window from GUI shells

That is, from IDEs like IDLE and such.

Short answer: Works around IPython because it specifically cooperates with matplotlib. But for most other things, the fact you now have two GUI-related event loops in the same interpreter(verify), which is messy at best.


Says http://matplotlib.org/users/shell.html#other-python-interpreters

Gui shells are at best problematic, because they have to run a mainloop,
but interactive plotting also involves a mainloop.  
Ipython has sorted all this out for the primary matplotlib backends.
There may be other shells and IDEs that also work with matplotlib in interactive mode,
but one obvious candidate does not: the python IDLE IDE is a Tkinter gui app
that does not support pylab interactive mode, regardless of backend.


matplotlib in browsers

mpld3 notes

This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)


Digging deeper, necessary hacks, lower level notes, etc.

Dates

This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)

The easiest way to deal with dates is to hand in datetime objects.

Matplotlib will do everything for you, primarily choosing an appropriate axis ticker formatter (AutoDateFormatter) (also convert the dates to its interal format).


When you want more manual or lower-level control (e.g. update the plot via set_data), then you probably have to deal with its internal format, possibly set a AutoDateFormatter yourself. There's a basic example above

(You can hand in numbers representing dates and e.g. use plot_date() over plot(), but there are no such alternatives for various other plot functions.)


There is:

  • matplotlib's internal format - floating point, days since 0001-01-01 00:00:00 (plus one)
you tend to not have to care about the actual values, until you care about interactive plots or such
  • datetime
matplotlib.dates.date2num takes a datetime and converts it to such a number
matplotlib.dates.num2date takes such a number and returns a datetime
(will handle a single value or a sequence)
  • unix timestamp
matplotlib.dates.epoch2num takes unix time
matplotlib.dates.num2epoch produces unix time
(will handle a single value or a sequence)
  • mx2num and num2mx


As always, keep on the watch for timezone stuff (e.g. when converting from datetime or mx). Google around.


See also:

Running from minimal environments (like a web server)

matplotlib will like to default to an interactive backend

To run without X, choose a backend that doesn't use one, before a call that implicitly chooses a backend for you (pylab, also pyplot).


Choices include Cairo (usually looks nicest), Agg, or things like ps or pdf, and more. For example:

import matplotlib
matplotlib.use('Cairo')


matplotlib wants a HOME directory, and it should be writeable.

This usually only matters when embedding in web environments (apache, mod_wsgi, CGI) where HOME is typically not set. You should alter this before you import matplotlib, like:

import os
env=os.environ
if 'HOME' not in env:
    env['HOME']='/tmp/' #a unique, empty subdir of /tmp would probably be slightly safer
import matplotlib

Writing to images

Backends will generally implement

matplotlib.pyplot.savefig

This takes

  • a path
  • a Python file-like object
various backends accept StringIO/BytesIO, some not though this may have changed by now
  • something backend-specific - e.g. [8] lets you write multi-page PDFs



Writing to images (memory-only)

This will be somewhat specific to the backend you prefer.


Cairo

Cairo can savefig() to a (c)StringIO objects, which is easier and more memory-efficient than going via an additional uncompressed raster.

By default it writes PNGs, and you can make it write other formats by handing along a format parameter to savefig, such as savefig(sio,format='PNG'), or one of 'PDF', 'PS', 'EPS', 'SVG'.


For example, we might wrap our plot-generating code like:

import matplotlib
from matplotlib.backends.backend_cairo import FigureCanvasCairo
fig = matplotlib.figure.Figure( figsize=(4,3), dpi=100 )
canvas = FigureCanvasCairo(fig)

# plot code
ax = fig.add_subplot(111)
X = [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]
Y = [ 0., 0.84,  0.90,  0.14, -0.75, -0.95, -0.27 ,  0.65 ,  0.985,  0.41 ]
ax.bar( X,Y )

output = StringIO.StringIO()
figure.savefig(output, format='PNG')
pngbytes = output.getvalue()
output.close()


Agg

You can ask Agg's figure/canvas for RGB pixel data (note: easily quite large).

Can be useful when you want to send it straight to PIL without an intermediate compressed form.


#If you do things at the figure level (i.e. pylab), get the underlying canvas, 
# and make sure the figure is drawn on the canvas (you may well get an empty image if it isn't)
canv = figure.canvas
canv.draw()
 
# Get the canvas' pixel size as a (x,y) tuple; you'll need to hand this to PIL along with the raw data.
# (You could probably do this at canvas level, but I prefer to use the underlying renderer so that you don't have to remember the dpi)
rend      = canv.get_renderer()
pixelsize = rend.get_canvas_width_height()
pizelsize = list(int(v)  for v in pixelsize) # apparently this became floats at some point, 
                                             # which the below does not like.
# Export raw image (also possible at canvas level) 
rgbImg    = rend.tostring_rgb()


# then e.g. read it into a PIL image. 
im=Image.fromstring('RGB', pixelsize, rgbImg) 
# You can use transparency, but ARGB is larger and takes a little more code to explain to PIL
 
pngdata = StringIO() #Make PIL save the image and store it into a string:
im.save(pngdata, 'PNG')



Backend list

These lists will change over time

Image backends:

  • Cairo
  • Agg
  • PS
  • PDF
  • SVG
  • EMF
  • pgf (referring to pgfplots(verify))

UI/Interactive backends:

  • TkAgg (probably the most portable)
  • WX
  • WXAgg
  • QtAgg
  • Qt4Agg
  • Qt5Agg
  • gdk
  • GTK
  • GTKCairo
  • GTKAgg
  • CocoaAgg
  • MacOSX (verify)
  • FltkAgg

Special/unsorted cases:

  • WebAgg - used in ipython(verify), also independently in browsers
  • nbAgg - used in ipython(verify)


User-made:

  • matascii


Notes:

  • Most backends can savefig() to a file.
  • The UI backends also react to show() and are often interactive.
  • Producing raster images for web use may be easiest with Cairo, because:
It allows StringIO-based saving, which most others do not
You do not have to switch backends to produce PNG, PDF, EPS, SVG -- which is useful because you cannot switch backends (an imposed restioction because not all backends react well to this) when you use persisting interpreters, which might apply to WSGI, mod_python and more.


See also:


On memory leaks

If you have long-running processes, it matters that state not cleaned up ends up being a memory leak.


As far as I can see, pylab doesn't even try to garbage collect.

The OO interface can, but there seems to be a known reference counting bug[10], and the basic solution is to close the figure:

matplotlib.pyplot.close(fig)

This isn't ideal when you intended to endlessly reuse the figure (e.g. use clf()).


See also:


Issues

I get an extra blank window

This article/section is a stub — probably a pile of half-sorted notes and is probably a first version, is not well-checked, so may have incorrect bits. (Feel free to ignore, or tell me)

Matplotlib tries to defer drawing work, but some functions work on the current state (either fundamentally and/or because of the way it's called).

For those functions, if a figure does not yet exist, it will make one.

For example, if

you create a figure,
then do a fig.tight_layout(),
then do a pyplot.show()

you will end up with an empty window, because tight_layout made one for you

you create a figure,
then do a pyplot.show()
then do a fig.tight_layout(),

it's fine.