I want to create a matplotlib animation, but instead of having matplotlib call me I want to call matplotlib. For example I want to do this:
from random import random
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def update(frame):
plt.scatter(random(),random())
fig, ax = plt.subplots()
ani = FuncAnimation(fig, update, interval=340, repeat=True)
ani.save("my.mov", "avconv")
Like this:
def update():
plt.scatter(random(),random())
fig, ax = plt.subplots()
ani = MadeUpSubClassPassiveAnimation(fig)
while True:
update()
ani.update()
# do other stuff ...
ani.save("my.mov", "avconv")
I realize I could drive a live plot like this:
def update():
plt.scatter(x, y)
plt.pause(0.01)
fig, ax = plt.subplots()
plt.ion()
while True:
update()
time.sleep(1)
But AFAIK I need to use Animation for the save() functionality. So, is it possible to drive Animation rather than have it drive me? If so how?
An Animation is run when being saved. This means that the animation needs to reproducibly give the same result when being run twice (once for saving, once for showing). In other words, the animation needs to be defined in terms of successive frames. With this requirement, any animation can be constructed using a callback on either a function (FuncAnimation) or on a list of frames (ArtistAnimation).
The example from the question could be done with an ArtistAnimation (in order not to have different random numbers for the saved and the shown animation, respectively):
from random import random
import matplotlib.animation
import matplotlib.pyplot as plt
def update(frame: int) -> list[matplotlib.artist.Artist]:
sc = ax.scatter(random(), random())
return [sc]
fig, ax = plt.subplots()
artists = []
for i in range(10):
sc = update(i)
artists.append(sc)
# If you want previous plots to be present in all frames, add:
# artists = [[j[0] for j in artists[:i+1]] for i in range(len(artists))]
ani = matplotlib.animation.ArtistAnimation(fig, artists, interval=100)
ani.save(__file__ + ".gif", writer="imagemagick")
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())
I'm trying to animate a box plot as data shifts over a time series.
I'm working off the matplotlib animate examples, which show how it works with the plot function, but that doesn't seem to carry over for a boxplot function:
Code Works below, but changing the two lines to box plot gives me errors
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
line, = ax.plot(np.arange(10)) # <-- ax.boxplot(np.arange(10))
ax.set_ylim(0, 20)
def update(data):
line.set_ydata(data) # < -- line = ax.boxplot(data)?
return line,
def data_gen():
i = 0
while True:
yield np.arange(10) + i
i += .1
ani = animation.FuncAnimation(fig, update, data_gen, interval=100)
plt.show()
Boxplot also doesn't seem to have a "set_data" function, or an "animated=True" parameter.
Essentially I'd like the animation to work the same as above, but depicting a box plot instead of a line plot.
I figured it out myself: The idea can be to clear the axes, and in each frame draw a new boxplot as shown below.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
# line, = ax.boxplot(np.arange(10)) <-- not needed it seems
ax.set_ylim(0, 20)
def update(data):
ax.cla() # <-- clear the subplot otherwise boxplot shows previous frame
ax.set_ylim(0, 20)
ax.boxplot(x=data)
def data_gen():
i = 0
while True:
yield np.arange(10) + i
i += .1
ani = animation.FuncAnimation(fig, update, data_gen, interval=100)
plt.show()
# Unset socks proxy
unset all_proxy
unset ALL_PROXY
# Install missing dependencies:
pip install pysocks
# Reset proxy
source ~/.bashrc
I am trying to do an animation using the FuncAnimation module, but my code only produces one frame and then stops. It seems like it doesn't realize what it needs to update. Can you help me what went wrong?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
def animate(i):
PLOT.set_data(x[i], np.sin(x[i]))
print("test")
return PLOT,
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
def animate(i):
PLOT.set_data(x[:i], np.sin(x[:i]))
# print("test")
return PLOT,
ani = animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
You need to keep a reference to the animation object around, otherwise it gets garbage collected and it's timer goes away.
There is an open issue to attach a hard-ref to the animation to the underlying Figure object.
As written, your code well only plot a single point which won't be visible, I changed it a bit to draw up to current index
I am trying to do an animation using the FuncAnimation module, but my code only produces one frame and then stops. It seems like it doesn't realize what it needs to update. Can you help me what went wrong?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
def animate(i):
PLOT.set_data(x[i], np.sin(x[i]))
print("test")
return PLOT,
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,2*np.pi,100)
fig = plt.figure()
sub = fig.add_subplot(111, xlim=(x[0], x[-1]), ylim=(-1, 1))
PLOT, = sub.plot([],[])
def animate(i):
PLOT.set_data(x[:i], np.sin(x[:i]))
# print("test")
return PLOT,
ani = animation.FuncAnimation(fig, animate, frames=len(x), interval=10, blit=True)
plt.show()
You need to keep a reference to the animation object around, otherwise it gets garbage collected and it's timer goes away.
There is an open issue to attach a hard-ref to the animation to the underlying Figure object.
As written, your code well only plot a single point which won't be visible, I changed it a bit to draw up to current index
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()