Best practices for organizing plotting code?

I do computational science and I think I'm typical in that I've
accumulated a huge pile of code to post-process simulations and draw
plots. I think the number of lines of plotting code is now greater
than the number of lines in the actual simulation code... The problem
with plotting code is that so much of it has such a short
lifetime---you have an idea, spend some time writing code to draw the
relevant plot, then the plot isn't interesting and you delete the
code. Therefore there's little incentive to spend any time making
sure that plotting code is at all well-designed. Nevertheless, _some_
of it tends to live a long time and get ever more complicated---then
the lack of design becomes ever more painful as time goes on. You
simply don't know at the beginning which code will be thrown away and
which will live a long time.

Over the years I've developed my favorite way to organize my plotting
code but it's far from perfect and I'd love to gather ideas from the
MPL community. So, my current "design principles" are basically

1) Don't over-design. A simple system that's used consistently is
better than a half-implemented complicated system. Furthermore, most
plotting code gets thrown away, so keeping overhead down is one of the
primary considerations.

2) Keep computation separate from plotting wherever possible.
Therefore I have functions like "def compute_optical_depth(...)" that
compute the physical quantities to be plotted and "def
plot_optical_depth(...)" that handle everything about the visual
appearance of the plot. Then when I want to draw some other plot
involving optical depth, the calculation is neatly packaged into a

3) Keep annotation, axis labels, legends, etc, separate from the code
that actually draws the lines on the axes. This allows you to compose
plots to a certain extent. I often find myself saying "I want plot B
to look just like plot A but with this extra information, extra lines,
extra annotation, or whatever" If the function that draws plot A just
puts the data on the axes without axis labels, etc, then the function
that draws plot B can easily use it directly. If the function that
draws plot A _also_ draws a bunch of annotations and labels, then the
function that draws plot B must either get rid of them or hope they
still make sense in the new context.

4) Don't put clf() and cla() all over the place. When working
interactively, it's very tempting to put clf()'s into every function
that draws a plot in order to save a few keystrokes. However, plots
don't know the context into which they're being drawn, therefore they
have no authority to clear the screen. They may "own" the whole
plotting window, or they may be incorporated into a larger context.
The function that worries about axis labels, annotations, and titles
is allowed to call cla(). The function that worries about subplots is
allowed to call clf(). If you might use the code over a slow link
(e.g. connecting to a supercomputing site via residential DSL) then no
function should call draw() -- that's the user's job.

The upshot of these is that I end up with four layers of functions:

1) compute_physical_quantity(...): just handles numbers
2) draw_physical_quantity(...): has calls to pylab.plot() handling
colors, linestyles, etc, but not annotations
3) some_plot(...): has calls to draw_physical_quantity(),
some_related_physical_quantity(), along with axis labels, annotations,
legends, and pylab.cla()
4) some_figure(...): has multiple panels with calls to
pylab.subplot(), pylab.clf(), some_plot_a(), some_plot_b(), etc.

Sometimes layers 2 and 3 are combined because I'm lazy if layer 2
would really be just a single call to pylab.plot.

Please remember that I'm not writing these down because I think
they're so great that everyone needs to know about them. I'm hoping
that people will respond with much better ideas that I can adopt for