0

matplotlib animation.save does not give the correct frames and iterations.

When I run the code in Jupyter with the %matplotlib notebook backend my plot runs perfectly and ends on the desired end frame (n=100). But when I playback the saved mp4, I end on n=99.

I changed the frames=130 and again notebook backend ends perfectly on n=100, implying that a.event_source.stop() was called correctly. But when I review the mp4, it ends on n=129 instead.

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

n = 100
x = np.random.randn(n)

# create the function that will do the plotting, where curr is the current frame
def update(curr):
    # check if animation is at the last frame, and if so, stop the animation a
    if curr == n:
        a.event_source.stop()
    plt.cla()
    bins = np.arange(-4, 4, 0.5)
    plt.hist(x[:curr], bins=bins)
    plt.axis([-4,4,0,30])
    plt.gca().set_title('Sampling the Normal Distribution')
    plt.gca().set_ylabel('Frequency')
    plt.gca().set_xlabel('Value')
    plt.annotate('n = {}'.format(curr), [3,27])

fig = plt.figure()
a = animation.FuncAnimation(fig, update, interval=10, frames=None)
a.save('norm_dist.mp4')
4
  • 1
    The frame where curr == n is not saved because you have stopped the animation. But it is still shown, because your code proceeds. You may put a return directly after a.event_source.stop() to not let the function continue execution until the end. Commented Jul 30, 2019 at 20:00
  • adding the return after a.event_source.stop() did not change the outcome. neither did placing the stop to after the updating of the plot. Commented Jul 31, 2019 at 7:37
  • Adding return after a.event_source.stop() works for me - you get n=99 in the last frame on screen and in the saved file. Placing the stop to after the updating of the plot wouldn't help. Commented Jul 31, 2019 at 10:50
  • I finally understood what is going on with both yours and tsnowlan's feed back. In short, 100 frames were done. My animation did exactly the 100 slices I wanted it to. But I was thrown off by the display not showing n=100 because the code asked it to stop at 99. I should have done a +1 to the annotation to account for zero index. Thanks. Commented Jul 31, 2019 at 16:14

1 Answer 1

1

Looking at the docs, if frames is set to none it uses itertools.count(interval) as the number of frames to use. The reason you're seeing 99 instead of 100 is because it is zero indexed.

You can use frames=n to set the number of frames you want and test that it's correct with:

ffprobe -v error -select_streams v:0 -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1 norm_dist.mp4

So this wasn't it, but for the sake of posterity:

It looks like the a.event_source.stop() is short circuiting the function. Try moving the check to the end and see if that gives you the frame count you expect.

Sign up to request clarification or add additional context in comments.

2 Comments

thanks for the suggestion. Yes I did try that and it does not change any of the results. The extra frames are still appearing when frames are set to 130, and it still does not stop at 100 when I set frames to None
I finally understood what is going on with both yours and ImportanceOfBeingEarnest's feed back. In short, 100 frames were done. My animation did exactly the 100 slices I wanted it to. But I was thrown off by the display not showing n=100 because the code asked it to stop at 99. I should have done a +1 to the annotation to account for zero index. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.