I have two arrays x and y, each one has more than 365000 elements. I would like to draw an animated line using these array elements. I'm using matplotlib.animation for it. Problem is when I execute the code below I can't see the graph smoothly(animated) drawed. Contrary I see it's final drawed version.
Here is my code:
#libs
# Movement instance creation-----------------------------
movement1=Movement(train1, track1)
# # Move the train on the track
movement1.move()
y = movement1.speed
x = movement1.pos
Writer = animation.writers['ffmpeg']
writer = Writer(fps=20, metadata=dict(artist='Me'), bitrate=1800)
fig = plt.figure()
ax = plt.axes(xlim=(0, 25), ylim=(0, 300))
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):
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=200, blit=True)
anim.save('basic_animation.mp4', writer=writer)
Here is the similar result that I expect:
Of course my graph would be another curve.
Your code is mostly fine; you just need to do three things.
Set the xdata and ydata of the line to different values every iteration of anim_func (otherwise, there would be no animation, would there?)
Set constant axis limits so your plot doesn't change shape
Remove the save call for display purposes (for me personally, I find it affects the animation)
So:
ax.axis((x.min(), x.max(), y.min(), y.max())
def animate(i):
line.set_data(x[:i], y[:i])
return line,
You need to define a set of data that is changing for animation to occur. In the example site you gave, the author does it by slicing the data using overdose.iloc[:int(i+1] (see below for the actual code used). This is the part that creates the animation as matplotlib plots whatever data is in the animate function. In your code you have input line.set_data(x, y) which I suppose is your entire dataset. That's why it isn't moving.
def animate(i):
data = overdose.iloc[:int(i+1)] #select data range
p = sns.lineplot(x=data.index, y=data[title], data=data, color="r")
p.tick_params(labelsize=17)
plt.setp(p.lines,linewidth=7)
Second thing to note is that your plot is getting chopped off at the top. That is likely because your initialisation is already setting the axis wrongly. What I would do is to add in a plt.axis([0, 25, 0, 'upper limit']) to help set the axis correctly.
Related
I am working on some animated scatter plots in python with matplotlib. I currently have this code:
def calulateStep():
# Math stuff ....
# Changes values in in 'circpos' Nx2 array
fig, ax = plt.subplots(figsize=(5, 5))
ax.set(xlim=(-WELLRADIUS,WELLRADIUS), ylim=(-WELLRADIUS,WELLRADIUS))
[x,y] = np.hsplit(circpos,2)
scat = ax.scatter(x.flatten(),y.flatten())
def animate(i):
calculateStep()
scat.set_offsets(circpos)
return scat,
anim = FuncAnimation(fig, animate, frames=60)
anim.save('test2.gif',writer='imagemagick')
plt.draw()
plt.show()
The function calculateStep calculates new x,y values for the scatter. circpos contains the data array at each step. this works well and produces an animated gif ofthe scatter plot as expected. However the function is a rather slow numerical calculation and many many steps are required to produce stable output, so I would rather calculate all before and then animate only select frames. So I tried this.
results = [circpos]
for h in range(61):
calculateStep()
results.append(circpos)
fig, ax = plt.subplots(figsize=(5, 5))
ax.set(xlim=(-WELLRADIUS,WELLRADIUS), ylim=(-WELLRADIUS,WELLRADIUS))
[x,y] = np.hsplit(results[0],2)
scat = ax.scatter(x.flatten(),y.flatten())
def animate(i):
scat.set_offsets(results.pop(0))
return scat,
anim = FuncAnimation(fig, animate, frames=60)
anim.save('test2.gif',writer='imagemagick')
plt.draw()
plt.show()
However with this method the generated gif contains only the final frame of the animation. If I print the data from within the animate function I find that the correct numerical values are being popped from the results list but for some reason only the final value is there in the gif. I have also tried using results[i] rather than results.pop(0) I am at a loss to understand this behavior.
Well it seems I solved my own problem. When I add each iteration of the global array circpos to the results list, it is of course a shallow copy. Meaning it's just a reference to the original circpos array. So I end up with a list full of references to the same object. The print out was just me misinterpreting what I was looking at.
Instead I now add circpos.copy() to my list to get new copies of the array at each step.
This has tripped me up in Python before I realize. Still learning!
I have a list of points, lets say as (x,y) pairs. I am trying to animate a plot so that each frame of the animation, a new point show up on the plot in a different color. Specifically on the 0th frame, the 0th point appears, on the the 1st frame, the 1st point appears, and so on. I would also like to have these points appear in a new color, specifically like a linear progression through a color palette as the points progress, so that you can "follow" the points by their color. This is similar to, and how I got as far as I am now: How can i make points of a python plot appear over time?. The first animation in the link is spot on, except without the points changing colors.
I am using matplotlib, matplotlib.pyplot, and FuncAnimation from matplotlib.animation
What I have already:
def plot_points_over_time(list_of_points):
num_points = len(list_of_points)
fig = plt.figure()
x, y = zip(*list_of_points)
plt.xlim(min(x),max(x))
plt.ylim(min(y),max(y))
colors = [plt.cm.gist_rainbow(each) for each in np.linspace(0,1,num_points)]
graph, = plt.plot([],[],'o')
def animate(i):
graph.set_data(x[:i+1],y[:i+1])
return graph
ani = FuncAnimation(fig, animate, frames = num_points, repeat = False, interval = 60000/num_points)
plt.show()
I can change the color of all of the points together on each frame by including the line graph.set_color(colors[i]) in the animate function, but not each point individually.
Figured it out with some digging and trial and error:
def plot_points_over_time(list_of_points):
num_points = len(list_of_points)
fig = plt.figure()
x, y = zip(*list_of_points)
plt.xlim(min(x),max(x))
plt.ylim(min(y),max(y))
colors = [plt.cm.gist_rainbow(each) for each in np.linspace(0,1,num_points)]
scat, = plt.plot([],[])
def animate(i):
scat.set_offsets(np.c_[x[:i+1], y[:i+1]])
scat.set_color(colors[:i+1])
return scat,
ani = FuncAnimation(fig, animate, frames = num_points, repeat = False, interval = 60000/num_points)
plt.show()
For a personal project, I'm trying to animate a fairly large data set (1000 rows) to show multiple bird dives in Jupyter notebook. Eventually I'd also like to add subplots of acceleration data along with it.
I used simple examples as a rough template, such as the growing coil example in: https://towardsdatascience.com/animations-with-matplotlib-d96375c5442c
The code itself seems to run slow but fine, however it doesn't output an animation, just a static graph:
Here's my current code:
x = np.array(dives.index)
y = np.array(dives['depth'])
x_data, y_data = [], []
fig = plt.figure()
ax = plt.axes(xlim=(0, 1000), ylim=(min(y),max(y)))
line, = ax.plot([], [])
def init():
line.set_data([], [])
return line,
def animate(i):
x_data.append(x[i])
y_data.append(y[i])
line.set_data(x, y)
return line,
plt.title('Bird Dives')
ani = animation.FuncAnimation(
fig, animate, init_func=init, frames= 1000, interval=50, blit=True)
ani.save('./plot-test.gif')
plt.show()
Is there a reason why it's just plotting a graph rather than an animated one?
Yes, your error is in your animate function. You have line.set_data(x, y), which is plotting the entire contents of x and y at every frame (and hence produces an animated graph that doesn't change).
What you intended to have in your animate function was line.set_data(x_data, y_data).
As for performance: you can improve this by not creating an empty list and appending to it at every iteration. Instead its simpler to slice your original arrays x and y. Consider the following animate function instead:
def animate(i):
line.set_data(x[:i], y[:i])
return line,
Having said this, given that you've got a thousand frames it's still going to take a little while to run.
I'm using matplotlib.animation to create a 4 bar linkage. in the arrX# arrY# variables I have the x&y of the lines (360 values in each). I'm using the standard template from the matplotlib website.
During one loop where variable frame goes from 0 to 359 everything is fine. But when it goes for a second loop, the image at the end (frame=359) of the last loop stays on the plot. At the end of every next plot it is drawn again and again.
How can I reset the plot or what can I do to eliminate the old lines at the end of the loop?
Code is below.
Image shows two sets of lines, where one set of lines is static that doesn't go away and is redrawn again and again at the end of each loop.
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
ln1, = plt.plot([], [], '-r')
ln2, = plt.plot([], [], '-b')
ln3, = plt.plot([], [], '-g')
def init():
ax.set_xlim(-100, 300)
ax.set_ylim(-100, 200)
return ln1,ln2,ln3
def update(frame):
xdata=(arrX2[frame])
ydata=(arrY2[frame])
ln1.set_data([0,xdata], [0,ydata])
ln2.set_data([xdata,arrX3[frame]],[ydata,arrY3[frame]])
ln3.set_data([d,arrX3[frame]],[0,arrY3[frame]])
return ln1,ln2,ln3
ani = FuncAnimation(fig, update, frames=range(0,360,1), init_func=init, blit=True,interval=1)
plt.show()
When I was creating a "Verifiable example" i found the problem.
Originally my init() function was this:
def init():
ax.set_xlim(-100, 300)
ax.set_ylim(-100, 200)
return ln1,ln2,ln3
This is wrong. This function should only hold the empty objects so that the animator can look into here and see what objects will need to be updated on each frame in the update() function.
Otherwise, in my example, once the "video reel" came to an end, and the time for the re-initialization came up, it simply plotted the last known line objects.
In the init() function I only kept the empty line objects. This worked.
def init():
ln1, = plt.plot([], [], '-r')
ln2, = plt.plot([], [], '-b')
ln3, = plt.plot([], [], '-g')
return ln1,ln2,ln3,
I'm convinced that this was an error. If not I just don't know.
P.S. Not sure if this needs to be kept here or not since this was a syntax error. I leave it up to more experienced users to decide :)
I am testing matplotlib animation of a plot with random data, I am experiencing the following issues:
The axes ranges xlim, ylim are updated almost randomly, and
when I switch between the program window and other windows.
The annotations are shown only on the frame when xlim and ylim are
updated they disapear the next frames until the plot is updated
again.
Sometimes the default menu of the plot freezes or disapear.
These issues could appear on both linux and windows (slightly differently).
Should I implement threading or eventually multiprocessing? or is it something else?
# -*- coding: utf-8 -*-
import re
import time
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from mpl_toolkits.axes_grid.anchored_artists import AnchoredText
import random
def init():
line.set_data([], [])
return line,
def animate(i):
y = random.randint(750, 2000)
xdata.append(i)
ydata.append(y)
xmin, xmax = ax.get_xlim()
ymin, ymax = ax.get_ylim()
print xmin, xmax
print ymin, ymax
###changing the xmax dynamically
if i >= xmax:
ax.set_xlim(xmin, xmax+(xmax/2))
ax.figure.canvas.draw()
###changing the ymax dynamically
if y >= ymax:
ax.set_ylim(ymin, y+(y/10))
ax.figure.canvas.draw()
#line.set_data(x, y)
line.set_data(xdata, ydata)
if y < 900:
annotation = ax.annotate('Min', xy=(i, y-5))
return line, annotation
#------------------------------------------
#initial max x axis
init_xlim = 5
init_ylim = 2000
fig = plt.figure()
ax = plt.axes(xlim=(0, init_xlim), ylim=(0, init_ylim))
ax.grid()
line, = ax.plot([], [], lw=2)
xdata, ydata = [], []
annotation = ax.annotate('Min', xy=(-1,-1))
annotation.set_animated(True)
anim = animation.FuncAnimation(fig, animate, init_func=init,frames=2000, interval=1000, blit=True)
plt.show()
TL; DR Turn blitting off and everything will 'work', but it might be slow.
You are running into assumptions made in the underlying code using blitting that the only thing changing will be be the stuff in the axes area (ie, not the ticks) and that you will be re-drawing on a fixed background. The way that blitting works is that a copy of the image on the gui canvas is made, each time you update a frame that copy is blitted back into the gui window (the state that is saved is the state at the end of the init function that FuncAnimation takes). The artists returned by your function are then drawn on top of this saved canvas. The region that gets updated this way is the region 'inside' your axes. The tick label are not re-drawn every time because drawing text is expensive.
Hence, your tick labels only update when the system triggers a full-redraw (triggered by changing windows), same with the annotations, they show up because the re-draw draws all artists. They go away again on the next frame because they are not a) on the saved 'base' canvas and b) not included in the list of things to draw returned by your call back function.
If you really must use blitting and add artists each time through you will have to do a bit more work and understand exactly how the animation infrastructure works.