Steve Iron
Steve Iron

Reputation: 261

Python Matplotlib animate bar and plot in one picture

Maybe you can help me. I'm trying to animate some bars together with a plot in matplotlib. For example, here's some code (only the bars) which is working fine:

from matplotlib import pyplot as plt
from matplotlib.animation import ArtistAnimation
import numpy as np

fig = plt.figure()
x = [1,2,3,4,5]
y = [5,7,2,5,3]

y_steps = []
for i in range(len(y)):
    y_steps.append(np.linspace(0, y[i], 50))

data = []
for i in range(len(y_steps[0])):
    data.append([y_steps[j][i] for j in range(len(y))])

ims = []
for i in range(len(y_steps[0])):
    pic = plt.bar(x, data[i], color='c')
    ims.append(pic)

anim = ArtistAnimation(fig, ims, interval=40)
plt.show()

But now I want some lines to grow together with the bars. I've tried a lot and googled a lot, but I am not able to make it work. For your understandig, I'm pasting my idea (of the not-working-code) here:

from matplotlib import pyplot as plt
from matplotlib.animation import ArtistAnimation
import numpy as np

fig = plt.figure()

x = [1,2,3,4,5]
y = [5,7,2,5,3]

y_steps = []
for i in range(len(y)):
    y_steps.append(np.linspace(0, y[i], 50))

data = []
for i in range(len(y_steps[0])):
    data.append([y_steps[j][i] for j in range(len(y))])

ims = []
for i in range(len(y_steps[0])):
    pic_1 = plt.bar(x, data[i], color='c')
    pic_2 = plt.plot(x, data[i], color='r')
    ims.append([pic_1, pic_2])

anim = ArtistAnimation(fig, ims, interval=40)
plt.show()

It looks like all the pictures, saved in ims, are shown at once and there's no animation.

Maybe someone of you can help me. Thanks a lot.

Upvotes: 2

Views: 4966

Answers (1)

unutbu
unutbu

Reputation: 880249

When using ArtistAnimation(fig, ims, ...), ims is expected to be a list of Artists. [pic_1, pic_2] is a list, not an Artist. ims.append([pic_1, pic_2]) appends the list as a single object to ims.

The simplest way to fix the problem is to change ims.append([pic_1, pic_2]) to

ims.extend([pic_1, pic_2])

because ims.extend([pic_1, pic_2]) appends pic_1 and pic_2 into ims separately.

You can see the difference between append and extend by playing with this example:

In [41]: x = []

In [42]: x.append([1, 2])

In [43]: x
Out[43]: [[1, 2]]   # x is a list containing 1 item which happens to be a list

In [44]: y = []

In [45]: y.extend([1,2])

In [46]: y
Out[46]: [1, 2]     # y is a list containing 2 items

Although that provides a quick fix, the result is rather "blinky". To make a smoother animation, avoid calling plt.bar and plt.plot so many times. It is more efficient to call each once, and then use the Rectangle.set_height and Line2D.set_data methods to modify the existing Rectangles and Line2Ds:

from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np

fig = plt.figure()

x = [1,2,3,4,5]
y = [5,7,2,5,3]

data = np.column_stack([np.linspace(0, yi, 50) for yi in y])

rects = plt.bar(x, data[0], color='c')
line, = plt.plot(x, data[0], color='r')
plt.ylim(0, max(y))
def animate(i):
    for rect, yi in zip(rects, data[i]):
        rect.set_height(yi)
    line.set_data(x, data[i])
    return rects, line

anim = animation.FuncAnimation(fig, animate, frames=len(data), interval=40)
plt.show()

Upvotes: 4

Related Questions