Running plt.subplot after the respective subplot has already been created shows a warning. This behaviour is expected for fig.add_subplot. However in pyplot this is to my knowledge the recommended way to activate a subplot, as seen from the tutorial.
import matplotlib.pyplot as plt
plt.figure(1) # the first figure
plt.subplot(211) # the first subplot in the first figure
plt.plot([1, 2, 3])
plt.subplot(212) # the second subplot in the first figure
plt.plot([4, 5, 6])
plt.figure(2) # a second figure
plt.plot([4, 5, 6]) # creates a subplot(111) by default
plt.figure(1) # figure 1 current; subplot(212) still current
plt.subplot(211) # make subplot(211) in figure1 current
plt.title('Easy as 1, 2, 3') # subplot 211 title
Actual outcome
When being run it produces a warning.
MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
Expected outcome
No warning. Using pyplot.subplot(211) should just activate the subplot, as described in the tutorial.
Alternatively, there needs to be another way to activate a subplot in pyplot (without having stored any handle to it previously).
That has been discussed at length in #7377 (comment) and linked issues. TLDR: the current implementation is quite brittle.
I think the correct way is indeed to store a handle to the axes and call sca on it (and I agree that the docs need to be updated accordingly).
If we really want to maintain the previously existing behavior, I would much prefer to move the "current axes" machinery out of Figure (where it simply doesn't belong) and into pyplot.
As a side note, it looks like the warning gets lost by sphinx-gallery. There should probably be an option to emit these warnings, and even better fail the build in that case...
This code example is not executed by sphinx-gallery, it's just some code in the text. I suppose we do not want to let these kinds of code snippets being parsed at all, since there is no guarantee that they are even runnable.
The discussion in #7377 doesn't actually consider the case of plt.subplot() (where activating is useful). I would think that it should only call add_subplot after it has checked for itself whether a subplot at that position already exists. All the "brittle" behaviour can then be removed from the Figure after the deprecation period (is 3.1 too soon for this period to end?).
This code example is not executed by sphinx-gallery, it's just some code in the text. I suppose we do not want to let these kinds of code snippets being parsed at all, since there is no guarantee that they are even runnable.
Oh, sorry, I completely missed that. I think that specific example could be run, though.
I think this issue is pretty important. If everything stays as it is now, the pure pyplot interface will not be able to handle subplots anymore. So I want to make sure everyone is aware of the consequences.
For better or worse, there are users out there who rely on being able to call plt.subplots(212) repeatedly to manage what the 'current axes' is to make sure their plots go to the right place (I have had an argument with a user that got, uh, heated, about the virtues of pyplot vs the OO api. Their position was basically that the "object" was the 3 number pattern and manually managed the context). This should be fixed to not raise when used from pyplot.
I am definitely not a fan of this change. One thing I have appreciated about python is that you can do OOP or not, and this break that. Trying to force a particular style of programming doesn't seem very pythonic - and this seems to be the uniform response to the questions raised about this change. Something like "use the OOP syntax" or "store the axes yourself" is all I can find.
For example, what is the recommended way of handling the following - which is the original question of the post. This situation came up today in my workflow for a real problem, but I boiled it down to a simple case. We start with a simple subplot arrangement and some data:
t=linspace(1,50,10)
y=rand(10,2)
z=rand(10,2)
for i in range(2):
subplot(2,1,i+1)
plot(t,y[:,i],'-o')
plot(t,z[:,i],'-s')
Now, I then realize that the matrix z should have 30 points instead of 10, and a slightly different x-axis. No problem!
t=linspace(1,50,10)
y=rand(10,2)
for i in range(2):
subplot(2,1,i+1)
plot(t,y[:,i],'-o')
t=linspace(1,50,30)
z=rand(30,2)
for i in range(2):
subplot(2,1,i+1)
plot(t,z[:,i],'-s')
...but now we have that deprication warning! Notice how the structure of the second example matches the first. It was a simple copy/paste and easy to develop. What's the recommended solution now? Something like this?
t=linspace(1,50,10)
y=rand(10,2)
ax=[]
for i in range(2):
subplot(2,1,i+1)
plot(t,y[:,i],'-o')
ax.append(gca())
t=linspace(1,50,30)
z=rand(30,2)
for i in range(2):
sca(ax[i])
plot(t,z[:,i],'-s')
Something else? No matter what, you break the symmetry, and make it harder to develop the code, and make it harder to read. This kind of case happens a lot in my workflow. As a hack I will probably do something like:
def subplot(*args):
import pylab as plt
try:
fig=plt.gcf()
if args in fig._stored_axes:
plt.sca(fig._stored_axes[args])
else:
plt.subplot(*args)
fig._stored_axes[args]=plt.gca()
except AttributeError:
plt.subplot(*args)
fig._stored_axes={}
fig._stored_axes[args]=plt.gca()
Reacted by divenex, Tim Fuchs, durandg12 and Adam Ginsburg
Should work, and not have a deprecation warning. I.e if that subplot exists it should become the current axes, but not be erased. I’m confused if that’s what happened before.
I totally don't understand what this response means. The symbol "I" doesn't exist, so plt.subplot(2, 1, I) won't work. plt.cla() clears the axis, which is not at all what I was asking about, nor what the original poster was asking, so I must be missing something here. Could you modify my simple problem above into something runnable?
The problem is that if you call, say, subplot(2,1,1) at one point, and then want to add to that same subplot (after doing a number of other subplots) you'd like to be able to do subplot(2,1,1) again to select that axis again without having to store the intermediate value. That was the old behavior and has worked well for over a decade, and certainly behaves like matlab's subplot works. the new deprecation seems to be warning that it will break that.
The original issue was with plt.axes #9024 and I think even at a lower level of fig.add_axes? I don't see that this necessarily needs to affect plt.subplot, though perhaps the machinery is not there to make plt.subplot redirect to the correct axis instance.
In #9024 someone complained that plt.axes() has reactivating behavior and basically all the core devs agreed to deprecate it.
Internally the code paths for plt.axes() and plt.subplot() are similar, so it seemed normal to also share the deprecation with subplot(). I guess one could try to tear apart the API to figure out how to make change axes() but not subplot(); this may actually not be so difficult. But I do think the behavior change on axes() should go through.
Yes, the axes() behavior would be surprising. However, subplot has always worked this way for the reasons I pointed out before. If they could be separated, that would be terrific!
Can you confirm that there is no real risk of deprecation here and that plt.subplot will work intuitively (i.e. reactivate an axes if it already exists) in the future?
If so, is there a way to mask the warning in the meantime ?
Please be careful when using "intuitively" to describe software. It is very dependent on your personal experience and context. Behavior that one person finds "intuitive" another may find counter intuitive.
The implicit "current axes" logic behind plt.xyz relies on hidden global state that can be updated in ways that, while convenient at the prompt, will grow exceedingly complex and hard to manage once you start to use it in longer scripts. Reading the following code
plt.subplot(222)
plt.subplot(122)
plt.plot(...)
some_helper(...)
plt.plot(...) # <- which axes does this plot to?!
It is impossible to tell what axes (or really what what Figure!) that last plot is going to go to without reading some_helper (and then all of the function it calls and of the functions they call....). To make things worse, if there is a plt.pause in there reading the wouldn't be enough because the user could have used the mouse to click on the figure (or some other figure) which would change the current axes. The same applies if the user is getting the prompt back.
Given that this has been documented forever, we probably can't actually let this break. @durandg12 Would you mind take a look at how fix this so this usage does not warn or raise, but so that we can remove the behavior of add_axes taking Axes object as input?
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!
Activity
anntzer commented on Oct 13, 2018
That has been discussed at length in #7377 (comment) and linked issues. TLDR: the current implementation is quite brittle.
I think the correct way is indeed to store a handle to the axes and call sca on it (and I agree that the docs need to be updated accordingly).
If we really want to maintain the previously existing behavior, I would much prefer to move the "current axes" machinery out of Figure (where it simply doesn't belong) and into pyplot.
As a side note, it looks like the warning gets lost by sphinx-gallery. There should probably be an option to emit these warnings, and even better fail the build in that case...
ImportanceOfBeingErnest commented on Oct 13, 2018
This code example is not executed by sphinx-gallery, it's just some code in the text. I suppose we do not want to let these kinds of code snippets being parsed at all, since there is no guarantee that they are even runnable.
The discussion in #7377 doesn't actually consider the case of plt.subplot() (where activating is useful). I would think that it should only call add_subplot after it has checked for itself whether a subplot at that position already exists. All the "brittle" behaviour can then be removed from the Figure after the deprecation period (is 3.1 too soon for this period to end?).
anntzer commented on Oct 13, 2018
Oh, sorry, I completely missed that. I think that specific example could be run, though.
ImportanceOfBeingErnest commented on Oct 15, 2018
I think this issue is pretty important. If everything stays as it is now, the pure pyplot interface will not be able to handle subplots anymore. So I want to make sure everyone is aware of the consequences.
tacaswell commented on Oct 15, 2018
For better or worse, there are users out there who rely on being able to call plt.subplots(212) repeatedly to manage what the 'current axes' is to make sure their plots go to the right place (I have had an argument with a user that got, uh, heated, about the virtues of pyplot vs the OO api. Their position was basically that the "object" was the 3 number pattern and manually managed the context). This should be fixed to not raise when used from pyplot.
anntzer commented on Oct 15, 2018
It would be nice to move this handling out of Axes/Figure and into pyplot, but in the meantime I guess one can just revert the warning...
rudrathegreat commented on Nov 8, 2018
I am using a very similar version to you (2.2.3 I think) and the following line gives me no warning -
And then you can just simply add the following code -
I hope that helps!
bblais commented on Jan 23, 2019
I am definitely not a fan of this change. One thing I have appreciated about python is that you can do OOP or not, and this break that. Trying to force a particular style of programming doesn't seem very pythonic - and this seems to be the uniform response to the questions raised about this change. Something like "use the OOP syntax" or "store the axes yourself" is all I can find.
For example, what is the recommended way of handling the following - which is the original question of the post. This situation came up today in my workflow for a real problem, but I boiled it down to a simple case. We start with a simple subplot arrangement and some data:
t=linspace(1,50,10) y=rand(10,2) z=rand(10,2) for i in range(2): subplot(2,1,i+1) plot(t,y[:,i],'-o') plot(t,z[:,i],'-s')Now, I then realize that the matrix z should have 30 points instead of 10, and a slightly different x-axis. No problem!
t=linspace(1,50,10) y=rand(10,2) for i in range(2): subplot(2,1,i+1) plot(t,y[:,i],'-o') t=linspace(1,50,30) z=rand(30,2) for i in range(2): subplot(2,1,i+1) plot(t,z[:,i],'-s')...but now we have that deprication warning! Notice how the structure of the second example matches the first. It was a simple copy/paste and easy to develop. What's the recommended solution now? Something like this?
t=linspace(1,50,10) y=rand(10,2) ax=[] for i in range(2): subplot(2,1,i+1) plot(t,y[:,i],'-o') ax.append(gca()) t=linspace(1,50,30) z=rand(30,2) for i in range(2): sca(ax[i]) plot(t,z[:,i],'-s')Something else? No matter what, you break the symmetry, and make it harder to develop the code, and make it harder to read. This kind of case happens a lot in my workflow. As a hack I will probably do something like:
def subplot(*args): import pylab as plt try: fig=plt.gcf() if args in fig._stored_axes: plt.sca(fig._stored_axes[args]) else: plt.subplot(*args) fig._stored_axes[args]=plt.gca() except AttributeError: plt.subplot(*args) fig._stored_axes={} fig._stored_axes[args]=plt.gca()jklymak commented on Jan 24, 2019
I think
Should work, and not have a deprecation warning. I.e if that subplot exists it should become the current axes, but not be erased. I’m confused if that’s what happened before.
bblais commented on Jan 25, 2019
I totally don't understand what this response means. The symbol "I" doesn't exist, so plt.subplot(2, 1, I) won't work. plt.cla() clears the axis, which is not at all what I was asking about, nor what the original poster was asking, so I must be missing something here. Could you modify my simple problem above into something runnable?
The problem is that if you call, say, subplot(2,1,1) at one point, and then want to add to that same subplot (after doing a number of other subplots) you'd like to be able to do subplot(2,1,1) again to select that axis again without having to store the intermediate value. That was the old behavior and has worked well for over a decade, and certainly behaves like matlab's subplot works. the new deprecation seems to be warning that it will break that.
jklymak commented on Jan 25, 2019
@bblais, plus or minus a typo, I am agreeing with you...
jklymak commented on Jan 25, 2019
The original issue was with plt.axes #9024 and I think even at a lower level of fig.add_axes? I don't see that this necessarily needs to affect plt.subplot, though perhaps the machinery is not there to make plt.subplot redirect to the correct axis instance.
anntzer commented on Jan 25, 2019
In #9024 someone complained that plt.axes() has reactivating behavior and basically all the core devs agreed to deprecate it.
Internally the code paths for plt.axes() and plt.subplot() are similar, so it seemed normal to also share the deprecation with subplot(). I guess one could try to tear apart the API to figure out how to make change axes() but not subplot(); this may actually not be so difficult. But I do think the behavior change on axes() should go through.
bblais commented on Jan 25, 2019
Yes, the axes() behavior would be surprising. However, subplot has always worked this way for the reasons I pointed out before. If they could be separated, that would be terrific!
1 remaining item
jklymak commented on Jan 25, 2019
Oh, my mistake - looks like @anntzer is taking a go at it.
#13269
durandg12 commented on Mar 23, 2020
I am having this issue with matplotlib 3.2.1.
Can you confirm that there is no real risk of deprecation here and that plt.subplot will work intuitively (i.e. reactivate an axes if it already exists) in the future?
If so, is there a way to mask the warning in the meantime ?
tacaswell commented on Mar 26, 2020
Please be careful when using "intuitively" to describe software. It is very dependent on your personal experience and context. Behavior that one person finds "intuitive" another may find counter intuitive.
The implicit "current axes" logic behind plt.xyz relies on hidden global state that can be updated in ways that, while convenient at the prompt, will grow exceedingly complex and hard to manage once you start to use it in longer scripts. Reading the following code
It is impossible to tell what axes (or really what what Figure!) that last plot is going to go to without reading some_helper (and then all of the function it calls and of the functions they call....). To make things worse, if there is a plt.pause in there reading the wouldn't be enough because the user could have used the mouse to click on the figure (or some other figure) which would change the current axes. The same applies if the user is getting the prompt back.
If instead you write
there is no ambiguity.
Given that this has been documented forever, we probably can't actually let this break. @durandg12 Would you mind take a look at how fix this so this usage does not warn or raise, but so that we can remove the behavior of add_axes taking Axes object as input?
github-actions commented on May 19, 2023
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!
anntzer commented on May 21, 2023
AFAICT this no longer warns. Feel free to ping for reopen if I got this wrong.
QuLogic commented on Aug 1, 2023
Yes, it was fixed by #19153.