Wakan Tanka
Wakan Tanka

Reputation: 8052

set_ydata for subplots OR why is figure containing two subplots significantly slower than figure without subpots

I've extended following example to use multiple subplots. Here is my 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)
########################################
ax[0].plot(t,s, lw=2, color='red', label="red")
ax[1].plot(t,s, lw=2, color='green', label="green")
########################################
# plt.axis([0, 1, -10, 10])
ax[0].set_xlim([0, 1])
ax[0].set_ylim([-10, 10])

ax[1].set_xlim([0, 1])
ax[1].set_ylim([-10, 10])
########################################
axcolor = 'lightgoldenrodyellow'
f1  = plt.axes([0.25, 0.20,  0.65, 0.03], axisbg=axcolor)
a1  = plt.axes([0.25, 0.15, 0.65, 0.03], axisbg=axcolor)

f2  = plt.axes([0.25, 0.1,  0.65, 0.03], axisbg=axcolor)
a2  = plt.axes([0.25, 0.05,  0.65, 0.03], axisbg=axcolor)

sf1 = Slider(f1, 'Freq1', 0.1, 30.0, valinit=f0)
sa1 = Slider(a1, 'Amp1',  0.1, 10.0, valinit=a0)

sf2 = Slider(f2, 'Freq2', 0.1, 30.0, valinit=f0)
sa2 = Slider(a2, 'Amp2',  0.1, 10.0, valinit=a0)
########################################
def update1(val):
    amp  = sa1.val
    freq = sf1.val

    # DOES NOT WORKS - set_ydata DOES NOT EXISTS
    # ax[1].set_ydata(amp*np.sin(2*np.pi*freq*t))
    # ax[2].set_ydata(amp*np.sin(2*np.pi*freq*t))

    # WORKS BUT IT SEEMS SLOW
    s = amp*np.sin(2*np.pi*freq*t)
    ax[0].clear()
    ax[0].plot(t,s, lw=2, color='red', label="red")
    ax[0].set_xlim([0, 1])
    ax[0].set_ylim([-10, 10])

    # THIS HAS NO EFFECT ON SPEED
    fig.canvas.draw_idle()

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


def update2(val):
    amp  = sa2.val
    freq = sf2.val

    # DOES NOT WORKS - set_ydata DOES NOT EXISTS
    # ax[1].set_ydata(amp*np.sin(2*np.pi*freq*t))
    # ax[2].set_ydata(amp*np.sin(2*np.pi*freq*t))

    # WORKS BUT IT SEEMS SLOW
    s = amp*np.sin(2*np.pi*freq*t)
    ax[1].clear()
    ax[1].plot(t,s, lw=2, color='green', label="green")
    ax[1].set_xlim([0, 1])
    ax[1].set_ylim([-10, 10])

    # THIS HAS NO EFFECT ON SPEED
    fig.canvas.draw_idle()

sf2.on_changed(update2)
sa2.on_changed(update2)

plt.show()

The only problem is that it is slower (when the slider is clicked) than the original. I suspect this is caused by bunch of code that is used in update1 and update2 functions. I do not know how to rewrite it more effectively. The original example is using the set_ydata function but seems that subplots does not have this function. I've been also thinking about one update function for all four sliders, but I do not know if is it possible to distinguish the object on which the update was triggered and handle it inside the function. Thanks

Upvotes: 0

Views: 952

Answers (1)

hitzg
hitzg

Reputation: 12711

You try to call set_ydata on the ax[0] (or ax[1], respectively). This obviously does not work, as these are axes instances. set_ydata is a method of Line2D artists. In the example that you have linked a line l is created in the beginning, which receives the new y data in the update function. You forgot that in your code.

So you need to create the lines:

line0, = ax[0].plot(t,s, lw=2, color='red')
line1, = ax[1].plot(t,s, lw=2, color='green')

in the beginning and then in your update functions you can call:

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

or

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

Note: The idea here is to only change the y coordinates of your lines. With your workaround, you deleted the entire graph and recreated it. This is more work and therefore it makes things slower.

Upvotes: 1

Related Questions