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>
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