8000 Proposal: Grid arrangement by number of plots · Issue #8997 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Proposal: Grid arrangement by number of plots #8997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pganssle opened this issue Aug 6, 2017 · 19 comments
Closed

Proposal: Grid arrangement by number of plots #8997

pganssle opened this issue Aug 6, 2017 · 19 comments
Labels
MEP: MEP needed topic: geometry manager LayoutEngine, Constrained layout, Tight layout
Milestone

Comments

@pganssle
Copy link
Member
pganssle commented Aug 6, 2017

I often have the situation where I have some number of plots I'd like to display that may vary, and I don't want to fiddle around with how many rows and how many columns and such. I think it would be good if matplotlib provided some way to declare a "grid strategy" (with some built-in strategies to cover most of what people want), such that you could create subplots with something like plt.subplots_from_strategy(n, grid_strategy=SquareGridStrategy), and you'd get n subplots arranged in something as close to a square as possible.

To demonstrate, I've worked up a basic "squarish" grid strategy that creates a symmetrical pattern alternating between rows of length x and rows of length x-1 to create something close to a square. The code is here (though this is just for demo purposes, I'm not particular about how it's actually implemented), and here is a gallery of demo plots. Some basic examples:

n=6 n=7 n=8 n=17

I figure this is a good start, and the 'squarish' grid strategy could be changed to take an aspect ratio defaulting to 1, so that it tries to keep within +/- 1 on either side of the aspect ratio.

I've also implemented in there a related but separate feature, which is the get_gridspec function, which takes a tuple of length nrows, where each element is the number of columns in the given row - so the n=7 plot above is represented by (2, 3, 2). When creating the gridspec, it center-justifies the evenly-spaced plots rather than having subplots of uneven size like you'd normally get with GridSpecFromSubplotSpec (see this related SO question). I'll note that it would be no trouble to add right and left justification options for this. If you'd like I can make a separate issue for discussion of this feature (which I think would be useful even without the GridStrategy stuff above, though the way I've framed the GridStrategy stuff, I think GridStrategy relies on this).

@tacaswell tacaswell added this to the 2.2 (next next feature release) milestone Aug 6, 2017
@tacaswell
Copy link
Member

I think this is an extremely good idea!

attn @jklymak who has been thinking about similar things.

@jklymak
Copy link
Member
jklymak commented Aug 6, 2017

This looks cool. What I'm trying to do is adjust the size of spines to allow tick labels and other axes components fit inside their grid spec. I didn't read what you did here, but my recommendation is that grid arrangement strategies stay abstract from the size of the axes they contain. Ie the gridspecs divide up the figure in whatever logical way they want and the axes layout handles what is inside that logical region.

@pganssle
Copy link
Member Author
pganssle commented Aug 6, 2017

Yeah, this only sets up a grid and populates it with subplots of equal sizes, it's built on top of the gridspec abstraction. IIRC, there was a proposal to implement a way of specifying grids with strings or lists of strings, like "AAABB" would create two plots with a ratio of 3:2. I don't know if anyone's working on that now, but if so there might be some overlap of interest there.

@tacaswell
Copy link
Member

A somewhat related idea (which is much less automatic) is at scipy there was a poster about a tool takes an SVG created in inkscape (with some meta-data in it) and builds a set of axes laid out according to rectangles in the SVG. A tool to draw boxes on an figure and then create as axes in that space would not be hard to implement.

@pganssle
Copy link
Member Author
pganssle commented Aug 6, 2017

@tacaswell Should I start by preparing a PR for this? Or is this something that I should make into an MEP?

If we start with just the basic idea, I think it can be handled pretty well in one or two PRs, but the open questions I have right now would be:

  1. What should the public interface look like for this?
    1. In my demo code, it's a single static class. I'm thinking it's probably better to make it a regular class that can be instantiated with various options, with maybe a helper function to automatically create the plots.
    2. Should GridStrategy explicitly be a layer of abstraction on top of GridSpec, or should that be considered an implementation detail, and GridStrategy would live alongside GridSpec as another kind of grid manager?
    3. Is there an obvious submodule that this should go in, or should it have its own submodule?
  2. Should the get_gridspec portion of it be a standard way of creating gridspecs, or just something that is used internally by GridStrategy?
  3. What strategies should be supported "out of the box" at first release?
    1. In terms of shapes, right now there's only the "squarish" strategy - I think "rectangularish" is an obvious generalization of the squarish case, but are there more useful ones? Circular? Triangular?
    2. There are many decisions I made about how to handle the symmetry that may or may not be easy to change - ideally we'd want some sort of grammar for these so that they can be specified downstream.
    3. Whichever strategies are supported out of the box, is there any easy way to specify these in a helper function, e.g. plt.subplots(10, grid_strategy='square') (or a separate subplots_from_strategy, with some appropriate keywords)? Is it worth doing so?

I don't think I'll have much bandwidth in the way of working on the implementation for a few weeks. Depending on how much you want to be done as part of the first release, it could be more or less time. I'm happy to let someone else take what I've done so far and run with it if interested.

@tacaswell
Copy link
Member

I think a MEP might be better here, but either way will work.

The current goal is 2.1 RC by the end of the month, this is too big to get in for that so we have time for 2.2.

Maybe put this in it's own module (axes_layouts ?) and having a bunch of top-level functions like square_layout, triangle_layout etc which have signatures like square_layout(fig, n, fig_extent=None, **extra_kwargs) -> list[Axes] which get wrapped into pyplot to create a figure pass it through and then return both the figure and the list of axes? (The goal of this is to keep this re-usable for people doing their own embedding (which is why on master subplots is now also a Figure method) and not force people through pyplot.)

Having a top-level generic function that takes in one of these clases would be good, but in general I think out-of-the-box we should have a few simple functions.

@jklymak
Copy link
Member
jklymak commented Aug 7, 2017

Have a look at #1109 for the disucsion on geometry managers.

I actually like the name GridStrategy.

@story645 story645 added topic: geometry manager LayoutEngine, Constrained layout, Tight layout MEP: MEP needed labels Aug 7, 2017
@pganssle
Copy link
Member Author
pganssle commented Aug 20, 2017

Created MEP 30 for discussion.

@pganssle
Copy link
Member Author

@tacaswell For the MEP, should I make a thread on the matplotlib-devel mailing list? I looked through the archives for a template and didn't see any MEP discussions for the past year, so I'm not sure if people just haven't been making MEPs or if they are no longer discussed on that list.

@WeatherGod
Copy link
Member
WeatherGod commented Aug 21, 2017 via email

@timhoffm
Copy link
Member

Before adding another interface along the lines of subplot, subplots, GridSpec, ImageGrid and possibly a geometry manager, maybe it's time to consider if this functionality can be unified. All these different ways to layout are confusing to new users.

Also, there are other aspects to consider. For example, I often want to have the axes to be the same size, no matter if I have n=2 or n=49, so it should be possible to specify subplot size and the figure size is automatically adjusted. Another handy thing would be named axes, something like

layout = Layout(['birds', 'bees', 'trees'], subplot_size=(2, 2), strategy='triangle')
layout.axes['bees'].plot(...)

This is really just one example, there are many other aspects. It's probably a fundamental effort to get a good API here - but there's also much to gain.

Is there any central place where all such ideas around working with and arranging mutliple axes are collected and discussed?

@jklymak
Copy link
Member
jklymak commented Nov 15, 2017

@timhoffm Very happy to have someone concerned about these problems as well. I've tried to dive in and offer some solutions, and your feedback here or in the issues noted below is extremely welcome.

I agree that there should be an attempt to have one supported/recommended way to lay out elements, and other ways be deprecated or at least not heavily featured in the docs. For instance, if I read the docs right now, I'd think subplot2grid is the way to layout plots, but its really not very flexible, and (IMHO) folks should use GridSpec. subplots is fine because it is a simple wrapper to GridSpec. plt.subplot is annoying because though it uses GridSpec, it uses a new one each time, so the individual subplots aren't organized together.

I was going to try and update the docs after #9082 (constrained_layout) is merged to steer folks towards what I consider the more standard tools. Of course, others may disagree on what is "standard".

I've never been a fan of axes_grid1, imagegrid etc. They are valiant attempts to fix shortcomings in the current layout strategies. I think constrained_layout overcomes most of those. I think tight_layout could overcome those as well with a tiny bit more structure to the figure/GridSpec/axes hierarchy (see #9482). I think once you see that hierarchy, then layout is generally pretty logical and flexible. Its the other things that have been kludged on that are still unwieledly.

The geometry managers (tight_layout and #9082 constrained_layout) don't really have anything to do with logical layout - they simply change the size of axes to maximize the use of the figure space. The inverse of the problem you are talking about above.

For your use case, I'm not particularly convinced that is a very general thing to want to do that it would become part of the general API. If you know the size of your elements, then making the figure be the right size isn't too hard. Maybe a layout function that said fig, axs = plt.subplotsFixedSize(M, N, axes_width=1.0, axes_height=1.0, hspace=0.02, wspace=0.02, top=0.9) would be relatively straight forward and easy to impliment, and possibly of general use. But I wouldn't put it into GridSpec (which is just for logical layout, doesn't care about sizes at all) or subplots.

@pganssle
Copy link
Member Author

I would be interested in such a discussion. My GridStrategy idea here is a convenience layer on top of the official grid management. I'd be happy to spin it off into a separate library if that would minimize confusion in terms of interfaces.

@tacaswell tacaswell modified the milestones: needs sorting, v3.0 Apr 8, 2018
@tacaswell tacaswell modified the milestones: v3.0, v3.1 Aug 11, 2018
@egpbos
Copy link
Contributor
egpbos commented Jan 21, 2019

Great to see this discussion. I very much recognize the common usecase @pganssle describes: a variable number of subplots that you just want to plot in a decently legible way, without having to fiddle with rows and columns. I especially often use this in Jupyter notebooks, where I'm iterating fast.

I recently came up with this simple solution that just returns an array of N Axeses, laid out such that they are "auto-wrapped" up to some specified number of columns, just like text in a text editor. I'll put it here just for reference:

def subplots(total, wrap=None, **kwargs):
  if wrap is not None:
    cols = min(total, wrap)
    rows = 1 + (total - 1)//wrap
  else:
    cols = total
    rows = 1
  fig, ax = plt.subplots(rows, cols, **kwargs)
  return fig, ax

Usually, I'll also pass squeeze=False to the subplots, just to make the code even simpler (or flatten ax).

< 8000 /div>
@jklymak
Copy link
Member
jklymak commented Feb 27, 2019

I think this has its own repo now: https://github.com/matplotlib/grid-strategy Closing here, but not to close off discussion; feel free to reopen or have a new discussion.

@jklymak jklymak closed this as completed Feb 27, 2019
@pganssle
Copy link
Member Author

Ah yeah, thanks @jklymak, forgot that this was still open. There's a whole new issue tracker over there for us to discuss on ;)

@hemangjoshi37a
Copy link
hemangjoshi37a commented May 4, 2020

DID this idea got executed by now??

@pganssle
Copy link
Member Author
pganssle commented May 4, 2020

@hemangjoshi37a Yes, there is a third-party package available: https://github.com/matplotlib/grid-strategy

@hemangjoshi37a
Copy link

@pganssle Thank you sir for your help...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
MEP: MEP needed topic: geometry manager LayoutEngine, Constrained layout, Tight layout
Projects
None yet
Development

No branches or pull requests

8 participants
0