Search code examples
pythonmatplotlib

Why can't subfigures be nested in gridspecs to keep their suptitles separate in matplotlib?


I would expect this code:

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(8, 6))

fig_gridspec = fig.add_gridspec(1, 1)

top_subfig = fig.add_subfigure(fig_gridspec[(0, 0)])

top_subfig.suptitle("I am the top subfig")

top_subfig_gridspec = top_subfig.add_gridspec(1, 1, top=.7)

nested_subfig = top_subfig.add_subfigure(top_subfig_gridspec[(0, 0)])

nested_subfig.suptitle("I am the nested subfig")

plt.show()

to generate two suptitles on different lines. Instead, they are overlapping.

a plot with two overlapping suptitles

Can anyone explain why? Also, is there a way to achieve such separation with nested subfigures?

Edit: To be clear, I mean, without changing the dimensions of the grid in the gridspec.

I know I can do this, and it might be what I wind up doing:

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(8, 6))

fig_gridspec = fig.add_gridspec(1, 1)

top_subfig = fig.add_subfigure(fig_gridspec[(0, 0)])

top_subfig.suptitle("I am the top subfig")

top_subfig_gridspec = top_subfig.add_gridspec(2, 1, height_ratios=[.1, 1])

nested_subfig = top_subfig.add_subfigure(top_subfig_gridspec[(1, 0)])

nested_subfig.suptitle("I am the nested subfig")

plt.show()

I just don't understand why my first code block doesn't work, and why there seems to be no way to adjust a nested subfigures's position within another subfigure.

Second edit: I don't have a minimum reproducible example for this, but relatedly, it also doesn't seem to be than hspace is doing anything for a gridspec that contains subfigures that also contains subfigures. I am starting to conclude that gridspec keyword arguments simply do not work when what the gridspec contains is subfigures, when the gridspec is associated with a subfigure, or both. I don't yet know the boundaries of the phenomenon.

Yet another edit: I should have said to begin with that I find in my larger context, not discussed here, that constrained_layout causes a bunch of problems even as it solves some others, so I can't address my issue that way. I'm really wondering about why it is I can't just get a subfigure to respect the gridspec it's in, and if the answer is "this is a bug in matplotlib" and you should not use subfigures for your use case, I'd accept it. Or if the answer is, "here is the fully justified reason for subfigures to behave this way" and you should not use subfigures for your use case, I'd accept that too.


Solution

  • Answering my own question.

    Subfigures are not meant to respect Gridspec keyword arguments.

    Further answering the question of what it is they do respect: they respect arguments passed to the subfigures method. It would be nice if this method also took top, left keywords, etc., but as of now it doesn't.

    However, this works to get my intended effect:

    import matplotlib.pyplot as plt
    
    fig = plt.figure(figsize=(8, 6))
    
    top_subfig = fig.subfigures(1, 1)
    
    top_subfig.suptitle("I am the top subfig")
    
    nested_subfigs = top_subfig.subfigures(2, 1, height_ratios=[.1, 1])
    
    nested_subfigs[1].suptitle("I am the nested subfig")
    
    plt.show() 
    

    While very similar to the "add a row in gridspec strategy" that's in my second code block in my question, I find this solution more satisfying, a) because I think it's the intended usage, and b) because understanding that if I make my subfigures this way, I can get the subfigures to respect some of the arguments I'd otherwise pass to a gridspec is going to solve other layout problems.