Wakan Tanka
Wakan Tanka

Reputation: 8042

Dynamically change y limit

Here is the code:

#!/usr/bin/python
import numpy as np
import matplotlib.pyplot  as plt
from   matplotlib.widgets import Slider
########################################
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 3
s = a0*np.sin(2*np.pi*f0*t)
########################################
plt.close('all')
fig, ax = plt.subplots(nrows=2, ncols=1)
plt.subplots_adjust(bottom=0.30)
########################################
line0, = ax[0].plot(t,s, lw=2, color='red',   label="red")
# IS THIS LINE NECESSARY
line1, = ax[1].plot(t,s, lw=2, color='green', label="green")
########################################
ax[0].set_xlim([0, 1])
ax[0].set_ylim([-10, 10])
########################################
axcolor = 'lightgoldenrodyellow'
left   = 0.25
bottom = 0.20
width  = 0.65
# DIFFERENT SCALE
# height = fig.get_size_inches()[1] * 0.1
height = 0.03
vgap   = 0.02
print "fig height %s" % fig.get_size_inches()[1]

f1  = plt.axes([left, bottom-0*(height+vgap), width, height], axisbg=axcolor)
a1  = plt.axes([left, bottom-1*(height+vgap), width, height], axisbg=axcolor)

sf1 = Slider(f1, 'Freq1', 0.1, 30.0, valinit=f0)
sa1 = Slider(a1, 'Amp1',  0.1, 10.0, valinit=a0)
########################################
def update1(val):
    amp  = sa1.val
    freq = sf1.val

    line0.set_ydata(amp*np.sin(2*np.pi*freq*t))
    line1.set_ydata(2*amp*np.sin(2*np.pi*freq*t))

sf1.on_changed(update1)
sa1.on_changed(update1)

plt.show()

When amplitude/frequency of wave on 1st plot is changed using slider it also changes the amplitude/frequency of wave on 2nd plot (it doubles the amplitude on 2nd plot). Problem is when amplitude of 1st plot is exceeds 3. In that case it simply does not fits in to the 2nd plot. Because it has the y range from -6 to 6. y limit is not enough

Three questions:

  1. Where does the -6 and 6 number comes from? I did not set them.
  2. How can be the y limit changed dynamically on 2nd plot? I can set it using e.g. ax[1].set_ylim([-20, 20]) but I want something more general. Let's pretend that 2nd plot can have y value more than 20 (it might be result of some complicated computation which is not known during plotting). Basically when amplitude of 1st plot is small value the y of 2nd plot should shrink and when amplitude is large y should expand.
  3. Can I leave the 2nd plot empty until I change the amplitude/frequency of the 1st plot. In other words can this be also (please show both cases, one when 2nd plot is already existing and another when it is not) achieved without creating 2nd plot first using following line: line1, = ax[1].plot(t,s, lw=2, color='green', label="green")

Upvotes: 4

Views: 3706

Answers (1)

J Richard Snape
J Richard Snape

Reputation: 20344

EDIT: User hitzg provided some helpful improvements to the original answer in comments - I've taken these and generally made the answer deeper and more generic.


I'll take the questions one by one

  1. Where did 6 and -6 come from?

That's matplotlib's best guess, based on the data that you plotted on the axis originally. If you don't specify any values, it will fit the axes limits the best it can.

For those with deeper interest - these lines of code are where matplotlib defaults to auto-scaling the axes if no scales are specified, with this line actually applying the auto scaling.

  1. How can be the y limit changed dynamically on 2nd plot? I

You're almost there. In update1(), add ax[1].autoscale_view() which, as you've added new data, must be preceded by ax[1].relim(). This will let matplotlib autoscale the axes to suit the values of whatever data you have plotted on them and is a generic solution. If you need 'manual' but dynamic control of the axes yourself, see my original suggestion below.

N.B. In the original version, I suggested simply writing ax[1].set_ylim(-2.2*amp,2.2*amp) or something similar where 2.2 was an arbitrary factor: the limits of the sine wave amplitude as set by the slider plus a little bit (the 0.2)

  1. Can I leave the 2nd plot empty until I change the amplitude/frequency of the 1st plot?

Yes. One way is to replace

line1, = ax[1].plot(t,s, lw=2, color='green', label="green")

with

line1, = ax[1].plot([],[], lw=2, color='green', label="green")

This creates a line object on the axes that you can update, but which initially has no data. N.B. I originally suggested using 0,0 instead of [],[], but this would plot a point at 0,0 which might not suit your purposes.

And then in update1() put

line1.set_xdata(t)

Final code for update1() function

def update1(val):
    amp  = sa1.val
    freq = sf1.val

    line0.set_ydata(amp*np.sin(2*np.pi*freq*t))
    line1.set_xdata(t)
    line1.set_ydata(2*amp*np.sin(2*np.pi*freq*t))
    ax[1].relim()
    ax[1].autoscale_view()

Upvotes: 4

Related Questions