Skip to content

Feature request: savefig with separated pad_inches in each direction #11764

Open

Description

@scarlehoff

This is just a thought I had, it would be desirable to be able to select "pad_inches" separately for the top/bottom/right/left margins.

Just to clarify, I want to be able to create my figures and whatever and only at the time of saving them to disk be able to do:

figure.savefig(filename, bbox_inches = 'tight', pad_inches_top = 0.4, pad_inches_right = 0.3)

Instead of having to change the shape of the figure I'm working with or creating a new figure with the right dimensions, as the padding work in all directions at the same time. Of course it's not a critical thing but in terms of usability would be a great help.

ps. I'm not sure whether this is a good idea or whether this is the correct place to submit a feature request. Apologies in advance.

Activity

  1. jklymak commented on Jul 24, 2018

    @jklymak
    Member

    Yes this is a good place to submit this idea. I don’t think it’d be a big deal to pass a four-tuple in pad_inches; I’d be against four new kwargs. Someone just has to write the PR!

    Edit: just to be clear: fig.savefig(fname, bbox_inches='tight', pad_inches=[l, r, b, t]) is what I was thinking.

  2. added
    🌱 Good first issueOpen a pull request against these issues if there are no active ones!
    on Jul 24, 2018
  3. timhoffm commented on Jul 24, 2018

    @timhoffm
    Member

    I agree that this should be a tuple and not new kwargs.

    Unfortunately, there's no consistent solution for the order.

    • Axes.imshow(extent=) uses (left, right, bottom, top)
    • SubplotParams(left, bottom, right, top)
    • CSS margin and padding use (top, right, bottom, left) - not part of matplotlib but a widely known convention.

    If I would design something from scratch, I'd go for the CSS convention. However, that would be badly inconsistent with both conventions used in matplotib.

    Because of this situation, I'm unsure if we want to support a simple tuple at all. A more verbose but unambiguous alternative would be a dict with keys left, right, bottom, top.

  4. jklymak commented on Jul 24, 2018

    @jklymak
    Member

    I think if you are going to do a dictionary, you might as well just do the explicit kwargs?

    Its kind of funny that SubplotParams has the kwarg order lbrt, but the documentation as lrbt. I always get confused about kwargs, but can __init__ be called w positional arguments? I don't see anywhere in the codebase where fig.subplots_adjust is called with positional arguments, so I somewhat suspect no one was really paying attention to the order of the kwargs.

  5. timhoffm commented on Jul 24, 2018

    @timhoffm
    Member

    I think if you are going to do a dictionary, you might as well just do the explicit kwargs?
    Not quite. The call is comparable complex or lengthy with both versions, but the API stays simpler: Just one arg.

    Since the doc is lrbt, it's probably an unintended mistake to have the order SubplotParams(left, bottom, right, top). However, even if we don't use that internally, it's now part of the public API and not easy to change.

  6. fredrik-1 commented on Jul 25, 2018

    @fredrik-1
    Contributor

    And (matplotlib) basemap use for same reason, left, right, top, bottom.

    I believe that the best API would be to let pad_inches be either a float or dict. I don't think that it would be a good idea to have four new kwargs for a functionality that is probably not going to be used that much. A tuple would also be good, less wordy but more difficult to remember.

  7. zjpoh commented on Jul 27, 2018

    @zjpoh

    I agree that tuple is not the best option here. I also agree that adding one dict is better than four new kwargs for easy of maintenance.

    If nobody is working on this yet, I would like to give it a try.

  8. jklymak commented on Jul 27, 2018

    @jklymak
    Member

    My objection to a dictionary is the same as my objection to fontdict for text. There is no discoverability for it from the command line of IPython or Jupyter notebook. Further to fill in a dictionary properly you have to get the keys correct; is it left-pad, leftpad, pad-left, padding-left etc etc etc. At least with a kwarg you can tab-complete to get the name.

    A tuple on the other hand is easy to get wrong, but there are really only two sensible ways it can be organized so at most you make two guesses.

    I understand why we have fontdict, but I have to google the entries every time I use it and that seems like bad API to me.

  9. timhoffm commented on Jul 27, 2018

    @timhoffm
    Member

    If we use a dict as param it should be described fully in the docstring of the respective param. I know that's not the case for fontdict. You don't get tab-completion from that, but at least you can look it up in the docstring without leaving IPython / the notebook.

    To that extent, a dict and a tuple are equally (non-)accessible. The dict has the advantage of being more explicit at the cost of being more verbose. I'd prefer that for rarely used options where there is no unambiguous order as in this case.

    Fontdict has another issue in that it's redundant to other kwargs, cf #10293. I think the situations between here and fontdict are not quite comparable. We should not mix this into the discussion here.

  10. jklymak commented on Jul 27, 2018

    @jklymak
    Member

    Just registering my general disaproval of packing method arguments in dictionaries; I don't really care about this kwarg in particular, but I think its a klunky API style in general.

  11. timhoffm commented on Jul 30, 2018

    @timhoffm
    Member

    Short summary of options:

    1. pad_inches supports tuple (left, right, bottom, top) (maybe alternative order, but the one given here seems prevalent within matplotlib - though we are not consistent with ourselves)
    2. Separate keywords pad_inches_left, pad_inches_right, ...
    3. pad_inches supports dict {'left'=..., 'right'=..., 'bottom'=..., 'top'=...}.

    Not adding the feature or supporting multiple ways are theoretically possible as well.

    Personally, I'm 50/50 on 1. or 3. But there were also other opinions. For a detailed discussion of the advantages/disadvantages see above.

    I think this is an important API decision, which should be taken deliberately. @tacaswell @efiring Can please some of the senior contributors decide on the API we want to use here.

  12. efiring commented on Jul 30, 2018

    @efiring
    Member

    Considerations: discoverability, readability, and code complexity, all viewed with the perspective of how often a feature will be used, and by whom.

    Discoverability involves documentation as well as possible tab-completion. Having a large number of kwargs hurts discoverability; the docstring becomes overwhelming, and there is a problem even for tab-completion, given the present ipython default of providing a quasi-gui interface instead of listing everything.

    Readability is high-priority, and having the option of packing related options into a single dict kwarg aids readability, compared to an opaque tuple with non-obvious ordering.

    The option proposed here, allowing more detail in pad_inches, is to me low-priority; I think its use will be rare. This puts extra weight on readability (if the option is indeed worth adding at all).

    If this is implemented, I think options 1 (tuple) and 2 (more kwargs) is perpetuating bad API; option 3 (allowing a dict) is by far superior.

  13. EzraBC commented on Aug 1, 2018

    @EzraBC

    Be aware, this is my first attempt at contributing to something.

    I propose to modify matplotlib.transforms.BboxBase.padded to return a Bbox with the points modified by values in a dict as opposed to a single value. If a single value is passed, it can be normalized via the dict.fromkeys method within BboxBase.padded as shown in master...EzraBC:master.

    Edit: This may be opening a can of worms, by in my opinion the most "pythonic" (though maybe not the most "matplotlib-ic") approach is to allow for as many possibilities as is easy to implement. As such, the link above now shows the BboxBase.padded method changed to allow a dict, list, tuple, or single value for the parameter p. If people more important than I disagree with this approach I am happy to pare it down.

  14. Roald87 commented on Aug 16, 2018

    @Roald87

    Is there something against using a namedtuple?

  15. 22 remaining items

  16. added
    status: closed as inactiveIssues closed by the "Stale" Github Action. Please comment on any you think should still be open.
    on Sep 10, 2024
  17. ttshaw1 commented on Jun 17, 2025

    @ttshaw1

    Can we reopen this? @zjpoh is this on your to-do list? I can take it over if not, this would be useful for a graph I'm making right now

  18. jklymak commented on Jun 17, 2025

    @jklymak
    Member

    You are welcome to put in a PR even if the issue is closed

  19. ttshaw1 commented on Jun 17, 2025

    @ttshaw1

    I opted for a [l,r,b,t] tuple because I think that's going to be the most accessible. Additional kwargs and more complex datatypes seem like overkill for most users to me. If I'm understanding the above correctly, SubplotParams()'s lbrt order is the black sheep and a lrbt tuple will be reasonably conventional. Can't speak to tab-completion as I don't think it's functional in my Spyder environment

  20. timhoffm commented on Dec 21, 2025

    @timhoffm
    Member

    Semi-OT: Very few users seem to rely on the parameter in SubplotParams(): https://github.com/search?q=%2F%5CbSubplotParams%5C%28%5B%5Cw%5C.%5D%2B%2C%2F+language%3APython+NOT+is%3Afork&type=code

    Therefore, it might be an option to make its parameters kwonly. This would remove the inconsistency. It also adds to clarity, and SubplotParams Is such an advanced use case that we can bear the additional verbosity.

    Edit: It's more complicated than that, as this also has to be done in other places like SubplotParams. update and Figure.subplots_adjust (usage). So likely not worth it. I think subplot params are generally less used nowadays since constrained layout is often a good automatic default.

  21. timhoffm commented on Dec 21, 2025

    @timhoffm
    Member

    Summarizing the current APIs we have (partially taken from above):

    • Axes.imshow(extent=) uses (left, right, bottom, top) - while they share the same structure, at least they are positions not pad
    • SubplotParams(left, bottom, right, top) - these are pads, but in a weird order
    • tight_layout (and related functionality) - only scalars as separate parameters: pad, h_pad, w_pad.

    So no matter what we do with a 4-tuple: It's a different API than tight_layout and it will be inconsistent either with the subplot_params or extent tuples.


    If we want to expand pad semantics, I'm inclined to go with the CSS margin / padding approach; i.e. accept

    • a single value for all cases
    • two values (vertical, horizontal)
    • four values (top, right, bottom left)

    Advantages:

    • a widely known convention and thus better memorizable
    • convenient as often the two-value version is sufficient (and reduces the need to think too much about the order of the individual sides)
    • can easily be retrofitted to any place we can currently have only a scalar pad (including tight_layout)

    Disadvantages:

    • Inconsistent with the order in subplot_params - but hopefully less used nowadays
    • Inconsistent with the order in extent. - we need to clearly document this.
  22. ttshaw1 commented on Dec 22, 2025

    @ttshaw1

    I agree it makes sense for imshow, SubplotParams, and tight_layout (and largely anything else where we're specifying a bounding box type of thing) to accept all of

    • a single value
    • specify the x and y axes
    • specify all four bounds

    But I want to point out that the left, right, bottom, top order is used in other contexts in matplotlib. Off the top of my head, hist2d's range ([[xmin, xmax], [ymin, ymax]]), and imshow's extent ([l, r, b, t]), though I'm sure there are a lot of other examples. In the typical "turn data into a pretty png for a powerpoint" tasks that I do, I see l, r, b, t quite a bit and other orders never.

    I'd vote for any argument in this bounding box-ish domain to accept

    • a single number or len-1 list-like to specify all
    • [x, y] list-like
    • [left, right, bottom, top] list-like
      wherever we can do that without breaking changes
  23. timhoffm commented on Dec 22, 2025

    @timhoffm
    Member

    , hist2d's range ([[xmin, xmax], [ymin, ymax]]), and imshow's extent ([l, r, b, t])

    This is not directly comparable. Logically, it’s a lis of ranges. A grouping [[pad_l, pad_r], [pad_top, pad_bottom]] wouldn’t make sense.

    It think we need to create a systematic overview of all cases involving „sizing“ in x and y to make an informed decision. Rectangle and Bbox directly to my mind. Though not all of these will be logically equivalent (see hist2d‘s range) I think it’s valuable to have the broader picture.

  24. jklymak commented on Dec 22, 2025

    @jklymak
    Member

    I agree that these things are just convention and it would be nice to have one paradigm in Matplotlib, though for back compat reasons I expect this will be hard.

    I find [t, r, b, l] super unintuitive, and I'm pretty surprised that is the CSS default. However, it makes some sense in CSS to start from the top, since web layout is from the top down (and then they went clockwise, I guess). I'm also unaware of it ever being used in Matplotlib.

    However, for Matplotlib, ax.set_position is [l, b, w, h]. For Bbox: "extents" are [l, b, r, t], and "bounds" are [l, b, w, h]. Given that these are super fundamental to Matplotlib, I'd suggest we keep the same convention and use [l, b, r, t]. I dont' find Rectangle as useful an analogy, but it is specified (xy), width, height where (xy) is "typically" the bottom left corner.

    So I think its pretty universal for Matplotlib (and Matlab before it) to start "left, bottom,..." I can see where the other convention comes from, but I think it's just a different paradigm.

  25. removed
    status: closed as inactiveIssues closed by the "Stale" Github Action. Please comment on any you think should still be open.
    status: inactiveMarked by the “Stale” Github Action
    on Dec 23, 2025
  26. timhoffm commented on Jan 2, 2026

    @timhoffm
    Member

    Given that there is no canonical order of the values and that this is and advanced feature used for fine-tuning, I vote to be defensive and only accept a dict for specifying per-side pads (option 3 from above).

    Advantage:

    • makes per-side pads possilbe
    • unambiguous
    • we can always add tuple support later once/if we come to an agreement this is worth it and we have a consistent memorizable API

    Disadvantage:

    • more typing, in particular if you want vertical pads and horizontal pads (i.e. the dict doesn't have an equivalent for the 2-tuple variant)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    API: changesChanges to the public API, typically requiring deprecation.API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions