How can I programmatically select a specific subplot in Matplotlib?
Asked Answered
B

1

18

So in a figure where three vertical subplots have been added with add_subplot, how can I select let's say the middle one?

Right now I do this list comprehension:

[r[0] for r in sorted([[ax, ax.get_geometry()[2]] for ax in self.figure.get_axes()], key=itemgetter(1))]

where I can simply select the index I want, with the corresponding axes. Is there a more straightforward way of doing this?

Billion answered 18/8, 2011 at 1:3 Comment(0)
C
17

From the matplotlib documentation:

If the figure already has a subplot with key (args, kwargs) then it will simply make that subplot current and return it.

Here's an example:

import matplotlib.pyplot as plt

fig = plt.figure()  
for vplot in [1,2,3]:
    ax = fig.add_subplot(3,1,vplot)
    ax.plot(range(10),range(10))

ax_again = fig.add_subplot(3,1,2)
ax_again.annotate("The middle one",xy=(7,5),xytext=(7,5))

plt.show()

The middle plot is called again so that it can be annotated.

What if I set the background with my original call, do I need to set it again when I get the subplot the second time?

Yes. The arguments and keywords for the original call are used to make a unique identifier. So for the figure to generate this unique identifier again, you need to pass the same arguments (grid definition, position) and keywords again. For example:

import matplotlib.pyplot as plt

fig = plt.figure()  
ax = fig.add_subplot(2,1,1,axisbg='red')
ax.plot(range(10),range(10))
ax = fig.add_subplot(2,1,2)
ax.plot(range(10),range(10))

ax_again = fig.add_subplot(2,1,1,axisbg='red')
ax_again.annotate("The top one",xy=(7,5),xytext=(7,5))

plt.show()

What if I use ax_again.change_geometry() ?

You would think change_geometry, e.g. from a 312 to a 422, would change how you use add_subplot, but it doesn't. There appears to be a bug or undefined behavior when you call change_geometry. The unique key that was original generated using the arguments and keywords, to the first add_subplot call, does not get updated. Therefore, if you want to get an axis back with an add_subplot call, you need to call add_subplot with the original arguments and keywords. For more info, follow this issue report: https://github.com/matplotlib/matplotlib/issues/429

My guess for now is that if you change any property of the subplot after generating it with add_subplot call, the unique will not be adjusted. So just use the original arguments and keywords, and hopefully this will work out.

Confidant answered 18/8, 2011 at 2:49 Comment(6)
Man I have spent hours pouring through those docs, but I still managed to overlook that vital part. ThanksBillion
I wish I understood matplotlib inside out. It would definitely make my work easier. It took me a while too, and it's definitely not easy to tease out these details from the docs. I'm trying to see if you pass kwargs in the initial call, do you have to pass them exactly the same the second time around, as the docs suggest. I'll update things if I come up with anything.Confidant
So it turns out that the figure class has a private member function _make_key that '(makes) a hashable key out of args and kwargs'; this is used to identify the subplot (and I guess all axes belonging to a figure). So yes you need to precisely use the same arguments and keywords.Confidant
Thanks for the tip. I think I will have to explore that _make_key function. I am having some issues with getting old subplots with add_subplot that have had their geometry changed with change_geometry.Billion
So I'm guessing this is not the intended behavior and I've submitted an issue report: github.com/matplotlib/matplotlib/issues/429 If you want to use change_geometry, you can, but to get the subplot back later, you need to call it with the original arguments and keywords. When you call change_geometry, it changes the geometry, but it doesn't tell the figure this, so the figure uses the old geometry still to define the unique key for that subplot. add_subplot uses the old geometry to access it again. Does this make sense?Confidant
No it doens't, right? My problems came down to that exact issue, i.e., the figure seem to use an old unique key. Logically speaking a change in the geometry for an axes object should mean that every other object (figure in this example) should update accordingly. Looks like a bug to me.Billion

© 2022 - 2024 — McMap. All rights reserved.