Wednesday, 19 December 2007

Matplotlib tips

Recently I've been doing some plots with matplotlib. Although it has all gone well, it hasn't been entirely obvious how to do certain things. So, for the benefit of posterity (i.e. me in a month's time) I'm going to record some of my newly-gained knowledge here.
  • Colours: 'gray' is a colour too, 'k' is short for black.
  • Don't like the border on a legend? You need to stick the legend into a variable, "leg = legend()" for example, and say "leg.draw_frame(False)" (this pearl of wisdom comes courtesy of the matplotlib source code).
  • Want to make the legend text (and hence the legend) smaller? Try legend(prop={"size":10}).
  • To adjust the position or font of text in a plot, it may be easiest to save as SVG, open in Inkscape (a truly fantastic program), click on the image and hit 'Ungroup' a few times (CTRL+SHIFT+G), make the adjustments, resave the SVG, and finally Export to Bitmap (300 dpi is probably good enough).
  • Want two overlapping histograms on the same plot? Use the width keyword to hist() to set the bar widths to half the bin size. Also, catch the rectangles used to make the second histogram so that you can offset the bars by adding half the bin size:

    a, b, c = hist(x, bins, facecolor='k', width=width)

    for rect in c:
    rect.set_x(rect.get_x() + width)
    Update 11/05/2010: With current version of Matplotlib, you need to replace width=width with rwidth=0.5.
    You might also want to store the first rectangle of each histogram in a list and pass it as a first argument to legend.
  • To add a regression line between x=100 and x=600 (and get the R and significance values for free), try:

    from scipy import stats
    grad, inter, r, p, std_err = stats.linregress(x, y)
    plot((100, 600), [grad*x + inter for x in [100, 600]])
  • Line styles: "-" for solid, "--" for dashed, ":" for dotted. Unfortunately, the gaps in 'dashed' are too big. To define your own line style, you need to catch the line in a variable, e.g. "lines = plot(...)", and use lines[0].set_dashes((5,2)) for dashes of length 5 separated by gaps of 2 (unknown units).
  • Want Greek symbols and subscripts in axes labels? It worked for me on windows with something like the following:

    rc('text', usetex=True)
    xlabel(r"$\rho_{8.0}", fontsize="large")
    Note that the first time you run this on Windows, matplotlib might need to connect to the internet to download a needed component (MiKTeX). Also, when you make changes to the TeX stuff you might need to run your program twice to pick up on the changes.
    Update 03/01/2011: With current version of Matplotlib, a TeX parser is included.
    The Angstrom symbol can be include in a legend as follows: "RMSD ($\AA$)". For more information on available symbols, see the Matplotlib mathtext page.

6 comments:

Thabo Tharan said...

Hi,
thanks for your matplotlib tips
though I am a computer science student, the tips are very helpful.

I too had these difficulties in the past and now I how to tackle them.

Cheers,
Thabotharan,
Uppsala, Sweden.

joon said...

Thanks for this. Actually you don't have to do "leg = legend()" and then "leg.draw_frame(False)" You can just do "legend().draw_frame(False)" as well.

Rodolfo said...

Very useful tips! Regard to symbol in axes labels you can also use python source codes intead of TeX. It's good to get same font-type. For example, 'Temperature '+u'\u2103' (= "Temperature °C"). I take the codes searching here:
http://www.fileformat.info/info/unicode/char/search.htm
and using the "python source code" listed on "Encodings" table.

Vincent Noel said...

You can also use

legend(..., frame_on=False)

which is a little more concise.

Mark said...

None of the methods described in the comments seem to work for me to remove the border around the legend. "draw_frame(False)" gives an error that the legend has no such method. Adding "frameon=False" (I've also tried every other permutation I've found online e.g. "frame_on") gives an error saying that "frameon" is an unexpected keyword. Whatever I am doing wrong must be very obvious. Any ideas?

baoilleach said...

It all depends on the version of Matplotlib. If you look at the docs for the latest version (here), you will see that it lists the "frameon" keyword.