making multi panel figures

I have about 140 lines of code that makes a map. I’d like to turn it into a program which makes a multiple panel (map) figure. I understand that subplot will help to do this. Ideally I would like the 140 lines to be like a subroutine called in a loop. In the current code there are two variable which would be passed to the subroutine, thetitle and ncfile. These are only two things different for each panel. So something like:

do irow = 1, 3

do icolumn = 1, 3

  call mapping code (thetitle,ncfile)

enddo

enddo

Here is the code. If the above method is not possible, I assume I’ll need to repeat the 140 lines N times, where N is the number of panels.

TIA

Mike

···

############################################################################

verbose=0 #verbose=2 says a bit more

import sys,getopt

from mpl_toolkits.basemap import Basemap, shiftgrid, cm
#from netCDF3 import Dataset as NetCDFFile
from mpl_toolkits.basemap import NetCDFFile
from pylab import *
#from matplotlib.mlab import csv2rec

alloptions, otherargs= getopt.getopt(sys.argv[1:],‘ro:p:X:Y:v:t:l:u:n:’) # note the : after o and
p
proj=‘lam’
#plotfile=None
#plotfile=‘testmap2.png’
usejetrev=False
colorbounds=[None,None]
extratext=""
xvar=None
yvar=None
thevar=None

Here set map title and the file containing gridded data to plot

thetitle=‘Map Title’
ncfile = NetCDFFile(‘simple_xy.nc’, ‘r’) # Here’s filename

therec=None
thelev=None
cbot=None
ctop=None
startlon=-180 #default assumption for starting longitude
for theopt,thearg in alloptions:
print theopt,thearg
if theopt==’-o’: # -o needs filename after it, which is now thearg
plotfile=thearg
elif theopt==’-p’:
proj=thearg
elif theopt==’-X’:
xvar=thearg
elif theopt==’-Y’:

yvar=thearg
elif theopt=='-v':
    thevar=thearg
elif theopt=='-t':
    thetitle=thearg
elif theopt=='-l':
    cbot=thearg
elif theopt=='-u':
    ctop=thearg
elif theopt=='-n':
    therec=thearg
elif theopt=='-m':
    thelev=thearg
elif theopt=='-r':
    usejetrev=True
else: #something went wrong
    print "hmm, what are these??? ", theopt, thearg
    sys.exit()

print “\nPlotting, please wait…maybe more than 10
seconds”
if proj==‘lam’: #Lambert Conformal
m = Basemap(llcrnrlon=-80.6,llcrnrlat=38.4,urcrnrlon=-66.0,urcrnrlat=47.7,
resolution=‘l’,area_thresh=1000.,projection=‘lcc’,
lat_1=65.,lon_0=-73.3)
xtxt=200000. #offset for text
ytxt=200000.
parallels = arange(38.,48.,2.)
meridians = arange(-80.,-64.,2.)
else: #cylindrical is default

m = Basemap(llcrnrlon=-180.,llcrnrlat=-90,urcrnrlon=180.,urcrnrlat=90.,\

resolution=‘c’,area_thresh=10000.,projection=‘cyl’)

 m =

Basemap(llcrnrlon=startlon,llcrnrlat=-90,urcrnrlon=startlon+360.,urcrnrlat=90.,
resolution=‘c’,area_thresh=10000.,projection=‘cyl’)
xtxt=1.
ytxt=0.
parallels = arange(-90.,90.,30.)
if startlon==-180:
meridians = arange(-180.,180.,60.)
else:
meridians = arange(0.,360.,60.)

if verbose>1: print m.doc
xsize = rcParams[‘figure.figsize’][0]
fig=figure(figsize=(xsize,m.aspect*xsize))
#ax = fig.add_axes([0.08,0.1,0.7,0.7],axisbg=‘white’)
ax = fig.add_axes([0.07,0.00,0.86,1.0],axisbg=‘white’)

make a pcolor plot.

#x, y = m(lons, lats)
#p = m.pcolor(x,y,maskdat,shading=‘flat’,cmap=cmap)
#clim(*colorbounds)

axes units units are left, bottom, width,

height
#cax = axes([0.85, 0.1, 0.05, 0.7]) # colorbar axes for map w/ no graticule
#cax = axes([0.88, 0.1, 0.06, 0.81]) # colorbar axes for map w/ graticule

axes(ax) # make the original axes current again

######### Plot symbol at station locations #################

draw coastlines and political boundaries.

m.drawcoastlines()
m.drawcountries()
m.drawstates()

draw parallels and meridians.

label on left, right and bottom of map.

#m.drawparallels(parallels,labels=[1,0,0,0])
#m.drawmeridians(meridians,labels=[1,1,0,1])

if not thetitle:
title(thevar+extratext)
else:
title(thetitle)

#data = csv2rec(‘latlon_GHCN.txt’,delimiter=’ ',names=[‘lat’,‘lon’])
#for i in range(len(data)):

x,y=m(data[‘lon’][i],data[‘lat’][i]) # Translate to basemap (Lambert) coordinate

space

ax.text(x,y,’.’)

plot(x,y,color=‘black’,marker=’.’,markersize=6.0)

xpt,ypt = m(-75.0,43.0)
ax.text(xpt,ypt,’*’)

#if plotfile:

savefig(plotfile, dpi=72, facecolor=‘w’, bbox_inches=‘tight’, edgecolor=‘w’, orientation=‘portrait’)

#else:

show()

#plt.savefig(‘map.png’)
plt.savefig(‘map.eps’)
show()

comment show to mass produce

Just a quick suggestion for cleaning up your code, please look into the argparse module to make command-line parsing so much easier to use.

http://docs.python.org/dev/library/argparse.html

Ben Root

Just a quick suggestion for cleaning up your code, please look into the argparse module to make command-line parsing so much easier to use.

http://docs.python.org/dev/library/argparse.html

Ben Root

Command line parsing? I’m new to python and matplotlib and was given this code by a colleague. I’ve managed to make simple modifications. Don’t know enough yet to use the examples in the link you provide.

I’ve simplified my code as much as possible. The first 50 lines make a map. The code below that makes a 4 panel graphic. Seems that plt.figure invokes a new plot window. But plt.semilogy, plt.loglog, and plt.hist do not, keeping the panels in a single window. This is what I need. Trying now to figure out how to transfer the fig=plt.figure line into the subplot section, without popping up a new window.

Mike

verbose=0 #verbose=2 says a bit more

import sys,getopt

from mpl_toolkits.basemap import Basemap, shiftgrid, cm
#from netCDF3 import Dataset as NetCDFFile
from mpl_toolkits.basemap import NetCDFFile
from pylab import *
import matplotlib.pyplot as
plt

#fg = plt.figure(figsize=(10,8))
#adj = plt.subplots_adjust(hspace=0.4,wspace=0.4)
#sp = plt.subplot(2,2,1)

Here set map title and the file containing gridded data to plot

thetitle=‘Map #1
ncfile = NetCDFFile(‘simple_xy.nc’, ‘r’) # Here’s filename

startlon=-180 #default assumption for starting longitude

m = Basemap(llcrnrlon=-80.6,llcrnrlat=38.4,urcrnrlon=-66.0,urcrnrlat=47.7,
resolution=‘l’,area_thresh=1000.,projection=‘lcc’,
lat_1=65.,lon_0=-73.3)
xtxt=200000. #offset for text
ytxt=200000.
parallels = arange(38.,48.,2.)
meridians = arange(-80.,-64.,2.)

if verbose>1: print m.doc
xsize = rcParams[‘figure.figsize’][0]
fig=plt.figure(figsize=(xsize,m.aspect*xsize))
ax =
fig.add_axes([0.07,0.00,0.86,1.0],axisbg=‘white’)
axes(ax) # make the original axes current again

draw coastlines and political boundaries.

m.drawcoastlines()
m.drawcountries()
m.drawstates()

if not thetitle:
title(thevar+extratext)
else:
title(thetitle)

#plt.show()

···

On 01/12/12 Ben Root wrote:

##########################################################################

Example: http://physics.nmt.edu/~raymond/software/python_notes/paper004.html

sp = plt.subplot(2,2,1)
x = linspace(0,10,101)
y = exp(x)
l1 = plt.semilogy(x,y,color=‘m’,linewidth=2)

sp = plt.subplot(2,2,2)
y = x**-1.67
l1 = plt.loglog(x,y)

sp = plt.subplot(2,2,3)
x = arange(1001)
y = mod(x,2.87)
l1 = plt.hist(y,color=‘r’,rwidth = 0.8)

sp = plt.subplot(2,2,4)
l1 =
plt.hist(y,bins=25,normed=True,cumulative=True,orientation=‘horizontal’)

plt.show()
#plt.savefig(‘map.eps’)

Just a quick suggestion for cleaning up your code, please look into the argparse module to make command-line parsing so much easier to use.

Ok, a quick crash course:

A “figure” can hold one or more “axes” (or subplots). When using “plt”, you can choose to make figures explicitly with the “fig = plt.figure()” command or not. The same is true for axes objects. If you call a command that needs a figure and/or an axes object to have been made and they don’t exist, then they are made for you automatically. Otherwise, the most recently accessed figure/axes are assumed. This is why plt.hist(), plt.semilog() and others are not creating a new figure window if one already existed.

Anyway, for your code, you do not want to bring in the plt.figure() call into the subploting section. The example you were given takes advantage of pyplot’s implicit syntax (where it is implicitly assumed which axes/figure object you are using). However, I personally do not like that approach, and instead decided to show you an explicit style. I created a foobar() function that takes a blank figure object and other parameters, creates the four subplot axes and performs drawing on each of them. Note that the title is for the subplot, not for the figure. If you want a title for the figure above all the other subplots, use “fig.suptitle()”. I then have a loop where a figure is created each time, the foobar() function acts on that figure, saved and then cleared before the next iteration.

Note, I noticed you had “plt.show()” commented out before the call to “plt.savefig()”. Usually, you will want savefig() to come before show() because closing the figure window will destroy the figure object, resulting in a blank figure to save if done afterwards.

I hope this is helpful!
Ben Root

verbose=0 #verbose=2 says a bit more

import sys,getopt

from mpl_toolkits.basemap import Basemap, shiftgrid, cm
#from netCDF3 import Dataset as NetCDFFile

from mpl_toolkits.basemap import NetCDFFile
from pylab import *
import matplotlib.pyplot as plt

Here set map title and the file containing gridded data to plot

thetitle=‘Map #1

ncfile = NetCDFFile(‘simple_xy.nc’, ‘r’) # Here’s filename

startlon=-180 #default assumption for starting longitude

m = Basemap(llcrnrlon=-80.6,llcrnrlat=38.4,urcrnrlon=-66.0,urcrnrlat=47.7,
resolution=‘l’,area_thresh=1000.,projection=‘lcc’,\

         lat_1=65.,lon_0=-73.3)

xtxt=200000. #offset for text
ytxt=200000.
parallels = arange(38.,48.,2.)
meridians = arange(-80.,-64.,2.)

if verbose>1: print m.doc
xsize = rcParams[‘figure.figsize’][0]

for fig_index in range(140) :
fig=plt.figure(figsize=(xsize,m.aspect*xsize))
fig.subplots_adjust(hspace=0.4, wspace=0.4)

if not thetitle :
    title = thevar + extratext
else :

    title = thetitle

foobar(fig, m, title)

fig.savefig("map_%d.eps" % fig_index)
plt.clf()   # Clears the figure object

def foobar(fig, m, title) :

fig.subplots_adjust(hspace=0.4,wspace=0.4)
ax = fig.add_subplot(2,2,1)

draw coastlines and political boundaries.

m.drawcoastlines(ax=ax)
m.drawcountries(ax=ax)
m.drawstates(ax=ax)

title(title)

x = linspace(0,10,101)
y = exp(x)
l1 = ax.semilogy(x,y,color='m',linewidth=2)

ax = fig.add_subplot(2,2,2)
y = x**-1.67
l1 = ax.loglog(x,y)

ax = fig.add_subplot(2,2,3)

x = arange(1001)
y = mod(x,2.87)
l1 = ax.hist(y,color=‘r’,rwidth = 0.8)

ax = fig.add_subplot(2,2,4)
l1 = plt.hist(y,bins=25,normed=True,cumulative=True,orientation=‘horizontal’)

···

On Thu, Jan 12, 2012 at 2:20 PM, Michael Rawlins <rawlins02@…9…> wrote:

On 01/12/12 Ben Root wrote:

Just a quick suggestion for cleaning up your code, please look into the argparse module to make command-line parsing so much easier to use.

http://docs.python.org/dev/library/argparse.html

Ben Root

Command line parsing? I’m new to python and matplotlib and was given this code by a colleague. I’ve managed to make simple modifications. Don’t know enough yet to use the examples in the link you provide.

I’ve simplified my code as much as possible. The first 50 lines make a map. The code below that makes a 4 panel graphic. Seems that plt.figure invokes a new plot window. But plt.semilogy, plt.loglog, and plt.hist do not, keeping the panels in a single window. This is what I need. Trying now to figure out how to transfer the fig=plt.figure line into the subplot section, without popping up a new window.

Mike

Ok, a quick crash course:

A “figure” can hold one or more “axes” (or subplots). When using “plt”, you can choose to make figures explicitly with the “fig = plt.figure()” command or not. The same is true for axes objects. If you call a command that needs a figure and/or an axes object to have been made and they don’t exist, then they are made for you automatically. Otherwise, the most recently accessed figure/axes are assumed. This is why plt.hist(), plt.semilog() and others are not creating a new figure window if one already existed.

Anyway, for your code, you do not want to bring in the plt.figure() call into the subploting section. The example you were given takes advantage of pyplot’s implicit syntax (where it is implicitly assumed which axes/figure object you are using). However, I personally do not like that approach, and instead decided to show you an explicit style. I created a foobar() function that takes a blank figure object and other parameters, creates the four subplot axes and performs drawing on each of them. Note that the title is for the subplot, not for the figure. If you want a title for the figure above all the other subplots, use “fig.suptitle()”. I then have a loop where a figure is created each time, the foobar() function acts on that figure, saved and then cleared before the next iteration.

Note, I noticed you had “plt.show()” commented out before the call to “plt.savefig()”. Usually, you will want savefig() to come before show() because closing the figure window will destroy the figure object, resulting in a blank figure to save if done afterwards.

I hope this is helpful!
Ben Root

OK starting to make sense. Yes very helpful. I think what’s you’ve set up might work, provided I can pass a filename for data into the function.

At the moment I’m getting an error:

NameError: name ‘foobar’ is not defined

for the line with: foobar(fig, m, title)

Mike

···

On 01/12/12 Ben Root wrote:

On Thu, Jan 12, 2012 at 2:20 PM, Michael Rawlins <rawlins02@…9…> wrote:

On 01/12/12 Ben Root wrote:

Just a quick suggestion for cleaning up your code, please look into the argparse module to make command-line parsing so much easier to use.

argparse — Parser for command-line options, arguments and sub-commands — Python 3.13.0a2 documentation

Ben Root

Command line parsing? I’m new to python and matplotlib and was given this code by a colleague. I’ve managed to make simple modifications. Don’t know enough yet to use the examples in the link you provide.

I’ve simplified my code as much as possible. The first 50 lines make a map. The code below that makes a 4 panel graphic. Seems that plt.figure invokes a new plot window. But plt.semilogy, plt.loglog, and plt.hist do not, keeping the panels in a single window. This is what I need. Trying now to figure out how to transfer the fig=plt.figure line into the subplot section, without popping up a new window.

Mike

My bad… I put the declaration of the foobar() function after it is called in the script. This isn’t an issue if they are in separate scopes, but because “def foobar” is in the same scope as the call to it, it must have already been declared before it gets called. Just move that function to the area after all the imports.

Ben Root

···

On Thu, Jan 12, 2012 at 4:00 PM, Michael Rawlins <rawlins02@…2642…> wrote:

On 01/12/12 Ben Root wrote:

On Thu, Jan 12, 2012 at 2:20 PM, Michael Rawlins <rawlins02@…9…> wrote:

On 01/12/12 Ben Root wrote:

Just a quick suggestion for cleaning up your code, please look into the argparse module to make command-line parsing so much easier to use.

http://docs.python.org/dev/library/argparse.html

Ben Root

Command line parsing? I’m new to python and matplotlib and was given this code by a colleague. I’ve managed to make simple modifications. Don’t know enough yet to use the examples in the link you provide.

I’ve simplified my code as much as possible. The first 50 lines make a map. The code below that makes a 4 panel graphic. Seems that plt.figure invokes a new plot window. But plt.semilogy, plt.loglog, and plt.hist do not, keeping the panels in a single window. This is what I need. Trying now to figure out how to transfer the fig=plt.figure line into the subplot section, without popping up a new window.

Mike

Ok, a quick crash course:

A “figure” can hold one or more “axes” (or subplots). When using “plt”, you can choose to make figures explicitly with the “fig = plt.figure()” command or not. The same is true for axes objects. If you call a command that needs a figure and/or an axes object to have been made and they don’t exist, then they are made for you automatically. Otherwise, the most recently accessed figure/axes are assumed. This is why plt.hist(), plt.semilog() and others are not creating a new figure window if one already existed.

Anyway, for your code, you do not want to bring in the plt.figure() call into the subploting section. The example you were given takes advantage of pyplot’s implicit syntax (where it is implicitly assumed which axes/figure object you are using). However, I personally do not like that approach, and instead decided to show you an explicit style. I created a foobar() function that takes a blank figure object and other parameters, creates the four subplot axes and performs drawing on each of them. Note that the title is for the subplot, not for the figure. If you want a title for the figure above all the other subplots, use “fig.suptitle()”. I then have a loop where a figure is created each time, the foobar() function acts on that figure, saved and then cleared before the next iteration.

Note, I noticed you had “plt.show()” commented out before the call to “plt.savefig()”. Usually, you will want savefig() to come before show() because closing the figure window will destroy the figure object, resulting in a blank figure to save if done afterwards.

I hope this is helpful!
Ben Root

OK starting to make sense. Yes very helpful. I think what’s you’ve set up might work, provided I can pass a filename for data into the function.

At the moment I’m getting an error:

NameError: name ‘foobar’ is not defined

for the line with: foobar(fig, m, title)

Mike

My bad… I put the declaration of the foobar() function after it is called in the script. This isn’t an issue if they are in separate scopes, but because “def foobar” is in the same scope as the call to it, it must have already been declared before it gets called. Just move that function to the area after all the imports.

Ben Root

Thanks for the help. Code throwing another error:

Traceback (most recent call last):
File “panels_testingNEW4.py”, line 69, in
foobar(fig, m, title)
File “panels_testingNEW4.py”, line 23, in foobar
title(title)
TypeError: ‘str’ object is not callable

···

On 01/12/12 Ben Root wrote:

On Thu, Jan 12, 2012 at 4:00 PM, Michael Rawlins <rawlins02@…9…> wrote:

On 01/12/12 Ben Root wrote:

On Thu, Jan 12, 2012 at 2:20 PM, Michael Rawlins <rawlins02@…9…> wrote:

On 01/12/12 Ben Root wrote:

Just a quick suggestion for cleaning up your code, please look into the argparse module to make command-line parsing so much easier to use.

argparse — Parser for command-line options, arguments and sub-commands — Python 3.13.0a2 documentation

Ben Root

Command line parsing? I’m new to python and matplotlib and was given this code by a colleague. I’ve managed to make simple modifications. Don’t know enough yet to use the examples in the link you provide.

I’ve simplified my code as much as possible. The first 50 lines make a map. The code below that makes a 4 panel graphic. Seems that plt.figure invokes a new plot window. But plt.semilogy, plt.loglog, and plt.hist do not, keeping the panels in a single window. This is what I need. Trying now to figure out how to transfer the fig=plt.figure line into the subplot section, without popping up a new window.

Mike

Ok, a quick crash course:

A “figure” can hold one or more “axes” (or subplots). When using “plt”, you can choose to make figures explicitly with the “fig = plt.figure()” command or not. The same is true for axes objects. If you call a command that needs a figure and/or an axes object to have been made and they don’t exist, then they are made for you automatically. Otherwise, the most recently accessed figure/axes are assumed. This is why plt.hist(), plt.semilog() and others are not creating a new figure window if one already existed.

Anyway, for your code, you do not want to bring in the plt.figure() call into the subploting section. The example you were given takes advantage of pyplot’s implicit syntax (where it is implicitly assumed which axes/figure object you are using). However, I personally do not like that approach, and instead decided to show you an explicit style. I created a foobar() function that takes a blank figure object and other parameters, creates the four subplot axes and performs drawing on each of them. Note that the title is for the subplot, not for the figure. If you want a title for the figure above all the other subplots, use “fig.suptitle()”. I then have a loop where a figure is created each time, the foobar() function acts on that figure, saved and then cleared before the next iteration.

Note, I noticed you had “plt.show()” commented out before the call to “plt.savefig()”. Usually, you will want savefig() to come before show() because closing the figure window will destroy the figure object, resulting in a blank figure to save if done afterwards.

I hope this is helpful!
Ben Root

OK starting to make sense. Yes very helpful. I think what’s you’ve set up might work, provided I can pass a filename for data into the function.

At the moment I’m getting an error:

NameError: name ‘foobar’ is not defined

for the line with: foobar(fig, m, title)

Mike

Oh, right, the creation of the “title” variable got rid of the existing function. Just do “ax.set_title(title)” instead.

Ben Root

···

On Thu, Jan 12, 2012 at 4:32 PM, Michael Rawlins <rawlins02@…2642…> wrote:

On 01/12/12 Ben Root wrote:

On Thu, Jan 12, 2012 at 4:00 PM, Michael Rawlins <rawlins02@…9…> wrote:

On 01/12/12 Ben Root wrote:

On Thu, Jan 12, 2012 at 2:20 PM, Michael Rawlins <rawlins02@…9…> wrote:

On 01/12/12 Ben Root wrote:

Just a quick suggestion for cleaning up your code, please look into the argparse module to make command-line parsing so much easier to use.

http://docs.python.org/dev/library/argparse.html

Ben Root

Command line parsing? I’m new to python and matplotlib and was given this code by a colleague. I’ve managed to make simple modifications. Don’t know enough yet to use the examples in the link you provide.

I’ve simplified my code as much as possible. The first 50 lines make a map. The code below that makes a 4 panel graphic. Seems that plt.figure invokes a new plot window. But plt.semilogy, plt.loglog, and plt.hist do not, keeping the panels in a single window. This is what I need. Trying now to figure out how to transfer the fig=plt.figure line into the subplot section, without popping up a new window.

Mike

Ok, a quick crash course:

A “figure” can hold one or more “axes” (or subplots). When using “plt”, you can choose to make figures explicitly with the “fig = plt.figure()” command or not. The same is true for axes objects. If you call a command that needs a figure and/or an axes object to have been made and they don’t exist, then they are made for you automatically. Otherwise, the most recently accessed figure/axes are assumed. This is why plt.hist(), plt.semilog() and others are not creating a new figure window if one already existed.

Anyway, for your code, you do not want to bring in the plt.figure() call into the subploting section. The example you were given takes advantage of pyplot’s implicit syntax (where it is implicitly assumed which axes/figure object you are using). However, I personally do not like that approach, and instead decided to show you an explicit style. I created a foobar() function that takes a blank figure object and other parameters, creates the four subplot axes and performs drawing on each of them. Note that the title is for the subplot, not for the figure. If you want a title for the figure above all the other subplots, use “fig.suptitle()”. I then have a loop where a figure is created each time, the foobar() function acts on that figure, saved and then cleared before the next iteration.

Note, I noticed you had “plt.show()” commented out before the call to “plt.savefig()”. Usually, you will want savefig() to come before show() because closing the figure window will destroy the figure object, resulting in a blank figure to save if done afterwards.

I hope this is helpful!
Ben Root

OK starting to make sense. Yes very helpful. I think what’s you’ve set up might work, provided I can pass a filename for data into the function.

At the moment I’m getting an error:

NameError: name ‘foobar’ is not defined

for the line with: foobar(fig, m, title)

Mike

My bad… I put the declaration of the foobar() function after it is called in the script. This isn’t an issue if they are in separate scopes, but because “def foobar” is in the same scope as the call to it, it must have already been declared before it gets called. Just move that function to the area after all the imports.

Ben Root

Thanks for the help. Code throwing another error:

Traceback (most recent call last):
File “panels_testingNEW4.py”, line 69, in
foobar(fig, m, title)

File “panels_testingNEW4.py”, line 23, in foobar
title(title)
TypeError: ‘str’ object is not callable