I'm animating a scatter plot with a code below. It reads data from a .csv file "sample.csv" and animates it by using np.roll in update function.
import numpy as np
import matplotlib.pyplot as plt
from numpy import genfromtxt
from matplotlib.animation import FuncAnimation
my_data = genfromtxt("sample.csv", delimiter=",", skip_header=1) # reading data
fig, ax = plt.subplots()
xdata, ydata = [], []
line, = ax.plot([], [], ".", markersize=14)
plt.grid()
def init():
ax.set_xlim(0, 50)
ax.set_ylim(0, 60)
return line,
def update(frame):
my_data[:,0] = np.roll(my_data[:,0],1) # moving graph
gap_loc = [19,20,21] # location of a gap
my_data[gap_loc, 1] = np.nan # creating a gap in graph
xdata.append(my_data[:,0])
ydata.append(my_data[:,1])
line.set_data(xdata, ydata)
return line,
ani = FuncAnimation(fig, update, frames=np.arange(0,50,1), init_func=init, blit=True)
plt.show()
The result looks like that:
As you see there is a gap which moves together with the remaining points. However, what I want to achieve is that the gap was stationary at the locations on the horizontal axis: 19,20,21.
How can I achieve this effect?
Below please find a dataset, I'm using for this animation.
Day,Var 1,Var 2
1,2,12
2,4,19
3,6,20
4,8,25
5,10,25
6,12,33
7,14,40
8,16,47
9,18,49
10,20,50
11,22,52
12,24,55
13,26,65
14,28,82
15,30,100
16,32,100
17,34,110
18,36,117
19,38,140
20,40,145
21,42,164
22,44,170
23,46,198
24,48,200
25,50,210
26,48,210
27,46,211
28,44,216
29,42,267
30,40,317
31,38,325
32,36,335
33,34,337
34,32,347
35,30,356
36,28,402
37,26,410
38,24,448
39,22,449
40,20,457
41,18,463
42,16,494
43,14,500
44,12,501
45,10,502
46,8,514
47,6,551
48,4,551
49,2,558
50,0,628
Define the gap when you load the data, and do so in the x column rather than the y:
# imports
my_data = genfromtxt("sample.csv", delimiter=",", skip_header=1) # reading data
gap_loc = [19,20,21] # location of a gap
my_data[gap_loc, 0] = np.nan # creating a gap in graph
# plotting code
So now when you roll the x column, there will always be np.nan at the x values [19, 20, 21], regardless of what the y coordinate is. You can use print(my_data) within the update function to make clear what was going on each iteration.
Here is the result:
Also, I think you are over-plotting because you continually expand xdata and ydata using append. I ended up just removing the xdata and ydata and doing:
def update(frame):
my_data[:,0] = np.roll(my_data[:,0],1) # moving graph
line.set_data(my_data[:,0], my_data[:,1])
return line,
Related
I'm doing a school project on evolutionary algorithms. I applied 2D particle swarm optimazation on an problem. I modified the PSO function such that I get as output:
a vector with the best parameters
the optimized solution
a list of array's that contains the x-values of the particles (lijst1)
a list of array's that contains the y-values of the particles (lijst2)
To visualise it, I wrote a loop that iterates through the list and plots the particles in 2D space. This not an elegant solution so I want to use animate from matplotlib.
I want to achieve that each frame shows an iteration of the PSO algorithm or in other words each frame should plot lijst1[i] and lijst2[i].
To give you guys an idea of the output I ran the algorithm a few iterations with few particles.
Lijst1:
[array([-3. , -1.03182383, -1.29765138, -2.63259751, 1.83881941]), array([-3. , -0.99308702, -1.61309168, -1.99811905, 1.31329269]), array([-2.26835254, -1.00436484, -1.48864562, -1.35639631, -0.49339506]), array([-1.89641446, -1.05791927, -1.22609973, -0.94357677, -1.416652 ])]
Lijst2:
[array([ 6. , 6. , 2.25620012, -0.49762621, 1.20955011]), array([ 5.39456692, 5.66213386, 1.53168852, -0.23988646, 1.97288628]), array([3.70817355, 4.68329424, 1.37540136, 0.88142455, 1.08398406]), array([2.60343502, 2.80393532, 1.67560348, 2.0280248 , 0.29435041])]
I used the sample code from the matplotlib website and modified it. Here is where I need help:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
%matplotlib notebook
n = 4
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')
def init():
ax.set_xlim(-1,1)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
xdata.append(lijst1[frame])
ydata.append(lijst2[frame])
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0,n-1,n), interval=1000, init_func=init, blit=True)
plt.show()
What I first wanted to achieve is to plot all the frames in the same figure. The lijst1 and lijst2 have 4 indices. So I thought let the frames denote [0,1,2,3] so that each frame lijst1[i] and lijst[i] get appended to the data. In the end probably my whole figure will be filled with particles, but that is okay for now.
For some reason I don't get input at all. I'm not sure why this is. Could I please get some feedback on this?
Question: How do I modify the animation function such that I visualise the convergence process of the PSO algorithm?
Thanks in advance
I wrote the code with the understanding that your question is to loop through the contents of the list data presented to you. The change from your code is to set the range of the axes. I also removed the initialization function as I thought it was unnecessary. I added a line from the scatter plot format as a graph setting. This is just a modification to check the animation.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
%matplotlib notebook
# from IPython.display import HTML
import numpy as np
from matplotlib.animation import PillowWriter
lijst1 = [np.array([-3. , -1.03182383, -1.29765138, -2.63259751, 1.83881941]), np.array([-3. , -0.99308702, -1.61309168, -1.99811905, 1.31329269]), np.array([-2.26835254, -1.00436484, -1.48864562, -1.35639631, -0.49339506]), np.array([-1.89641446, -1.05791927, -1.22609973, -0.94357677, -1.416652 ])]
lijst2 = [np.array([ 6. , 6. , 2.25620012, -0.49762621, 1.20955011]), np.array([ 5.39456692, 5.66213386, 1.53168852, -0.23988646, 1.97288628]), np.array([3.70817355, 4.68329424, 1.37540136, 0.88142455, 1.08398406]), np.array([2.60343502, 2.80393532, 1.67560348, 2.0280248 , 0.29435041])]
n = 4
fig, ax = plt.subplots()
ax = plt.axes(xlim=(-5,5), ylim=(-2, 10))
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro--', lw=2)
def init():
ax.set_xlim(-1,1)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
# xdata.append(lijst1[frame])
# ydata.append(lijst2[frame])
xdata = lijst1[frame].tolist()
ydata = lijst2[frame].tolist()
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=len(lijst1), interval=1000, blit=True)# init_func=init,
plt.show()
ani.save('swarm_ani.gif', writer='pillow')
# plt.close()
# HTML(ani.to_html5_video())
I am acquiring data from an external device and I am plotting it in real-time using matplotlib.animation.FuncAnimation. For this measurement, it is important to keep the bottom y-axis limit at 0, but keeping the upper limit free. The device itself returns two sets of data for this measurement so I am animating both sets at the same time, hence the subplots.
Searching online suggests using axes.set_ylim(bottom=0), both in this question and this one. However their solutions do not work for me.
The autoscalling that's already part of the code is using axes.relim() and axes.autoscale_view(True, True, True), from the answer to another question which I have since forgotten. Messing with these lines of code seems to fix the viewing window but it no longer scales with the data. The data could then animate itself 'off-screen'.
I've recreated below the essence of what the acquisition (ideally) looks like since it's easier then using multiple files.
I am assuming that the problem lies in animate_corr(i) with the scaling. The rest is of the code is simply getting and plotting the data.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
#used to generate test data
import random
pi = np.pi
factor = 1
random.seed(123)
#procedural data generation for left plot
def get_data(data):
global mu
global b
global index
global factor
b=b*0.99
factor=factor*1.01
new_data = [factor*(((1-((1/(2*b))*np.exp(-1*(abs(i-mu))/b)))*random.random())+10) for i in index]
return new_data
#procedural data generation for right plot
def get_data_norm(data):
global mu
global b
global index
new_data = [((1-((1/(2*b))*np.exp(-1*(abs(i-mu))/b)))+10) for i in index]
return new_data
#animation function, assuming problem is here
def animate_corr(i):
global dat
global dat_norm
dat = get_data(dat)
dat_norm = get_data_norm(dat_norm)
#these two not working as expected
axs_corr[0].set_ylim((0, None), auto=True)
axs_corr[1].set_ylim(bottom=0, top=None, auto=True)
line_corr.set_ydata(dat)
line_corr_norm.set_ydata(dat_norm)
#rescales axes automatically
axs_corr[0].relim()
axs_corr[0].autoscale_view(True,True,True)
axs_corr[1].relim()
axs_corr[1].autoscale_view(True,True,True)
return line_corr, line_corr_norm,
#plots definitions
fig_corr, axs_corr = plt.subplots(1,2, sharex=True, figsize=(10,5))
fig_corr.suptitle('Animated Correlation')
#x is fixed
length = 1001
index = np.linspace(-10,10,length)
#laplacian distribution parameters
mu = 0
b = 2
#data
dat = [(1-((1/(2*b))*np.exp(-1*(abs(i-mu))/b)))+10 for i in index]
dat_norm = [(1-(1/(2*b))*np.exp(-(abs(i-mu))/b))+10 for i in index]
#initial plots
line_corr, = axs_corr[0].plot(index, dat)
line_corr_norm, = axs_corr[1].plot(index, dat_norm)
#titles
axs_corr[0].set_title('Random')
axs_corr[1].set_title('No Random')
#axes labels
fig_corr.text(0.51, 0.04, 'Time (ns)', ha='center')
fig_corr.text(0.04, 0.5, 'Coincidinces', va='center', rotation='vertical')
#animation call
ani_corr = animation.FuncAnimation(fig_corr, animate_corr, interval=10, blit=False, save_count=50)
plt.show()
I would like to have both plots have the y-axis limit fixed at 0. So the left one would keep increasing its max value and seeing this reflected in its scale. The right plot would have its dip get sharper and sharper but once its smaller than 0, the plot wouldn't change its scale anymore (since this plot doesn't have its values get larger).
#ivallesp almost had it. Removing axs_corr[0].set_ylim((0, None), auto=True) and axs_corr[1].set_ylim((0, None), auto=True) from before the set_ydata method and placing them after the autoscale_view call, for both plots, made it work as I wanted it too.
The following code should work :D.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
#used to generate test data
import random
pi = np.pi
factor = 1
random.seed(123)
#procedural data generation for left plot
def get_data(data):
global mu
global b
global index
global factor
b=b*0.99
factor=factor*1.01
new_data = [factor*(((1-((1/(2*b))*np.exp(-1*(abs(i-mu))/b)))*random.random())+10) for i in index]
return new_data
#procedural data generation for right plot
def get_data_norm(data):
global mu
global b
global index
new_data = [((1-((1/(2*b))*np.exp(-1*(abs(i-mu))/b)))+10) for i in index]
return new_data
#animation function, assuming problem is here
def animate_corr(i):
global dat
global dat_norm
dat = get_data(dat)
dat_norm = get_data_norm(dat_norm)
#these two not working as expected
axs_corr[0].set_ylim((0, None), auto=True)
axs_corr[1].set_ylim(bottom=0, top=None, auto=True)
line_corr.set_ydata(dat)
line_corr_norm.set_ydata(dat_norm)
#rescales axes automatically
axs_corr[0].relim()
axs_corr[0].autoscale_view(True,True,True)
axs_corr[0].set_ylim(0, None)
axs_corr[1].relim()
axs_corr[1].autoscale_view(True,True,True)
axs_corr[1].set_ylim(0, None)
return line_corr, line_corr_norm,
#plots definitions
fig_corr, axs_corr = plt.subplots(1,2, sharex=True, figsize=(10,5))
fig_corr.suptitle('Animated Correlation')
#x is fixed
length = 1001
index = np.linspace(-10,10,length)
#laplacian distribution parameters
mu = 0
b = 2
#data
dat = [(1-((1/(2*b))*np.exp(-1*(abs(i-mu))/b)))+10 for i in index]
dat_norm = [(1-(1/(2*b))*np.exp(-(abs(i-mu))/b))+10 for i in index]
#initial plots
line_corr, = axs_corr[0].plot(index, dat)
line_corr_norm, = axs_corr[1].plot(index, dat_norm)
#titles
axs_corr[0].set_title('Random')
axs_corr[1].set_title('No Random')
#axes labels
fig_corr.text(0.51, 0.04, 'Time (ns)', ha='center')
fig_corr.text(0.04, 0.5, 'Coincidinces', va='center', rotation='vertical')
#animation call
ani_corr = animation.FuncAnimation(fig_corr, animate_corr, interval=10, blit=False, save_count=50)
plt.show()
I would like to replace part of my plot where the function dips down to '-1' with a dashed line carrying on from the previous point (see plots below).
Here's some code I've written, along with its output:
import numpy as np
import matplotlib.pyplot as plt
y = [5,6,8,3,5,7,3,6,-1,3,8,5]
plt.plot(np.linspace(1,12,12),y,'r-o')
plt.show()
for i in range(1,len(y)):
if y[i]!=-1:
plt.plot(np.linspace(i-1,i,2),y[i-1:i+1],'r-o')
else:
y[i]=y[i-1]
plt.plot(np.linspace(i-1,i,2),y[i-1:i+1],'r--o')
plt.ylim(-1,9)
plt.show()
Here's the original plot
Modified plot:
The code I've written works (it produces the desired output), but it's inefficient and takes a long time when I actually run it on my (much larger) dataset. Is there a smarter way to go about doing this?
You can achieve something similar without the loops:
import pandas as pd
import matplotlib.pyplot as plt
# Create a data frame from the list
a = pd.DataFrame([5,6,-1,-1, 8,3,5,7,3,6,-1,3,8,5])
# Prepare a boolean mask
mask = a > 0
# New data frame with missing values filled with the last element of
# the previous segment. Choose 'bfill' to use the first element of
# the next segment.
a_masked = a[mask].fillna(method = 'ffill')
# Prepare the plot
fig, ax = plt.subplots()
line, = ax.plot(a_masked, ls = '--', lw = 1)
ax.plot(a[mask], color=line.get_color(), lw=1.5, marker = 'o')
plt.show()
You can also highlight the negative regions by choosing a different colour for the lines:
My answer is based on a great post from July, 2017. The latter also tackles the case when the first element is NaN or in your case a negative number:
Dotted lines instead of a missing value in matplotlib
I would use numpy functionality to cut your line into segments and then plot all solid and dashed lines separately. In the example below I added two additional -1s to your data to see that this works universally.
import numpy as np
import matplotlib.pyplot as plt
Y = np.array([5,6,-1,-1, 8,3,5,7,3,6,-1,3,8,5])
X = np.arange(len(Y))
idxs = np.where(Y==-1)[0]
sub_y = np.split(Y,idxs)
sub_x = np.split(X,idxs)
fig, ax = plt.subplots()
##replacing -1 values and plotting dotted lines
for i in range(1,len(sub_y)):
val = sub_y[i-1][-1]
sub_y[i][0] = val
ax.plot([sub_x[i-1][-1], sub_x[i][0]], [val, val], 'r--')
##plotting rest
for x,y in zip(sub_x, sub_y):
ax.plot(x, y, 'r-o')
plt.show()
The result looks like this:
Note, however, that this will fail if the first value is -1, as then your problem is not well defined (no previous value to copy from). Hope this helps.
Not too elegant, but here's something that doesn't use loops which I came up with (based on the above answers) which works. #KRKirov and #Thomas Kühn , thank you for your answers, I really appreciate them
import pandas as pd
import matplotlib.pyplot as plt
# Create a data frame from the list
a = pd.DataFrame([5,6,-1,-1, 8,3,5,7,3,6,-1,3,8,5])
b=a.copy()
b[2]=b[0].shift(1,axis=0)
b[4]=(b[0]!=-1) & (b[2]==-1)
b[5]=b[4].shift(-1,axis=0)
b[0] = (b[5] | b[4])
c=b[0]
d=pd.DataFrame(c)
# Prepare a boolean mask
mask = a > 0
# New data frame with missing values filled with the last element of
# the previous segment. Choose 'bfill' to use the first element of
# the next segment.
a_masked = a[mask].fillna(method = 'ffill')
# Prepare the plot
fig, ax = plt.subplots()
line, = ax.plot(a_masked, 'b:o', lw = 1)
ax.plot(a[mask], color=line.get_color(), lw=1.5, marker = 'o')
ax.plot(a_masked[d], color=line.get_color(), lw=1.5, marker = 'o')
plt.show()
I have a list of pandas DataFrames with 2 columns each. So far I have a function that, when given an index i, it takes the frame corresponding to index i and plots a graph of data from the first column against the data of the second column.
list = [f0,f1,f2,f3,f4,f5,f6,f7,f8,f9]
def getGraph(i):
frame = list[i]
frame.plot(x = "firstColumn",y = "secondColumn")
return 0
My question now is, how do I make this iterate over the list of frames and animate the graphs displaying each one for 0.3 seconds in succession.
Preferably, I would like to use the FuncAnimation class in the animation library which does the heavy lifting and optimizations for you.
Set animate function and init to axes, figure and line:
from matplotlib import pyplot as plt
from matplotlib import animation
import pandas as pd
f0 = pd.DataFrame({'firstColumn': [1,2,3,4,5], 'secondColumn': [1,2,3,4,5]})
f1 = pd.DataFrame({'firstColumn': [5,4,3,2,1], 'secondColumn': [1,2,3,4,5]})
f2 = pd.DataFrame({'firstColumn': [5,4,3.5,2,1], 'secondColumn': [5,4,3,2,1]})
# make a global variable to store dataframes
global mylist
mylist=[f0,f1,f2]
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 5), ylim=(0, 5))
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function of dataframes' list
def animate(i):
line.set_data(mylist[i]['firstColumn'], mylist[i]['secondColumn'])
return line,
# call the animator, animate every 300 ms
# set number of frames to the length of your list of dataframes
anim = animation.FuncAnimation(fig, animate, frames=len(mylist), init_func=init, interval=300, blit=True)
plt.show()
For more info look for the tutorial: https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/
I've got an animation with lines and now I want to label the points.
I tried plt.annotate() and I tried plt.text() but the labes don't move.
This is my example code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def update_line(num, data, line):
newData = np.array([[1+num,2+num/2,3,4-num/4,5+num],[7,4,9+num/3,2,3]])
line.set_data(newData)
plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line,
fig1 = plt.figure()
data = np.array([[1,2,3,4,5],[7,4,9,2,3]])
l, = plt.plot([], [], 'r-')
plt.xlim(0, 20)
plt.ylim(0, 20)
plt.annotate('A0', xy=(data[0][0], data[1][0]))
# plt.text( data[0][0], data[1][0], 'A0')
line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l),
interval=200, blit=True)
plt.show()
Can you help me please?
My next step is:
I have vectors with origin in these Points. These vectors change their length and their direction in each animation step.
How can I animate these?
Without animation this works:
soa =np.array( [ [data[0][0],data[1][0],F_A0[i][0][0],F_A0[i][1][0]],
[data[0][1],data[1][1],F_B0[i][0][0],F_B0[i][1][0]],
[data[0][2],data[1][2],F_D[i][0][0],F_D[i][1][0]] ])
X,Y,U,V = zip(*soa)
ax = plt.gca()
ax.quiver(X,Y,U,V,angles='xy',scale_units='xy',scale=1)
First thanks a lot for your fast and very helpful answer!
My Vector animation problem I have solved with this:
annotation = ax.annotate("C0", xy=(data[0][2], data[1][2]), xycoords='data',
xytext=(data[0][2]+1, data[1][2]+1), textcoords='data',
arrowprops=dict(arrowstyle="->"))
and in the 'update-function' I write:
annotation.xytext = (newData[0][2], newData[1][2])
annotation.xy = (data[0][2]+num, data[1][2]+num)
to change the start and end position of the vectors (arrows).
But what is, wehn I have 100 vectors or more? It is not practicable to write:
annotation1 = ...
annotation2 = ...
.
:
annotation100 = ...
I tried with a list:
...
annotation = [annotation1, annotation2, ... , annotation100]
...
def update(num):
...
return line, annotation
and got this error:
AttributeError: 'list' object has no attribute 'axes'
What can I do? Have you any idea?
I'm coming here from this question, where an annotation should be updated that uses both xy and xytext. It appears that, in order to update the annotation correctly, one needs to set the attribute .xy of the annotation to set the position of the annotated point and to use the .set_position() method of the annotation to set the position of the annotation. Setting the .xytext attribute has no effect -- somewhat confusing in my opinion. Below a complete example:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
fig, ax = plt.subplots()
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
L = 50
theta = np.linspace(0,2*np.pi,L)
r = np.ones_like(theta)
x = r*np.cos(theta)
y = r*np.sin(theta)
line, = ax.plot(1,0, 'ro')
annotation = ax.annotate(
'annotation', xy=(1,0), xytext=(-1,0),
arrowprops = {'arrowstyle': "->"}
)
def update(i):
new_x = x[i%L]
new_y = y[i%L]
line.set_data(new_x,new_y)
##annotation.xytext = (-new_x,-new_y) <-- does not work
annotation.set_position((-new_x,-new_y))
annotation.xy = (new_x,new_y)
return line, annotation
ani = animation.FuncAnimation(
fig, update, interval = 500, blit = False
)
plt.show()
The result looks something like this:
In case that versions matter, this code has been tested on Python 2.7 and 3.6 with matplotlib version 2.1.1, and in both cases setting .xytext had no effect, while .set_position() and .xy worked as expected.
You have the return all objects that changed from your update function. So since your annotation changed it's position you should return it also:
line.set_data(newData)
annotation = plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line, annotation
You can read more about matplotlib animations in this tutorial
You should also specify the init function so that the FuncAnimation knows which elements to remove from the plot when redrawing on the first update. So the full example would be:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Create initial data
data = np.array([[1,2,3,4,5], [7,4,9,2,3]])
# Create figure and axes
fig = plt.figure()
ax = plt.axes(xlim=(0, 20), ylim=(0, 20))
# Create initial objects
line, = ax.plot([], [], 'r-')
annotation = ax.annotate('A0', xy=(data[0][0], data[1][0]))
annotation.set_animated(True)
# Create the init function that returns the objects
# that will change during the animation process
def init():
return line, annotation
# Create the update function that returns all the
# objects that have changed
def update(num):
newData = np.array([[1 + num, 2 + num / 2, 3, 4 - num / 4, 5 + num],
[7, 4, 9 + num / 3, 2, 3]])
line.set_data(newData)
# This is not working i 1.2.1
# annotation.set_position((newData[0][0], newData[1][0]))
annotation.xytext = (newData[0][0], newData[1][0])
return line, annotation
anim = animation.FuncAnimation(fig, update, frames=25, init_func=init,
interval=200, blit=True)
plt.show()
I think I figured out how to animate multiple annotations through a list. First you just create your annotations list:
for i in range(0,len(someMatrix)):
annotations.append(ax.annotate(str(i), xy=(someMatrix.item(0,i), someMatrix.item(1,i))))
Then in your "animate" function you do as you have already written:
for num, annot in enumerate(annotations):
annot.set_position((someMatrix.item((time,num)), someMatrix.item((time,num))))
(You can write it as a traditional for loop as well if you don't like the enumerate way). Don't forget to return the whole annotations list in your return statement.
Then the important thing is to set "blit=False" in your FuncAnimation:
animation.FuncAnimation(fig, animate, frames="yourframecount",
interval="yourpreferredinterval", blit=False, init_func=init)
It is good to point out that blit=False might slow things down. But its unfortunately the only way I could get animation of annotations in lists to work...