0

I've been trying to recreate this animation in matplotlib by roughly following the answer to this thread: matplotlib animation multiple datasets (It's a monte carlo approximation of PI).

I'd like to know how I can accumulate the frames in my plot so that each frame adds additional dots on top of the previously plotted ones, so that the dots get more and more dense as time progresses. Also, do I have to fundamentally rewrite my code, since the approximation of PI requires that I carry over the number of blue and red points counted of every iteration?

fig, ax = plt.subplots(figsize=(5,5))

plt.rcParams['animation.ffmpeg_path'] = r'C:\FFmpeg\bin\ffmpeg'

b_sc = ax.scatter([],[],s=1,c='b')
r_sc = ax.scatter([],[],s=1,c='r')


def init():

    ax.set_ylim(0,1)
    ax.set_xlim(0,1)

    return b_sc,r_sc


def update(frame):

    org = np.array([0,0])
    r, b = (0,0)

    np.random.seed(frame)

    dots = np.random.rand(100,2)
    blue = np.array([dot for dot in dots if np.linalg.norm(org-dot) <= 1])
    red = np.array([dot for dot in dots if np.linalg.norm(org-dot) > 1])

    dr, db = red.shape[0], blue.shape[0]

    r += dr
    b += db

    pi = b/(frame*(100))*4

    if db != 0:
        b_sc.set_offsets(blue)
    if dr != 0:
        r_sc.set_offsets(red)

#    ax.figure.canvas.draw()

    return b_sc,r_sc


FFwriter = animation.FFMpegWriter(fps=144)

ani = FuncAnimation(fig, update, frames=range(1,100), interval=1,
                    init_func=init, blit=True)

plt.show()
5
  • Maybe you could keep track of all points that you found in a separate list and plot that list. So every update function would extend the "gathering list" which grows every iteration with the new data you now get in b_sc and r_sc Commented Sep 16, 2019 at 13:49
  • I think if I did that I could just use an iterable with an increasingly big number of vectors, like in the example of the wikipedia page, and then just keep the random seed constant. It would have the same effect visually. Maybe FuncAnimate is really just built for the purpose of animating consecutive frames. The only approach I can think of is passing each iteration to init() somehow? Commented Sep 16, 2019 at 14:11
  • Then why not do that? How would passing to init() help here? The documentation says the init() is used to draw a clear frame. Commented Sep 16, 2019 at 14:28
  • I understood init as the function which contains the elements of the plot which do not change for blitting (better preformance, bc you don't have to redraw everything on every frame?). And yeah, I know I can make it look just the way I want to, but I wanted to know whether overlaying consecutive frames is generally possible. It might be handy, if you want to partition your ram usage into more managable chunks. Commented Sep 16, 2019 at 15:49
  • It seems you want to append the new random numbers created in each frame to a global list and plot this complete list in each frame. Commented Sep 16, 2019 at 16:28

1 Answer 1

1

You can actually leverage the mutability of lists for this animation using the extend method.

fig, ax = plt.subplots(figsize=(5,5))

# plt.rcParams['animation.ffmpeg_path'] = r'C:\FFmpeg\bin\ffmpeg'

b_sc = ax.scatter([],[],s=1,c='b')
r_sc = ax.scatter([],[],s=1,c='r')
txt = ax.text(.9, .1, '', backgroundcolor='w') # let's print pi on the animation!

# init empty lists
blue=[] 
red=[]

def init():

    ax.set_ylim(0,1)
    ax.set_xlim(0,1)

    return b_sc,r_sc


def update(frame):

    org = np.array([0,0])
    r, b = (0,0)

    np.random.seed(frame)

    dots = np.random.rand(100,2)
    
    # swap the numpy computation for a list extention
    blue.extend([dot for dot in dots if np.linalg.norm(org-dot) <= 1])
    red.extend([dot for dot in dots if np.linalg.norm(org-dot) > 1])

    # don't forget to change this to `len` since `shape` no longer applies
    dr, db = len(red), len(blue)

    r += dr
    b += db

    pi = b/(frame*(100))*4
    txt.set_text('{:.3f}'.format(pi))

    if db != 0:
        b_sc.set_offsets(blue)
    if dr != 0:
        r_sc.set_offsets(red)

#    ax.figure.canvas.draw()

    return b_sc, r_sc, txt


# FFwriter = animation.FFMpegWriter(fps=144)

ani = animation.FuncAnimation(fig, update, frames=range(1,100), interval=20,
                    init_func=init, blit=True)

enter image description here

This little edit made things run great for me. What a fun way to compute pi!

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

2 Comments

Thanks and +1 for enthusiasm
I'm glad it helped, if you think it answered your question don't forget to accept it : )

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.