Custom-sized and spanning subplots

Hello, all. I’d like to add to matplotlib facilities for (a) conveniently specifying the relative sizes of subplots, and (b) creating subplots that span cells of the subplot grid. For example, to obtain a column of three subplots with the last one 50% taller than the other two, the user would provide [1, 1, 1.5] as a row size parameter, and matplotlib would calculate the appropriate axes positions. An example of spanning using the current machinery is the mri_with_eeg
example. Are these features of interest?

The calculation code was easy to write, but I’d like input on how it can best be implemented and accessed by users. One issue is where the relative sizes should be stored. Presently, while figures hold information about the margins around and between their subplots, as far as I can tell they are unaware of any grid structure among their subplots (although they do track as keys the arguments to add_subplot). Grid dimensions are instead stored with each axes along with the position of the axes within the grid. (The fact that the grid dimensions need not agree among all of the axes in a figure allows for layouts like mri_with_eeg. However, pushing that too far can yield unattractive results: The axes specified by “subplot(2, 2, 1); subplot(2, 4, 5); subplot(2, 4, 6)” are misaligned unless wspace = 0, a consequence of how the wspace parameter is interpreted. A new spanning mechanism should yield aligned axes.)

A second issue is that the numbers of rows and columns are overdetermined, being specified explicitly by the arguments to subplot and implicitly by the lengths of the lists containing the custom sizes.

Considering those issues, I thought of a few possible approaches for specifying custom row and column sizes:

A. Store the row and column sizes in the instance of figure.SubplotParams held in the figure’s subplotpars attribute. User code would look something like the following, using an example kwarg row_sizes:

fig = figure(subplotpars=mpl.figure.SubplotParams(row_sizes=[1, 1, 1.5]))

fig.add_subplot(3, 1, 1) # etc.

fig.subplots_adjust(row_sizes=[1, 1, 3]) # resize the subplots

There would be corresponding pyplot functionality. This approach allows the user to manage the sizes at the figure level (which I consider convenient), but only one set of sizes is accommodated. One way to handle the overspecified grid dimensions is a conflict-resolution scheme. For example, in axes.SubplotBase.update_params, if the number of rows (originating in the subplot call) matches the length of the figure’s row specifier, then honor the custom row sizes, and likewise for columns. Otherwise, either (a) raise an error, or (b) fall back to using equal rows (or columns) but send a message to verbose.report at some debug level. If no custom sizes are specified, then everything operates as now and no errors nor verbose.report messages will be generated. Because the numbers of rows and columns are stored with the axes, each axes would handle the conflict resolution independently of the other axes. Another scheme (which I like better) would introduce alternative subplot syntax such as add_subplot(row, col), where row and col are numbered Python-style from 0. That form would cause the subplot to inherit the numbers of rows and columns and the custom sizes in fig.subplotpars, whereas the current forms would use only equally-sized subplots. The new form also relieves the user of constructing the Matlab-style single subplot number.

B. Instead of the above, associate the size information with the axes. User code might resemble

fig = figure()

fig.add_subplot(2, 2, 1, col_sizes=[3, 1]) # ±—+ ++

fig.add_subplot(2, 2, 2, col_sizes=[3, 1]) # ±—+ ++

fig.add_subplot(2, 2, 3, col_sizes=[1, 3]) # ++ ±—+

fig.add_subplot(2, 2, 4, col_sizes=[1, 3]) # ++ ±—+

This continues the current ability to create different grids (different numbers of rows and columns, only now with different row and column sizes) within the same figure, but they’re managed at the axes level. Resizing in this approach would need to be performed with each affected axes, or a figure-level method could walk the child axes (possibly checking for matching numbers of rows and columns). This approach still overspecifies the numbers of rows and columns, and conflicts would need to be resolved somehow. With the above syntax, errors could be raised by subplot if its arguments disagree. Or, subplot could truncate or recycle the list of sizes to match the grid dimensions.

C. Create a new layout mechanism parallel to or containing the subplot mechanism. I can imagine such a mechanism handling nested grids or creating colorbar axes that adjust with their parent axes within a complex layout. Such a mechanism would be beyond what I can contribute now working alone, but I mention it essentially to ask whether anyone sees a need to build a new system instead of augmenting the current one.

While approach A appeals to me intuitively (in that it handles the sizes at the figure level), it differs from the current structure of matplotlib in which each axes independently maintains the dimensions of its subplot grid and its position within it. I have some concern that going in the direction of figure-level storage would involve structural tension unless a new system is implemented (approach C). So, unless there is a call for something dramatically different, I’m leaning toward approach B with some figure-level methods to facilitate convenient changes across multiple child axes. What do you folks think?

As for the approach for creating spanning subplots, I like extending the syntax of subplot along the lines of:

subplot(num_rows, num_cols, row, col)

# non-spanning; row and col are numbered Python-style from 0

subplot(num_rows, num_cols, (row_start, row_stop), (col_start, col_stop))

# spans row_start through row_stop - 1 (Python-style)
# and likewise for columns

How does that look?

This email ran long, but I didn’t want to propose significant changes to the matplotlib interface without first sharing some of the issues I’ve encountered and getting feedback about how to proceed.
Thanks.

Hi Stan,

You may want to have a look at the mplsizer MPL toolkit I wrote a long
time ago and have failed to properly advertise or maintain. But, it does
much of what you propose by emulating wx sizers for matplotlib. Anyhow,
this is available by svn checkout from

I'd be happy if you wanted to take over the project and push it forward,
but I also understand if you have other implementation ideas. (The wx
API is not to everyone's taste, for one thing.)

-Andrew

Stan West wrote:

···

Hello, all. I'd like to add to matplotlib facilities for (a)
conveniently specifying the relative sizes of subplots, and (b) creating
subplots that span cells of the subplot grid. For example, to obtain a
column of three subplots with the last one 50% taller than the other
two, the user would provide [1, 1, 1.5] as a row size parameter, and
matplotlib would calculate the appropriate axes positions. An example of
spanning using the current machinery is the mri_with_eeg
<http://matplotlib.sourceforge.net/examples/pylab_examples/mri_with_eeg.html&gt;
example. Are these features of interest?

The calculation code was easy to write, but I'd like input on how it can
best be implemented and accessed by users. One issue is where the
relative sizes should be stored. Presently, while figures hold
information about the margins around and between their subplots, as far
as I can tell they are unaware of any grid structure among their
subplots (although they do track as keys the arguments to add_subplot).
Grid dimensions are instead stored with each axes along with the
position of the axes within the grid. (The fact that the grid dimensions
need not agree among all of the axes in a figure allows for layouts like
mri_with_eeg. However, pushing that too far can yield unattractive
results: The axes specified by "subplot(2, 2, 1); subplot(2, 4, 5);
subplot(2, 4, 6)" are misaligned unless wspace = 0, a consequence of how
the wspace parameter is interpreted. A new spanning mechanism should
yield aligned axes.)

A second issue is that the numbers of rows and columns are
overdetermined, being specified explicitly by the arguments to subplot
and implicitly by the lengths of the lists containing the custom sizes.

Considering those issues, I thought of a few possible approaches for
specifying custom row and column sizes:

A. Store the row and column sizes in the instance of
figure.SubplotParams held in the figure's subplotpars attribute. User
code would look something like the following, using an example kwarg
row_sizes:

    fig = figure(subplotpars=mpl.figure.SubplotParams(row_sizes=[1, 1,
    1.5]))
    fig.add_subplot(3, 1, 1) # etc.
    fig.subplots_adjust(row_sizes=[1, 1, 3]) # resize the subplots

There would be corresponding pyplot functionality. This approach allows
the user to manage the sizes at the figure level (which I consider
convenient), but only one set of sizes is accommodated. One way to
handle the overspecified grid dimensions is a conflict-resolution
scheme. For example, in axes.SubplotBase.update_params, if the number of
rows (originating in the subplot call) matches the length of the
figure's row specifier, then honor the custom row sizes, and likewise
for columns. Otherwise, either (a) raise an error, or (b) fall back to
using equal rows (or columns) but send a message to verbose.report at
some debug level. If no custom sizes are specified, then everything
operates as now and no errors nor verbose.report messages will be
generated. Because the numbers of rows and columns are stored with the
axes, each axes would handle the conflict resolution independently of
the other axes. Another scheme (which I like better) would introduce
alternative subplot syntax such as add_subplot(row, col), where row and
col are numbered Python-style from 0. That form would cause the subplot
to inherit the numbers of rows and columns and the custom sizes in
fig.subplotpars, whereas the current forms would use only equally-sized
subplots. The new form also relieves the user of constructing the
Matlab-style single subplot number.

B. Instead of the above, associate the size information with the axes.
User code might resemble

    fig = figure()
    fig.add_subplot(2, 2, 1, col_sizes=[3, 1]) # +----+ ++
    fig.add_subplot(2, 2, 2, col_sizes=[3, 1]) # +----+ ++
    fig.add_subplot(2, 2, 3, col_sizes=[1, 3]) # ++ +----+
    fig.add_subplot(2, 2, 4, col_sizes=[1, 3]) # ++ +----+

This continues the current ability to create different grids (different
numbers of rows and columns, only now with different row and column
sizes) within the same figure, but they're managed at the axes level.
Resizing in this approach would need to be performed with each affected
axes, or a figure-level method could walk the child axes (possibly
checking for matching numbers of rows and columns). This approach still
overspecifies the numbers of rows and columns, and conflicts would need
to be resolved somehow. With the above syntax, errors could be raised by
subplot if its arguments disagree. Or, subplot could truncate or recycle
the list of sizes to match the grid dimensions.

C. Create a new layout mechanism parallel to or containing the subplot
mechanism. I can imagine such a mechanism handling nested grids or
creating colorbar axes that adjust with their parent axes within a
complex layout. Such a mechanism would be beyond what I can contribute
now working alone, but I mention it essentially to ask whether anyone
sees a need to build a new system instead of augmenting the current one.

While approach A appeals to me intuitively (in that it handles the sizes
at the figure level), it differs from the current structure of
matplotlib in which each axes independently maintains the dimensions of
its subplot grid and its position within it. I have some concern that
going in the direction of figure-level storage would involve structural
tension unless a new system is implemented (approach C). So, unless
there is a call for something dramatically different, I'm leaning toward
approach B with some figure-level methods to facilitate convenient
changes across multiple child axes. What do you folks think?

As for the approach for creating spanning subplots, I like extending the
syntax of subplot along the lines of:

    subplot(num_rows, num_cols, row, col)
      # non-spanning; row and col are numbered Python-style from 0
    subplot(num_rows, num_cols, (row_start, row_stop), (col_start,
    col_stop))
      # spans row_start through row_stop - 1 (Python-style)
      # and likewise for columns

How does that look?

This email ran long, but I didn't want to propose significant changes to
the matplotlib interface without first sharing some of the issues I've
encountered and getting feedback about how to proceed. Thanks.

------------------------------------------------------------------------

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

------------------------------------------------------------------------

_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

You may want to have a look at the mplsizer MPL toolkit I
wrote a long time ago and have failed to properly advertise
or maintain.

Thanks; I'll take a look at it.

While I check out the mplsizer toolkit, I'm still interested in any feedback
on my ideas for subplot layout features. Does anyone have any critiques,
concerns, preferences, suggestions, etc., to voice? Thanks.

Stan

My main comment is to not try and reuse subplot for this. Subplot is
a very thin wrapper of Axes, which handles layout on a regular grid.
You want your grids to be irregular, so make a new subclass of Axes
that acts the way you want. This will be easier than trying to tack
extras complexity on top of subplot.

We can then expose it to the toplevel with

  ax = fig.add_your_new_axes(whatever)

and to pyplot.

···

On Fri, Nov 21, 2008 at 8:08 AM, Stan West <stan.west@...595...> wrote:

While I check out the mplsizer toolkit, I'm still interested in any feedback
on my ideas for subplot layout features. Does anyone have any critiques,
concerns, preferences, suggestions, etc., to voice? Thanks.

From: John Hunter [mailto:jdh2358@…149…]
Sent: Friday, November 21, 2008 09:23

My main comment is to not try and reuse subplot for this.

...

You want your grids to be irregular, so make a new subclass
of Axes that acts the way you want.

Understood. I appreciate the feedback.

···

-----Original Message-----

Is there any development in this project. I was searching for the ways to
change the subplot sizes, but could not find any easy or nicer way.

···

--
View this message in context: http://old.nabble.com/Custom-sized-and-spanning-subplots-tp20485188p29580203.html
Sent from the matplotlib - devel mailing list archive at Nabble.com.

matplotlib version 1.0 now has a few different tools that could help you. There is GridSpec,

http://matplotlib.sourceforge.net/api/gridspec_api.html#

There is also AxesGrid1:

http://matplotlib.sourceforge.net/mpl_toolkits/axes_grid/index.html#toolkit-axesgrid-index

Maybe one of these could be what you are looking for?

Ben Root

···

On Tue, Aug 31, 2010 at 2:38 AM, imsc <rajkrpan@…149…> wrote:

Is there any development in this project. I was searching for the ways to

change the subplot sizes, but could not find any easy or nicer way.

View this message in context: http://old.nabble.com/Custom-sized-and-spanning-subplots-tp20485188p29580203.html

Sent from the matplotlib - devel mailing list archive at Nabble.com.

matplotlib version 1.0 now has a few different tools that could help you.
There is GridSpec,

http://matplotlib.sourceforge.net/api/gridspec_api.html#

http://matplotlib.sourceforge.net/users/gridspec.html

There is also AxesGrid1:

http://matplotlib.sourceforge.net/mpl_toolkits/axes_grid/index.html#toolkit-axesgrid-index

axesgrid1 can do things that gridspec cannot (e.g., fixed size axes
such as 5x5 inch) , but I would recommend it for an experienced user.

Regards,

-JJ

···

On Wed, Sep 1, 2010 at 9:33 AM, Benjamin Root <ben.root@...553...> wrote:

Maybe one of these could be what you are looking for?