4

I need your help. Please consider the code below, which plots a sinusoid using pylab in IPython. A slider below the axis enables the user to adjust the frequency of the sinusoid interactively.

%pylab
# setup figure
fig, ax = subplots(1)
fig.subplots_adjust(left=0.25, bottom=0.25)

# add a slider
axcolor = 'lightgoldenrodyellow'
ax_freq = axes([0.3, 0.13, 0.5, 0.03], axisbg=axcolor)
s_freq = Slider(ax_freq, 'Frequency [Hz]', 0, 100, valinit=a0)

# plot 
g = linspace(0, 1, 100)
f0 = 1
sig = sin(2*pi*f0*t)
myline, = ax.plot(sig)

# update plot
def update(value):
    f = s_freq.val
    new_data = sin(2*pi*f*t)
    myline.set_ydata(new_data)     # crucial line
    fig.canvas.draw_idle()

s_freq.on_changed(update)

Instead of the above, I need to plot the signal as vertical lines, ranging from the amplitude of each point in t to the x-axis. Thus, my first idea was to use vlines instead of plot in line 15:

myline = ax.vlines(range(len(sig)), 0, sig)

This solution works in the non-interactive case. The problem is, plot returns an matplotlib.lines.Line2D object, which provides the set_ydata method to update data interactively. The object returned by vlines is of type matplotlib.collections.LineCollection and does not provide such a method. My question: how do I update a LineCollection interactively?

MaxPowers
  • 5,235
  • 2
  • 44
  • 69
  • 3
    Probably `set_offsets`or `set_verts`. – tacaswell Mar 29 '15 at 17:00
  • 1
    I could not get `set_offset` or `set_verts` to work. `set_segments` does work but you must provide it in the format of a 3D array where each element is of the form `[[x, ymin], [x, ymax]]`. – Aaron Voelker Mar 31 '20 at 16:32

2 Answers2

0

Using @Aaron Voelker's comment of using set_segments and wrapping it up in a function:

def update_vlines(*, h, x, ymin=None, ymax=None):
    seg_old = h.get_segments()
    if ymin is None:
        ymin = seg_old[0][0, 1]
    if ymax is None:
        ymax = seg_old[0][1, 1]

    seg_new = [np.array([[xx, ymin],
                         [xx, ymax]]) for xx in x]

    h.set_segments(seg_new)

Analog for hlines:

def update_hlines(*, h, y, xmin=None, xmax=None):
    seg_old = h.get_segments()
    if xmin is None:
        xmin = seg_old[0][0, 0]
    if xmax is None:
        xmax = seg_old[0][1, 0]

    seg_new = [np.array([[xmin, yy],
                         [xmax, yy]]) for yy in y]

    h.set_segments(seg_new)
scleronomic
  • 4,392
  • 1
  • 13
  • 43
0

I will give examples for vlines here.

If you have multiple lines, @scleronomic solution works perfect. You also might prefer one-liner:

myline.set_segments([np.array([[x, x_min], [x, x_max]]) for x in xx])

If you need to update only maximums, then you can do this:

def update_maxs(vline):
    vline[:,1] = x_min, x_max
    return vline

myline.set_segments(list(map(update_maxs, x.get_segments())))

Also this example could be useful: LINK