Subplots from existing figures

Hello,

I have some functions that produce various figures. This is usually
done by calling figure(), then the plot function (eg bar). I save the
figure object and pass it out.

I would like to be able to collect several figure objects from such
functions and collect them as subplots in a single figure for easy
printing, comparison. I don't want to change the functions that create
them though, because I will also want to view them individually in the
future.

So given a load of figure objects, how can I make a new figure with
each subplot one of the existing figures? (I hope this is clear). Does
it matter if the original figure object has been closed? I'm having a
look through the help to see if I could find it but it's taking a bit
of time and I thought it's probably quite easy if your more familiar
with the object structure of matplotlib.

Thanks

Robin

I'm afraid I haven't been able to make very much progress with this on
my own. I tried calling get_children on the figure objects I have, and
then tried calling set_axes with the subplot of a new figure on each
of the children in the hope that this would bind them over, but after
playing a bit nothing I've tried seems to work.

Is it possible to do this, or even if it is is it perhaps too
complicated/involved to be worth while? (I had hoped it would be
relatively straightforward once I found the right combination of
get/set functions to move the plot objects over to the new subplot).

Thanks,

Robin

···

On Feb 15, 2008 12:04 PM, Robin <robince@...287...> wrote:

Hello,

I have some functions that produce various figures. This is usually
done by calling figure(), then the plot function (eg bar). I save the
figure object and pass it out.

I would like to be able to collect several figure objects from such
functions and collect them as subplots in a single figure for easy
printing, comparison. I don't want to change the functions that create
them though, because I will also want to view them individually in the
future.

So given a load of figure objects, how can I make a new figure with
each subplot one of the existing figures? (I hope this is clear). Does
it matter if the original figure object has been closed? I'm having a
look through the help to see if I could find it but it's taking a bit
of time and I thought it's probably quite easy if your more familiar
with the object structure of matplotlib.

Robin,

I'm not sure I understand what you want to do; is it to recycle axes objects that were originally in one or more figures and put them in another figure? It looks like that might be doable if you create the new figure, then for each axes call ax.set_figure(newfig), and then for each axes call newfig.add_axes(ax). I would be not the least surprised if all this failed, though.

Eric

Robin wrote:

···

On Feb 15, 2008 12:04 PM, Robin <robince@...287...> wrote:

Hello,

I have some functions that produce various figures. This is usually
done by calling figure(), then the plot function (eg bar). I save the
figure object and pass it out.

I would like to be able to collect several figure objects from such
functions and collect them as subplots in a single figure for easy
printing, comparison. I don't want to change the functions that create
them though, because I will also want to view them individually in the
future.

So given a load of figure objects, how can I make a new figure with
each subplot one of the existing figures? (I hope this is clear). Does
it matter if the original figure object has been closed? I'm having a
look through the help to see if I could find it but it's taking a bit
of time and I thought it's probably quite easy if your more familiar
with the object structure of matplotlib.

I'm afraid I haven't been able to make very much progress with this on
my own. I tried calling get_children on the figure objects I have, and
then tried calling set_axes with the subplot of a new figure on each
of the children in the hope that this would bind them over, but after
playing a bit nothing I've tried seems to work.

Is it possible to do this, or even if it is is it perhaps too
complicated/involved to be worth while? (I had hoped it would be
relatively straightforward once I found the right combination of
get/set functions to move the plot objects over to the new subplot).

Thanks,

Robin

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users

I'm not sure I understand what you want to do; is it to recycle axes
objects that were originally in one or more figures and put them in
another figure?

Hi,

I have a number of figures created from functions like this:
fig1 = plot_fig1(data1)
fig2 = plot_fig2(data2)
etc.

Now instead of having two separate figure windows displaying the
plots, I would like them to appear as subplots in a new figure (single
window) and close the previous figures. Other than moving to the
subplot in a new figure, I would like everything about the plot to
remain the same (titles, labels, scale,ticks etc.)

I had imagined something like this would be possible:
newfig = figure()
sp1 = subplot(211)
for x in fig1.get_children():
   x.set_axes(sp1)

or similar, but it doesn't seem to work. Also, when trying something
like this, does it matter if the original figure windows are already
closed? (ie does the act of closing the window change the objects to
make them not show-able any more).

I'm not sure if I can do this reallocation by figure or axes, that is
what I was asking really.

It looks like that might be doable if you create the
new figure, then for each axes call ax.set_figure(newfig), and then for
each axes call newfig.add_axes(ax). I would be not the least surprised
if all this failed, though.

If I move the axes over as you describe, will this also bring the
plotted data, titles etc.? I will try and play around with this later
today. Would I first have to delete the existing axes created when I
create the subplot? Wouldn't that make a problem, since isn't it these
created axis that have the important subplot position properties (if I
just move over the existing axes how will the subplot placing work).
I imagine I might have to, copy over the axes from old figure, set
position and size of old axes to those of new subplot axes, copy over
all children (rectangles, text etc.)?

Alternatively if theres another way of achieving the same thing. I
thought I could modify the functions to take an optional axes
argument, so if not present they make a new figure, and if present
they use that:
fig = figure()
sp1 = subplot(211)
plot_data1(data1, axes=sp1)

but then I'm not sure how inside the function, I use plot commands
(bar, plot, imshow) on this provided subplot axis.

Thanks,

Robin

···

On Feb 18, 2008 4:44 AM, Eric Firing <efiring@...202...> wrote:

You will need to remove the axes from the original figure:

  oldfig.delaxes(ax)

and then add it to the new figure

  ax.set_figure(newfig)
  newfig.add_axes(ax)

If you are adding several, you may want to play with the axes position
or geometry (for subplots) ax.set_position and ax.change_geometry

And yes, the data, titles, labels, grids, ticks, etc will be brought
over. As Eric points out, this is an under-exercised part of the code
so something may break, though last time I tried it it worked.

JDH

···

On Feb 18, 2008 1:43 AM, Robin <robince@...287...> wrote:

If I move the axes over as you describe, will this also bring the
plotted data, titles etc.? I will try and play around with this later
today. Would I first have to delete the existing axes created when I
create the subplot? Wouldn't that make a problem, since isn't it these

Thanks, that allowed me to make some real progress, and worked to some
degree. Here is the code I am using:

figs = [x[0] for x in results]
oldaxs = [fig.get_children()[1] for fig in figs]
for fig, ax in zip(figs,oldaxs):
   fig.delaxes(ax)
   pl.close(fig)

newf = pl.figure()

for i,ax in enumerate(oldaxs):
   ax.set_figure(newf)
   newf.add_axes(ax)
   ax.change_geometry(2,2,i+1)

pl.show()

Unfortunately, there are some problems:
- while the subplots are correct, they don't resize when I resize the window
- the plots are bar graphs, only the first xtick label is there, the
others are missing
- top of the xaxis of the bottom row of plots overlaps with the bottom
of the x axis of the top row - generally it looks pretty bad

Since this approach is proving problematic, and sounds like it isn't
really supported anyway (I had imagined it would be relatively
straightforward) I think it would be better to try the other way
(passing optional axes argument to plot to if I want it in a
multi-subplot figure, otherwise just create a new figure). I would
appreciate some pointers on how to do this. ie:

fig = plot_data1(data1) # this should create a new figure obect and return it

newfig = figure()
sp1 = subplot(221)
plot_data1(data1, axes=sp1) # this should plot the data on the
provided subplot axes.

However I do not know how to make the bar, title, xlabel, xtick etc.
commands I am using from pylab within the function act on the
specified subplot axes (if present).
Is this possible?

Thanks,

Robin

···

On Feb 18, 2008 2:00 PM, John Hunter <jdh2358@...287...> wrote:

You will need to remove the axes from the original figure:

  oldfig.delaxes(ax)

and then add it to the new figure

  ax.set_figure(newfig)
  newfig.add_axes(ax)

If you are adding several, you may want to play with the axes position
or geometry (for subplots) ax.set_position and ax.change_geometry

And yes, the data, titles, labels, grids, ticks, etc will be brought
over. As Eric points out, this is an under-exercised part of the code
so something may break, though last time I tried it it worked.

figs = [x[0] for x in results]
oldaxs = [fig.get_children()[1] for fig in figs]
for fig, ax in zip(figs,oldaxs):
    fig.delaxes(ax)
    pl.close(fig)

Probably better:

oldaxs = []
for fig in figs:
    for ax in fig.axes:
        fig.delaxes(ax)
        oldaxs.append(ax)
    pl.close(fig)

Unfortunately, there are some problems:
- while the subplots are correct, they don't resize when I resize the window
- the plots are bar graphs, only the first xtick label is there, the
others are missing
- top of the xaxis of the bottom row of plots overlaps with the bottom
of the x axis of the top row - generally it looks pretty bad

Since this approach is proving problematic, and sounds like it isn't
really supported anyway (I had imagined it would be relatively

It's not that it isn't supported, it's just rarely used and so may
have slowly broken over time. It is useful, so It would be worthwhile
for us to fix it. Are you using 0.91.2 or svn?

straightforward) I think it would be better to try the other way
(passing optional axes argument to plot to if I want it in a
multi-subplot figure, otherwise just create a new figure). I would
appreciate some pointers on how to do this. ie:

You might consider something like

def myfunc(ax=None):
    if ax is None:
        fig = figure()
        ax = fig.add_subplot(111)
    ax.plot(something)

However I do not know how to make the bar, title, xlabel, xtick etc.
commands I am using from pylab within the function act on the
specified subplot axes (if present).
Is this possible?

ax.set_xlabel('mylabel')
ax.set_title('my title')
ax.grid(True)

etc... See help(matplotlib.axes.Axes)

JDH

···

On Feb 18, 2008 9:34 AM, Robin <robince@...287...> wrote:

Thanks very much for all your help.

It's not that it isn't supported, it's just rarely used and so may
have slowly broken over time. It is useful, so It would be worthwhile
for us to fix it. Are you using 0.91.2 or svn?

I am using 0.91.2 at the moment (on Mac OS X - I think this was the
downloaded binary). I could build from svn if you think it would make
a difference.

I guess for now I will use the function argument method, but I'm happy
to keep this code around and provide what feedback I can.

I suspect that if the subplot geometry was set to autoupdate when the
window is resized as it is for a fresh subplot, then the overlapping
axes might go away after a window resize. The missing xticks is a bit
of a mystery though - they all seems to be there, just not shown. I
tried setting them visible and redrawing but no luck.

Thanks again,

Robin

···

On Feb 18, 2008 3:44 PM, John Hunter <jdh2358@...287...> wrote:

Hi guys,

I’ve been wanting to do something similar as well. Create a figure from other figures. The function accepts a list of figures, and according to the ratio (rows to columns) creates a bigger figure of appropriate size and ‘copies’ the axes into the new figure.

I calculate the positions and size of the subplots in the new figure proportionately (ie. a value between 0 and 1), and then do a ax.set_position. This seems to be broken somehow, the axes still think the size of the figure they’re in is the original figure’s size. Even though I already moved them over to the new larger figure. I’ve tried to hack this by simply passing in values bigger then 1, but I can’t get it to work.

If I don’t make the new figure bigger everything works, though the labels get a bit cramped then.

If someone can have a look and give me some advise it would be very appreciated.

Regards,
Christiaan

···

import math
from matplotlib.figure import *

def multisubplot(figures=[], ratio=1.0, wspace=0.0, hspace=0.0):
fig = Figure()

n = len(figures)
if n < 1:
    fig.add_subplot(111)
    return fig

# calculate number of rows and columns
columns = int(math.ceil(sqrt(float(n)/(ratio))))
rows = int(math.ceil(float(n)/float(columns)))

# resize the new figure
w_inches = figures[0].get_size_inches()[0]*(columns)
h_inches = figures[0].get_size_inches()[1]*(rows)

fig.set_size_inches(w_inches, h_inches, forward=True)

print fig.get_size_inches()   

# calculate the spacing
wspace = wspace / (float(columns))
hspace = hspace / (float(rows))   

# calculate the l,b,w,h of all subplots
width = 1/float(columns) - wspace
height = 1/float(rows) - hspace
           
positions = []
for i in range(rows):
    for j in range(columns):
        positions.append([(j)*(width + wspace) + wspace/2, \
                (rows-i-1)*(height + hspace) + hspace/2 , \
                width, height])

# hack broken axes scaling
for pos in positions:
    print ''
    #pos[0] = pos[0] * (columns)
    #pos[1] = pos[1] * (rows)
    #pos[2] = pos[2] * (columns)
    #pos[3] = pos[3] * (rows)

print n
print 'columns', columns, 'rows', rows
print 'wspace', wspace, 'hspace', hspace
print 'width', width, 'height', height
for pos in positions:
    print pos
   
for i in range(rows):
    for j in range(columns):
        x = i*(columns) + j
        if x < n:               
            for ax in figures[x].axes:
                figures[x].delaxes(ax)
                ax.set_figure(fig)
                fig.add_axes(ax)
                ax.set_position(positions[x])   
           
return fig

#create some figures to pass to our function
pl = []
for i in range(13):
fig = Figure()
ax = fig.add_subplot(111)
ax.plot([1,math.sin(i),2])
pl.append(fig)

figsub = multisubplot(pl,ratio=1, wspace=0.1, hspace=0.1)

_______________________________–

On 18/02/2008, Robin <robince@…287…> wrote:

Thanks very much for all your help.

On Feb 18, 2008 3:44 PM, John Hunter <jdh2358@…287…> wrote:

It’s not that it isn’t supported, it’s just rarely used and so may

have slowly broken over time. It is useful, so It would be worthwhile
for us to fix it. Are you using 0.91.2 or svn?

I am using 0.91.2 at the moment (on Mac OS X - I think this was the
downloaded binary). I could build from svn if you think it would make

a difference.

I guess for now I will use the function argument method, but I’m happy
to keep this code around and provide what feedback I can.

I suspect that if the subplot geometry was set to autoupdate when the

window is resized as it is for a fresh subplot, then the overlapping
axes might go away after a window resize. The missing xticks is a bit
of a mystery though - they all seems to be there, just not shown. I
tried setting them visible and redrawing but no luck.

Thanks again,

Robin


This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft® Visual Studio 2008.

http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/


Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net

https://lists.sourceforge.net/lists/listinfo/matplotlib-users