I currently have an array G = [x,y,t] where each spatial point (G[0][i], G[1][i]) has a time component t = G[2][i]. The array is sorted by time. I am trying to animate the scatter plot so points show up in chronological order and do not disappear. Here is my current code:
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation
fig = plt.figure(figsize=(10,7))
ax = plt.subplot(111, xlim=(0,1), ylim=(0,1))
def animationUpdate(k):
x = G[0][:k]
y = G[1][:k]
scat.set_offsets(np.c_[x,y])
return scat
anim = FuncAnimation(fig, animationUpdate, frames=10, interval=100, blit=True)
I get the error "'PathCollection' object is not iterable" which I am not sure how to fix. I am also unsure how to arrange it so the points show up with respect to their time component. Do I modify the frames or interval section of FuncAnimation? Thanks!
Related
I am trying to create an animation containing a fixed sphere and a trajectory on the surface of the sphere, with a trail of the trajectory containing the last "windowSize" points in the trajectory.
Now, for the purposes of the code I will show here, I won't have an actual such trajectory, but rather just some random points changing each frame.
I am using matplotlib.animation.FuncAnimation. When I use the option blit=False, the animation works as expected. However, I would like to use blit=True to optimize performance.
When I do that, though, what happens is that nothing seems to happen in the animation, except that when I rotate the figure, then it shows an updated version of the figure (some number of frames ahead) and then freezes again.
The code below is based on this similar question.
Let me show the code I am using
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.animation
import pandas as pd
Np = 5000
windowSize = 1000
m = np.random.rand(Np, 3)
df = pd.DataFrame({ "x" : m[0:Np,0], "y" : m[0:Np,1], "z" : m[0:Np,2]})
def init_graph():
u, v = np.mgrid[0:2*np.pi:50j, 0:np.pi:50j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
z = np.cos(v)
ax.plot_surface(x, y, z, color="bisque", alpha=0.3)
return graph,
def update_graph(num):
if (num<windowSize):
graph._offsets3d = (df.x[0:num], df.y[0:num], df.z[0:num])
else:
graph._offsets3d = (df.x[(num-windowSize):num], df.y[(num-windowSize):num], df.z[(num-windowSize):num])
title.set_text('3D Test, time={}'.format(num))
return graph,
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_box_aspect((1,1,1))
title = ax.set_title('3D Test')
graph = ax.scatter(0, 0, 0)
ani = matplotlib.animation.FuncAnimation(fig, update_graph, frames=Np, init_func=init_graph, interval=200, blit=True, repeat=False)
plt.show()
m is an Np by 3 matrix, and each row represents a 3d point (in my real use case, each row is a point in a trajectory on the sphere surface, but for this demo I created m as random numbers).
I create a variable graph that contains a scatter plot, which I believe is an Artist. This is what I return from both the init_func and the updating func which are passed to FuncAnimation (as per the docs).
From what I read, you return an iterable of the Artists which will be updated in the animation. Thus I return a tuple of one element, graph,.
Now, in update_graph, the updating function for the animation, I am updating the scatter plot using graph._offsets3d, which I read in another question here on StackOverflow. I am not totally sure if this is the way to do it and I didn't find much information in the docs about whether to use this or one of the setting methods on the scatter plot.
Why doesn't blitting work with scatter plots?
I am trying to animate a simple demonstration of Benfold's Law. I am expecting an animated bar graph from this code:
import matplotlib.animation as animation
fig = plt.figure()
plt.xticks(np.arange(1,10))
def animate(i):
plt.title("Iteration: " + str(i))
plt.plot(np.arange(1,10,1),1000*benford[1:], linestyle="", marker="d",color='r')
plt.bar(all_leads[i].keys(), all_leads[i].values())
ani = animation.FuncAnimation(fig, animate, interval=100)
plt.show()
I get an empty plot.
Empty Plot
The animate(i) function works to give a correct individual plot
animate(10)
Image of correctly produced plot
Any ideas what I am doing wrong.
Because I don't have your data, I made a mock animation as best I could from what was provided. First, you have to plot your graph outside of the animate function, then you have to update the x and y data within the animate function, lastly - to loop your animation, you have to set frames to some value (we will go with 10 for your case). While this all isn't perfectly in line with your graphs (again, I don't have your data), this should get you started. You can also take a look at my other answer for an additional example.
%matplotlib notebook # If you are working in jupyter notebook
import matplotlib.animation as animation
fig,ax = plt.subplots()
plt.xticks(np.arange(1,10))
plot, = ax.plot(np.arange(1,10,1),np.arange(1,10,1), linestyle="", marker="d",color='r')
def animate(i):
plot.set_ydata(np.arange(1,10,1)[i:i+3])
plot.set_xdata(np.arange(1,10,1)[i:i+3])
plt.title("Iteration: " + str(i))
ani = animation.FuncAnimation(fig, animate, interval=100, frames=10)
plt.show()
You can also add an xlim argument in the animate function to follow your animation across the x-axis:
def animate(i):
plot.set_ydata(np.arange(1,10,1)[i:i+3])
plot.set_xdata(np.arange(1,10,1)[i:i+3])
plt.title("Iteration: " + str(i))
plt.xlim(i, i+4)
Gives:
I'm trying to animate a figure using matplotlib->FuncAnimate function. However, I'm having trouble understanding how Blit works. With each frame, I want to draw only the new data point on top of the old one. It says that using Blit it should automatically update only the values that changed. Thus, if I turn it on (blit=True) the previous data points should remain in my figure. But this is not the case. The previous data get deleted and the figure gets redraw from scratch.
In the documentation, it says that I have to return "iterable_of_artists" and the algorithm will know which data has changed. I want to just pass the new data and just plot on top of the old one. By the way, what is an "iterable_of_artists", is that just a list of objects that can be drawn? if someone could point me out to the definition, I would appreciate it.
Anyway, I have worked several base examples that show the odd behavior. In the first example, I'm turning Blit=True and drawing only the new data using the animate function. This in theory should draw on top of the old ones, but is not the case, only the new data is drawn.
import time
import random
import numpy
import matplotlib
import matplotlib.pyplot as pyplot
from matplotlib.animation import FuncAnimation
def livePlot():
fig, ax = pyplot.subplots(1,1)
ax = pyplot.axes(xlim=(0, 2), ylim=(0, 100))
line, = ax.plot([], [], 'ro') #ax.plot will return a tupple
def init():
line.set_data(0, 50)
return line, #Return is not necessary when blit=False
def animate(frame):
x = frame
y = random.randint(0, 100)
line.set_data(x,y)
return line, #Return is not necessary when blit=False
animation = FuncAnimation(
fig, animate,
init_func = init,
frames= [0.5, 1, 1.5, 2.0],
interval=1000,
repeat=False,
blit=True, # Turning on Blit
cache_frame_data = True)
pyplot.show()
if __name__ == "__main__":
livePlot()
I was able to achieve my goal by tricking the FuncAnimate method. I can use the ax and plot in each frame the new data. If I do that, the old data remains and only the new data is drawn. However, I can do that with Blit=True or Blit=False, it has no effect. So, I'm so confused on how Blit works and what would be the correct way to plot only the new data without having to create a list with all the data to plot. Passing a large list will create a large variable in memory if I have a long set of data points. Here is my workaround but I'm not sure if this is the correct way to do it or if there is a better ways of using Blit=True and just redraw the new data.
import time
import random
import numpy
import matplotlib
import matplotlib.pyplot as pyplot
from matplotlib.animation import FuncAnimation
def livePlot():
fig, ax = pyplot.subplots(1,1)
ax = pyplot.axes(xlim=(0, 2), ylim=(0, 100))
def init():
ax.plot(0, 50, 'ro')
return []
def animate(frame):
x = frame
y = random.randint(0, 100)
ax.plot(x, y, 'ro') # plotting directly on the axis. This keeps the old data
return [] # fooling the blit algorithm with an empty stream
animation = FuncAnimation(
fig, animate,
init_func = init,
frames= [0.5, 1, 1.5, 2.0],
interval=1000,
repeat=False,
blit=True,
cache_frame_data = True)
pyplot.show()
if __name__ == "__main__":
livePlot()
I have trajectory data where each vehicle has its own time to start. Each vehicle is a point in the animation. So, in the dataset, for each row there is coordinate point (x,y) along with a timestamp. So, fixed time interval would not work for me. I tried with loop and sleep but it not showing the animation but only the first result. But if debug line by line, it seems okay(updating with new points after each iteration). Here is my code (this is to test: loop, sleep and animation):
#sample data
x=[20,23,25,27,29,31]
y=[10,12,14,16,17,19]
t=[2,5,1,4,3,1,]
#code
fig, ax = plt.subplots()
ax.set(xlim=(10, 90), ylim=(0, 60))
for i in range(1,6):
ax.scatter(x[:i+1], y[:i+1])
plt.show()
time.sleep(t[i])
How can get the animation effect?
The already mentioned FuncAnimation has a parameter frame that the animation function can use an index:
import matplotlib.pyplot as plt
import matplotlib.animation as anim
fig = plt.figure()
x=[20,23,25,27,29,31]
y=[10,12,14,16,17,19]
t=[2,9,1,4,3,9]
#create index list for frames, i.e. how many cycles each frame will be displayed
frame_t = []
for i, item in enumerate(t):
frame_t.extend([i] * item)
def init():
fig.clear()
#animation function
def animate(i):
#prevent autoscaling of figure
plt.xlim(15, 35)
plt.ylim( 5, 25)
#set new point
plt.scatter(x[i], y[i], c = "b")
#animate scatter plot
ani = anim.FuncAnimation(fig, animate, init_func = init,
frames = frame_t, interval = 100, repeat = True)
plt.show()
Equivalently, you could store the same frame several time in the ArtistAnimation list. Basically the flipbook approach.
Sample output:
I am trying to modify and example by making the animation run on increasing x values. I want update the x axis tick labels to update according to the x values.
I am trying to use the animation features (specifically FuncAnimation) in 1.2. I can set the xlimit but the tick labels are not updating. I tried explicitly setting the tick labels too and this does not work.
I saw this: Animating matplotlib axes/ticks and
I tried to adjust the bbox in animation.py but it did not work. I am fairly new to matplotlib and do not know enough about what is really going on to address this issue so I would appreciate any help.
Thank you
"""
Matplotlib Animation Example
author: Jake Vanderplas
email: vanderplas#astro.washington.edu
website: http://jakevdp.github.com
license: BSD
Please feel free to use and modify this, but keep the above information. Thanks!
"""
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
# animation function. This is called sequentially
def animate(i):
x = np.linspace(i, i+2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
ax.set_xlim(i, i+2)
return line,
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
plt.show()
See Animating matplotlib axes/ticks, python matplotlib blit to axes or sides of the figure?, and Animated title in matplotlib
The simple answer is remove blit=True
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20)
If you have blit = True only artists that have changed are re-drawn (rather than re-drawing all of the artists) which makes the rendering more efficient. Artists are marked as changed if they are returned from the update-function (in this case animate). The other detail is that the artists must be with in the axes bounding box with the way the code works in animation.py. See one of the links at the top for how to deal with this.