I would like to create a animation where my data points would gradually appear on my graph and freeze when all the data points have appeared. I've seen in done with correlations i'm just not too sure how to do it with just individual points themselves
This isn't something that will show anything particularly useful but i though it would look cool since i am trying to visualize some location data on a map
I know this isn't very clear so please as for clarifications, I'm not too sure how to phrase my problem very well.
Thanks
matplotlib.animation.FuncAnimation is the right tool for you. First create an empty graph, and then gradually add data points to it in the function. The following piece of code will illustrate it:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x = np.arange(10)
y = np.random.random(10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph, = plt.plot([], [], 'o')
def animate(i):
graph.set_data(x[:i+1], y[:i+1])
return graph
ani = FuncAnimation(fig, animate, frames=10, interval=200)
plt.show()
The result (saved as gif file) is shown below:
EDIT: To make the animation look stopped when finished in matplotlib window, you need to make it infinite (omit frames parameter in FuncAnimation), and set the frame counter to the last number in your frame series:
def animate(i):
if i > 9:
i = 9
graph.set_data(x[:i+1], y[:i+1])
return graph
ani = FuncAnimation(fig, animate, interval=200)
Or, which is better, you can set repeat parameter in FuncAnimation to False, as per answer to this question.
EDIT 2: To animate a scatter plot, you need a whole bunch of other methods. A piece of code is worth a thousand words:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x = np.arange(10)
y = np.random.random(10)
size = np.random.randint(150, size=10)
colors = np.random.choice(["r", "g", "b"], size=10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph = plt.scatter([], [])
def animate(i):
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i+1])
graph.set_facecolors(colors[:i+1])
return graph
ani = FuncAnimation(fig, animate, repeat=False, interval=200)
plt.show()
Related
I am trying to create a crude animation, where each additional data point is plotted on the same graph. The problem is that the loop is generating a new graph for each data point.
Here's the code:
x = []
y = []
for i in range(3):
x.append(random.randint(0,10))
y.append(random.randint(0,10))
plt.scatter(x,y)
plt.pause(0.1)
This resulted in 3 separate plots stacked vertically. I would like all data points to update on the same graph, creating an animation. Thanks!
I've made an adaptation of your code (in particular, in each iteration I plot only another scatter point, because plotting each time ALL the points soon becomes unbearably slow).
If you will execute this file, as I invite you to do, $ python3 slow.py, it will print 0 50 100 150 200 and, initially fast, then slower and slower, it will produce a progressive display of the data points, all in the same Axes.
I have to confess that I don't understand your problem description because it's so different from what I've seen.
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
import random
def point():
return (random.randint(0,10), random.randint(0,10))
plt.xlim((-1, 11))
plt.ylim((-1, 11))
random.seed(20221229)
N = 200
cmap = plt.get_cmap('plasma')
plt.colorbar(ScalarMappable(cmap=cmap)).set_ticks((0,1), labels=("1",str(N)))
for i in range(N):
if not(i%50) : print(i)
plt.scatter(*point(), color=cmap(i/N), ec='black', s=80)
plt.pause(0.001)
print(N)
plt.show()
To update matplotlib graph you should use the module animation like Matplotlib is not very compatible with threads.
Here is an example adding a new point every 2 seconds :
import matplotlib.pyplot as pl
from matplotlib.animation import FuncAnimation
import random
datas = [0, 50]
fig = pl.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x=datas, y=datas, marker = '+', c='red')
def update(frame):
global datas
ax.clear()
ax.scatter(x=datas, y=datas, marker = '+', c='red')
datas.append(random.randint(0,50))
animation = FuncAnimation(fig, update, interval=2000, repeat = True)
pl.show()
Interval (in milliseconds) in FuncAnimation function plays the role of the pause function you are looking for.
James, I think plt.scatter can't make an animation. All the code will be executed and then a chart with 3 points will be the result.
To avoid the generation of multiple figures you can use plt.subplots.
fig, ax = plt.subplots()
for i in range(3):
ax.scatter(x=random.randint(0,10),y= random.randint(0,10))
If you want to create some animated figure use matplotlib.animation.FuncAnimation , as in the answer of the following topic. How can i make points of a python plot appear over time?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
x = np.arange(10)
y = np.random.random(10)
size = np.random.randint(150, size=10)
colors = np.random.choice(["r", "g", "b"], size=10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph = plt.scatter([], [])
def animate(i):
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i+1])
graph.set_facecolors(colors[:i+1])
return graph
ani = FuncAnimation(fig, animate, repeat=False, interval=200)
HTML(ani.to_jshtml())
Mark the option loop, and click in the minus or plus sing to slow or speed up the animation
Alternative approach, using FuncAnimation
from matplotlib.pyplot import Normalize, get_cmap, subplots
from matplotlib.cm import ScalarMappable
from matplotlib.animation import FuncAnimation, PillowWriter
from numpy.random import rand, seed
def update(xyn):
x, y, n = xyn
ax.scatter(x, y, color=cmap(n/N), ec='grey')
def start():
ax.set_xlim((-0.1, 1.1)) ; ax.set_ylim((-0.1, 1.1))
cb = fig.colorbar(ScalarMappable(cmap=cmap, norm=Normalize(0, 1)))
cb.set_ticks((0, 1), labels=('First', 'Last'))
def points(n):
seed(20230102)
def _():
for n_ in range(n):
yield rand(), rand(), n_
return _
fig, ax = subplots()
cmap = get_cmap('Greys')
N = 80
FuncAnimation(fig, update, points(N), start, repeat=False).save(
'scatter.gif', writer=PillowWriter())
Sorry I am a bit new to python and matplotlib so I dont know if I am asking it correctly. As of now I am plotting graphs where I collect an array of integers coming via serial port and refreshing the whole plot area at once. Now I want to do partial refresh (idk if it is the correct word) something like a PPG/ECG trace where once the line/trace reaches the end of plot area it starts from beginning, something like in the example here
[1]: http://theblogofpeterchen.blogspot.com/2015/02/html5-high-performance-real-time.html.
I do understand that if I keep on appending the serial port data and plotting it as soon as it arrives will keep on extending the plot forward but I have no clue how to return back at the the beginning point and redraw it gradually as in ECG.
Kindly help in this regard
Thank you
I have a solution below using FuncAnimation.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.misc import electrocardiogram
ecg = electrocardiogram()[0:1000]
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], '-')
# this is repeat length
n = 200
def init():
ax.set_xlim(0, n)
ax.set_ylim(-1, 2)
return ln,
def update(i):
# update xlim of axes
if i % n == 0:
ln.axes.set_xlim(int(i/n)*n, int(i/n)*n+n)
else:
xdata.append(i)
ydata.append(ecg[i])
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=1000, init_func=init, blit=True, interval=10, repeat=False)
I am a student and I am new to matplotlib animation.
I am trying to figure out how to animate zooming in towards the center of my 3d scatterplot, and I've included my code below. I am trying to get the zeroes to be at the middle of each axis so I am able to see the overall plot as a zoom in. I don't get an error whenever I run my code but when I run the animation the intervals change abruptly and don't seem to go in a certain pattern. Another thing I've noticed is that the zeroes are only sometimes in the middle of the axis, while the plot "glitches out".
Thank You.
import matplotlib.pylab as plt
import matplotlib.animation as animation
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
%matplotlib notebook
x = np.random.rand(100)*100
y = np.random.rand(100)*100
z = np.random.rand(100)*100
#setup figure
fig = plt.figure()
ax = fig.add_subplot(111, facecolor='LightCyan', projection = '3d')
#set up viewing window (in this case the 25 most recent values)
ax.set_xlim([-1, 1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
#sets up list of images for animation
plot = ax.scatter(x, y, z, color='b', marker= '*',)
def func(i):
x_lim = ax.set_xlim(-i,i)
y_lim = ax.set_ylim(-i, i)
z_lim = ax.set_zlim(-i, i)
return plot
ani = animation.FuncAnimation(fig, func, frames=100, interval=1000, blit=True)
Hi I am trying to create a movie of 15 Gaussian graphs that move to the left (thats essentially what the code is suppose to do)
However, my idea for how to create the for loop to create the 15 graphs has not created more than 1, it only speeds up the animation.
A similar code worked on matlab. It created 15 different Gaussian curves.
Here is a sample of my code.
any help would be appreciated.
Thanks
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('agg')
import matplotlib.animation as animation
Gamma=0.0005
q=1.6e-19
m=0.067*9e-31
B=10
Ec=(1.0567e-34)*B/m
#e=2.78
#E0=0+(1.0567e-34)*x*i/m
fig, ax = plt.subplots()
pass
x = np.arange(0, 3.4e-3, 1.7e-5) # x-array, third number is interval here, x is energy
line, = ax.plot(x, np.e**(-(x-((1.0567e-34)*1*1/m))**2/Gamma**2))
def animate(i):
for p in xrange(1,3):
line.set_ydata(np.e**((-(x-((1.0567e-34)*p*i/m))**2)/Gamma**2)) # update the data
return line,
#Init only required for blitting to give a clean slate.
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, animate, np.arange(0, 2, .01), init_func=init,
interval=10, blit=True)
Writer = animation.writers['ffmpeg']
writer = Writer(fps=20, metadata=dict(artist='Me'), bitrate=1800)
ani.save('QHanimati.mp4', writer=writer)
plt.show()
You currently have exactly one line in your code. This line gets updated. If you want to have more lines, you need to create more lines.
You then also need to update all of those lines.
(Since the role of p isn't clear from the example I took it as some incrementing number here. I also restricted this to 8 curves not to overcrowd the image.)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
Gamma=0.0005
q=1.6e-19
m=0.067*9e-31
B=10
Ec=(1.0567e-34)*B/m
fig, ax = plt.subplots()
n = 8 # number of lines
x = np.arange(0, 3.4e-3, 1.7e-5)
lines = [ax.plot(x, np.e**(-(x-((1.0567e-34)*1*1/m))**2/Gamma**2))[0] for _ in range(n)]
def animate(i):
for ln, line in enumerate(lines):
p = (ln+1)/10.
line.set_ydata(np.e**((-(x-((1.0567e-34)*p*i/m))**2)/Gamma**2)) # update the data
return lines
#Init only required for blitting to give a clean slate.
def init():
for line in lines:
line.set_ydata(np.ma.array(x, mask=True))
return lines
ani = animation.FuncAnimation(fig, animate, np.arange(0, 2, .01), init_func=init,
interval=10, blit=True)
plt.show()
Although hours of browsing stackoverflow definitely improved my python animate code, I could not quite figure out one thing and therefore I'm turning towards the kind souls of the community in the hope that someone might be able to shred some light.
In a nutshell, I have a background image that is about 2000 x 1000 pixels let's say, I need to scatter some points over this image and animate the process and save whole thing as a video. I'm able to update the scatterplot as needed only with the exception that I can't remove the precedent scatterplot. So the output is not what I would really want. I would be glad if someone could have a glance at the code and see where the glitch is. I've used scat.remove() which seems to be doing nothing.
Thank you in advance folks.
import matplotlib.pyplot as plt
import pylab as pl
import numpy as np
from pylab import savefig
from matplotlib import animation
import matplotlib
######################################################
fig = plt.figure()
ax = plt.axes()
a = plt.imread('background.jpg')
im = plt.imshow(a)
#######################################################
def randpair(n):
x,y=[],[]
for i in xrange(n):
x.append(np.random.randint(100,1900))
y.append(np.random.randint(100,900))
return x,y
def animate(i):
scat = ax.scatter(0,0,color='white')
points = np.random.randint(5,size=10)
for j in points:
xy = randpair(j)
x = xy[0]
y = xy[1]
print x,y
if len(x) > 0 :
scat.remove()
scat = ax.scatter(x,y,color='r',s=18)
plt.xticks([])
plt.yticks([])
return scat,ax, # ax returns the text to be updated and scat returns the scatterplot.
anim = animation.FuncAnimation(fig, animate, 49,interval=300, blit=True)
writer = animation.writers['ffmpeg']
anim.save('film_3.mp4')
#plt.show()
In the code you already remove the last scatter before the loop is finished; so some scatter plots will be added and then removed immediately.
One can prevent this by collecting the scatters in a list, then removing the scatters in the list from the canvas using remove and cleaning the list.
Apart from that, returning the complete ax object is a little useless. So I would suggest to simply turn blitting off, since it will not matter for saving the animation.
Here is a complete code that worked for me:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
fig = plt.figure()
ax = plt.axes()
scats = []
a = np.random.rand(8,18)
im = ax.imshow(a, cmap="YlGn", vmin=0, vmax=3, extent=[0,2000,0,1000])
plt.xticks([])
plt.yticks([])
def randpair(n):
x,y=[],[]
for i in xrange(n):
x.append(np.random.randint(100,1900))
y.append(np.random.randint(100,900))
return x,y
def animate(i):
global scats
# first remove all old scatters
for scat in scats:
scat.remove()
scats=[]
# now draw new scatters
points = np.random.randint(5,size=10)
for j in points:
x, y = randpair(j)
if len(x) > 0 :
scats.append(ax.scatter(x,y,color='r',s=18))
anim = matplotlib.animation.FuncAnimation(fig, animate, 50,
interval=1000, blit=False)
writer = matplotlib.animation.FFMpegWriter(fps=15,
codec="h264",
extra_args=["-preset", "veryslow","-crf","0"])
anim.save(__file__+".mp4", writer=writer)
plt.show()