Reputation: 821
I am exploring live graphs with matplotlib animations. Currently, I'm just adding a random integer to an array, and having my plot update based on the new number. The issue is that I seem to be getting an extra value drawn to the plot.
I have the following code:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
xs = []
ys = []
def animate(i, xs, ys):
x = i + 1
y = random.randint(1, 50)
print '{}: {}, {}'.format(i, x, y)
xs.append(x)
ys.append(y)
ax1.clear()
ax1.plot(xs, ys)
ani = animation.FuncAnimation(fig,
animate,
frames = 10,
fargs = (xs, ys),
repeat = False)
plt.show()
I only want 10 values to be drawn, so I set frames = 10
in the FuncAnimate
call. However, the print statement is outputting an 11th value:
So it's clear that 11 frames are being generated, rather than 10. Looking at the documenation for FuncAnimate, I can't see a reason why this would be happening.
Can anyone tell me what I'm missing?
Thanks!
Upvotes: 1
Views: 1154
Reputation: 12496
Maybe it is not the most elegant way to solve it, but you could use a flag as a workaround in order to avoid the run of the animation in the very first loop:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
xs = []
ys = []
flag = 0
def animate(i, xs, ys):
global flag
if i == 0 and flag == 0:
flag = 1
return
if flag == 1:
x = i + 1
y = random.randint(1, 50)
print '{}: {}, {}'.format(i, x, y)
xs.append(x)
ys.append(y)
ax1.clear()
ax1.plot(xs, ys)
ani = animation.FuncAnimation(fig,
animate,
frames = 10,
fargs = (xs, ys),
repeat = False)
plt.show()
Output:
0: 1, 37
1: 2, 2
2: 3, 46
3: 4, 39
4: 5, 30
5: 6, 47
6: 7, 16
7: 8, 3
8: 9, 3
9: 10, 49
Plot:
EDIT:
I have done some research.
This behavior is due to the fact you (and I) do not specified the init_func
parameter of the FuncAnimation
. As you can read from the documentation:
init_func
: callable, optional A function used to draw a clear frame. If not given, the results of drawing from the first item in the frames sequence will be used. This function will be called once before the first frame. The required signature is:
def init_func() -> iterable_of_artists
If
blit == True
, init_func must return an iterable of artists to be re-drawn. This information is used by the blitting algorithm to determine which parts of the figure have to be updated. The return value is unused ifblit == False
and may be omitted in that case.
If you do not specify the init_func
, the first frame is repeated (to be used as a initialization frame).
That said, I think the most proper way to avoid the repetition is to call a init
function where you do not plot anything:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
xs = []
ys = []
def init():
ax1.clear()
def animate(i, xs, ys):
x = i + 1
y = random.randint(1, 50)
print '{}: {}, {}'.format(i, x, y)
xs.append(x)
ys.append(y)
ax1.clear()
ax1.plot(xs, ys)
ani = animation.FuncAnimation(fig = fig,
func = animate,
init_func = init,
frames = 10,
fargs = (xs, ys),
repeat = False)
plt.show()
Output:
0: 1, 12
1: 2, 25
2: 3, 20
3: 4, 49
4: 5, 49
5: 6, 28
6: 7, 26
7: 8, 49
8: 9, 10
9: 10, 2
Animation:
Upvotes: 3