<div dir="ltr">Hey Rory,<div><br></div><div>In general, especially for library code, you should avoid relying on the pyplot state machine.</div><div><br></div><div>That means explicitly passing Axes and Figure object around to and from your functions.</div><div><br></div><div>For me, making that switch meant added an `ax=None` kwarg to the end of my function signatures.</div><div><br></div><div>Then I carry around an axes_validator function that looks something like this:</div><div><a href="https://github.com/matplotlib/mpl-probscale/blob/master/probscale/validate.py#L6">https://github.com/matplotlib/mpl-probscale/blob/master/probscale/validate.py#L6</a><br></div><div><br></div><div>But that's not really necessary. So in your case, I think you should do something like this:</div><div><br></div><div><br></div><div><div><font face="monospace, monospace"><span style="font-size:12.8px">import numpy as np</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">import matplotlib.pyplot as plt</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">def bode_plot(g, ax_mag=None, ax_phase=None, mag_opts=None, phase_opts=None):</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    # create axes if they're not both supplied</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    if not ax_mag or not ax_phase:</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">        fig, (ax_mag, ax_phase) = plt.subplots(nrows=2)</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    # make the plotting options empty dicts if</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    # not supplied</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    if not mag_opts:</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">        mag_opts = {}</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    if not phase_opts:</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">        phase_opts = {}</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    # compute signal stuff</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    freq, mag, phase = freq_resp(g)</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    # 99% sure semilogx returns a tuple of artists, so I unpack it</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    # you should check this though</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    mag_artist, = ax_mag.semilogx(freq, 20 * np.log10(mag), **mag_opts)</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    phase_artist, = ax_phase.semilogx(freq, phase, **phase_opts)</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    </span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    # package the output for later (if you want to modify artists)</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    output = {</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">        'fig': fig,</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">        'axes': (ax_mag, ax_phase),</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">        'arists': (mag_artist, phase_artist)</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    }</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">    return output</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div>And then you'd use the code like this:<font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px"><br></span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">fig, (ax_mag, ax_phase) = plt.subplots(nrows=2, figsize=(12, 6))</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">g_mpl = bode_plot(g, ax_mag=ax_mag, ax_phase=ax_phase, color='r', linewidth=2, label='G')</span></font></div><div><font face="monospace, monospace"><span style="font-size:12.8px">h_mpl = bode_plot(h, ax_mag=ax_mag, ax_phase=ax_phase, color='b'm linewidth=1, label='H')</span></font></div></div><div><font face="monospace, monospace"><span style="font-size:12.8px">ax_mag.legend()</span></font></div><div><br></div><div><br></div><div>Or do something like this:</div><div><span style="font-family:monospace,monospace;font-size:12.8px">g_mpl = bode_plot(g, color='r', linewidth=2, label='G')</span><br></div><div><span style="font-family:monospace,monospace;font-size:12.8px">h_mpl = bode_plot(h, ax_mag=g_mpl['axes'][0], ax_phase=</span><span style="font-family:monospace,monospace;font-size:12.8px">g_mpl['axes'][0])</span><span style="font-family:monospace,monospace;font-size:12.8px"><br></span></div><div><span style="font-family:monospace,monospace;font-size:12.8px"><br></span></div><div><span style="font-family:monospace,monospace;font-size:12.8px"><br></span></div><div>Does that help?<br></div><div>-Paul</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sat, Jan 20, 2018 at 11:44 AM, Rory Yorke <span dir="ltr"><<a href="mailto:rory.yorke@gmail.com" target="_blank">rory.yorke@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi,<br>
<br>
I'm a contributor to the Python Control Systems Library [1], which uses<br>
Matplotlib for plotting.<br>
<br>
We recently noticed deprecation warnings due to how we use<br>
pyplot.subplot.  We use it in the Matlab manner of either getting a<br>
handle to an existing axis, or creating one if no suitable axis exists.<br>
<br>
The warning is<br>
<br>
  MatplotlibDeprecationWarning: Adding an axes using the same arguments<br>
  as a previous axes currently reuses the earlier instance.  In a future<br>
  version, a new instance will always be created and returned.<br>
  Meanwhile, this warning can be suppressed, and the future behavior<br>
  ensured, by passing a unique label to each axes instance.<br>
<br>
For example, to plot the frequency response a linear dynamical system<br>
(AKA Bode plot of the system), the relevant function could be simplified<br>
to:<br>
<br>
    def bode_plot(g):<br>
       freq, mag, phase = freq_resp(g)<br>
       subplot(211)<br>
       semilogx(freq, 20*log10(mag))<br>
       subplot(212)<br>
       semilogx(freq, phase)<br>
<br>
We've replaced that with code like this:<br>
<br>
    def bode_plot(g):<br>
       freq, mag, phase = freq_resp(g)<br>
<br>
       ax_mag = None<br>
       ax_phase = None<br>
       for ax in gcf.axes():<br>
           if ax.get_label() == 'control-bode-magnitude':<br>
             ax_mag = ax<br>
           elif ax.get_label() == 'control-bode-phase':<br>
             ax_phase = ax<br>
<br>
       if ax_mag is None or ax_phase is None:<br>
         clf()<br>
         ax_mag = subplot(211, label = 'control-bode-magnitude')<br>
         ax_phase = subplot(212, label = 'control-bode-phase)<br>
<br>
       ax_mag.semilogx(freq, 20*log10(mag))<br>
       ax_phase.semilogx(freq, phase)<br>
<br>
This means that calls like<br>
<br>
    bode_plot(g)<br>
    bode_plot(h)<br>
<br>
will show the response of g and h on the same figure.<br>
<br>
Is this method of using labels to check for existing axes reasonable?<br>
Is there a better way?<br>
<br>
Actual code exhibiting warnings at [2]; new code at [3].  The latter<br>
link is to an as-yet unmerged branch, and may disappear.<br>
<br>
Thanks,<br>
<br>
Rory<br>
<br>
[1] <a href="https://github.com/python-control/python-control" rel="noreferrer" target="_blank">https://github.com/python-<wbr>control/python-control</a><br>
[2] <a href="https://github.com/python-control/python-control/blob/af8d4ee39dfa574c2b3b335f4cdb4be858ae469a/control/freqplot.py#L175" rel="noreferrer" target="_blank">https://github.com/python-<wbr>control/python-control/blob/<wbr>af8d4ee39dfa574c2b3b335f4cdb4b<wbr>e858ae469a/control/freqplot.<wbr>py#L175</a><br>
[3] <a href="https://github.com/murrayrm/python-control/blob/dc1820a4e64d73937c7de8df078c41ec1773e048/control/freqplot.py#L138" rel="noreferrer" target="_blank">https://github.com/murrayrm/<wbr>python-control/blob/<wbr>dc1820a4e64d73937c7de8df078c41<wbr>ec1773e048/control/freqplot.<wbr>py#L138</a><br>
______________________________<wbr>_________________<br>
Matplotlib-users mailing list<br>
<a href="mailto:Matplotlib-users@python.org">Matplotlib-users@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/matplotlib-users" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/matplotlib-<wbr>users</a><br>
</blockquote></div><br></div>