How do I create a matplotlib animation with multiple animated functions - python

I am trying to create an animation of growing concentric circles in python. As the program runs, more circles should generate from the centre and grow outwards
Right now I have this, which just creates one expanding circle.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 128), ylim=(0, 128))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
theta = np.linspace(0, 2 * np.pi, 100)
r = np.sqrt(i)
x = r * np.cos(theta) + 64
y = r * np.sin(theta) + 64
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=1000, interval=10, blit=True)
plt.gca().set_aspect('equal', adjustable='box')
plt.show()
How do I modify my code so that new growing circles generate from the middle to create growing concentric circles.

You can keep a list of lines, and add a new one every few frames with a smaller radius
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 128), ylim=(0, 128))
# Keep a list of lines instead of a single one
lines = ax.plot([], [], lw=2)
def init():
for line in lines:
line.set_data([], [])
return lines
def animate(i):
# Add a new line every 100 frames
if i // 100 >= len(lines):
new_line, = ax.plot([], [], lw=2)
lines.append(new_line)
for line_num, line in enumerate(lines):
theta = np.linspace(0, 2 * np.pi, 100)
# Reduce the radius of the new lines
r = np.sqrt(i - 100 * line_num)
x = r * np.cos(theta) + 64
y = r * np.sin(theta) + 64
line.set_data(x, y)
return lines
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=1000, interval=10, blit=True)
plt.gca().set_aspect('equal', adjustable='box')
plt.show()

Related

Dynamically update bar chart value labels in matplotlib animation using ax.bar_label()

I am trying to dynamically update the bar chart value labels in an animated matplotlib chart. The toy code I am using is here:
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
fig = plt.figure()
x = [1,2,3,4,5]
y = [5,7,2,5,3]
ax1 = plt.subplot(2, 1, 1)
ax2 = plt.subplot(2, 1, 2)
data = np.column_stack([np.linspace(0, yi, 50) for yi in y])
rects = ax1.bar(x, data[0], color='c')
line, = ax2.plot(x, data[0], color='r')
ax1.set_ylim(0, max(y))
ax1.bar_label(rects, padding=1)
ax2.set_ylim(0, max(y))
def animate(i):
for rect, yi in zip(rects, data[i]):
rect.set_height(yi)
ax1.bar_label(rects, padding=1)
line.set_data(x, data[i])
anim = animation.FuncAnimation(fig, animate, frames=len(data), interval=40)
plt.show()
The value labels are being printed at each time step, but they remain on the plot.
How can I update the value labels with each step in the animation without having the old labels remain on the chart?
You can recreate rects in each frame in clearing ax1:
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
fig = plt.figure()
x = [1,2,3,4,5]
y = [5,7,2,5,3]
ax1 = plt.subplot(2, 1, 1)
ax2 = plt.subplot(2, 1, 2)
data = np.column_stack([np.linspace(0, yi, 50) for yi in y])
ax1.set_ylim(0, max(y))
ax2.set_ylim(0, max(y))
line, = ax2.plot(x, [0] * len(x), color='r')
def animate(i):
ax1.cla()
ax1.set_ylim(0, max(y))
rects = ax1.bar(x, data[i], color='c')
ax1.bar_label(rects, padding=1)
line.set_data(x, data[i])
anim = animation.FuncAnimation(fig, animate, frames=len(data), interval=40)
plt.show()

How to use an image instead of a marker in an animated graph using Matplot.lib

I have been trying to use an image that moves in an animation instead of "ro" (red dots) or similar markers. image_1, image_2, image_3 So far I have a graph that has a little red dot moving over the dotted line. I want an image to be the one that moves through the dotted line instead of a red dot.
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
img = plt.imread("/Users/salomondabbah/Desktop/mar.jpg")
ax.imshow(img, extent=[0, 2 * np.pi, -1, 1])
xtrack = np.linspace(0, 2 * np.pi, 240)
ytrack = np.sin(xtrack)
ln, = plt.plot([], [], ***'ro'***, zorder=15)
ax.plot(xtrack, np.sin(xtrack), 'k', linewidth=25.0, zorder=5)
ax.plot(xtrack, ytrack, '--y',linewidth=2.0, zorder=10)
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
ln.set_data(frame, np.sin(frame))
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=False)
plt.show()
Thank you in advance for any solutions provided to my issue!
Similar to what you are doing, but using set_extent with imshow instead of set_data with a line:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots(figsize=(5,5))
xdata, ydata = [], []
xtrack = np.linspace(0, 2 * np.pi, 240)
ytrack = np.sin(xtrack)
img = plt.imread("car.png")
im = ax.imshow(img, zorder=10, aspect='auto')
ax.plot(xtrack, np.sin(xtrack), 'k', linewidth=25.0, zorder=5)
ax.plot(xtrack, ytrack, '--y',linewidth=2.0, zorder=10)
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
l = frame - 0.5
r = frame + 0.5
b = np.sin(frame) - 0.2
t = np.sin(frame) + 0.2
im.set_extent([l,r,b,t])
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=False)
ani.save('ani.gif', writer='pillow')
The only finnicky thing is getting the desired size & aspect ratio of the graph and size & aspect ratio of the car. The overall graph can be edited by using figsize when it is created as I have added. But if you don't set the aspect parameter of the imshow to auto, it will override the figure size and shape, in order to make the image have correct dimensions (it seems). So you may have to play around with the 0.5 and 0.2 values in the update to keep your image having approximately correct dimensions.

How do I get a fill_between shape in Funcanimation?

I would like to make a moving plot where the area under the curve gets colored while the curve is getting plotted.
I have googled a bit and found that I should somehow create a patch. However, I do not understand any of the examples they give, so let me ask it here with my specific example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import pylab as p
data = np.loadtext('datafile.dat', delimiter=',')
A = data[:,1]
B = data[:,2]
fig = plt.figure(figsize=(25,5), dpi=80)
ax = plt.axes(xlim=(0, 3.428), ylim=(-1,1))
line, = ax.plot([], [], lw=5)
def init():
line.set_data([], [])
return line,
def animate(i):
x = A[0:(i-1)*400]
y = B[0:(i-1)*400]
line.set_data(x,y)
# Here is the problem. I would now like to add the following line
# p.fill_between(x, 0, y, facecolor = 'C0', alpha = 0.2)
return line,
anim = animation.FuncAnimation(fig,animate, init_func=init, frames = 857, interval=20, blit=True)
I hope someone can give me a solution for my problem or at least point me in the correct direction.
So my question would be: how can I add the commented part without any errors?
Assuming you want blit = True, you will need to return the patch produced by fill_between as well.
p = plt.fill_between(x, y, 0)
return line, p,
Complete working example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
X = np.linspace(0,3.428, num=250)
Y = np.sin(X*3)
fig = plt.figure(figsize=(13,5), dpi=80)
ax = plt.axes(xlim=(0, 3.428), ylim=(-1,1))
line, = ax.plot([], [], lw=5)
def init():
line.set_data([], [])
return line,
def animate(i):
x = X[0:(i-1)]
y = Y[0:(i-1)]
line.set_data(x,y)
p = plt.fill_between(x, y, 0, facecolor = 'C0', alpha = 0.2)
return line, p,
anim = animation.FuncAnimation(fig,animate, init_func=init,
frames = 250, interval=20, blit=True)
plt.show()

Matplotlib animation in for loop?

I'm trying to plot some data by animation in a for loop. I want it to wait until the animation is finished and then proceed in the for loop. Pause seems to work to allow this but sometimes the movies are very long and I want to close and move to the next one. Anybody know how I can achieve this?
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import time
for j in range(0,2):
fig = plt.figure(j)
mngr = plt.get_current_fig_manager()
mngr.window.setGeometry(j*256,0,256, 256)
ax = plt.axes(xlim=(0, 2), 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(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i+j/4.))
line.set_data(x, y)
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,repeat=False)
plt.pause(0.02*200)
plt.show(block=True)
One way is to use a KeyboardInterrupt exception to move to the next plot.
For better readability, move your plotting into a function:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import time
def animate_multi(j):
fig = plt.figure(j)
mngr = plt.get_current_fig_manager()
mngr.window.setGeometry(j*256,0,256, 256)
ax = plt.axes(xlim=(0, 2), 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(0, 2, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i+j/4.))
line.set_data(x, y)
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,repeat=False)
plt.pause(0.02*200)
plt.close()
plt.show(block=True)
Now, in your loop except KeyboardInterrup and continue to the next animation:
for j in range(5):
try:
print('Working on plot', j)
animate_multi(j)
except KeyboardInterrupt:
plt.close()
Note: You might have to press <Ctrl>-<C> twice to skip to the next animation.

animated subplots using matplotlib

I have this code. I want to add a subplot to draw the cosine function. (I do not want to create a class). The second plot should be dynamically updated as well
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def data_gen():
t = data_gen.t
cnt = 0
while cnt < 1000:
cnt+=1
t += 0.05
yield t, np.sin(2*np.pi*t) * np.exp(-t/10.)
data_gen.t = 0
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
ax.set_ylim(-1.1, 1.1)
ax.set_xlim(0, 5)
ax.grid()
xdata, ydata = [], []
def run(data):
# update the data
t,y = data
xdata.append(t)
ydata.append(y)
xmin, xmax = ax.get_xlim()
if t >= xmax:
ax.set_xlim(xmin, 2*xmax)
ax.figure.canvas.draw()
line.set_data(xdata, ydata)
return line,
ani = animation.FuncAnimation(fig, run, data_gen, blit=True, interval=10,
repeat=False)
plt.show()
Basically you can use a very similar structure as the one you have in your example. You only need to create an additional axes (subplot) and a second line object:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def data_gen():
t = data_gen.t
cnt = 0
while cnt < 1000:
cnt+=1
t += 0.05
y1 = np.sin(2*np.pi*t) * np.exp(-t/10.)
y2 = np.cos(2*np.pi*t) * np.exp(-t/10.)
# adapted the data generator to yield both sin and cos
yield t, y1, y2
data_gen.t = 0
# create a figure with two subplots
fig, (ax1, ax2) = plt.subplots(2,1)
# intialize two line objects (one in each axes)
line1, = ax1.plot([], [], lw=2)
line2, = ax2.plot([], [], lw=2, color='r')
line = [line1, line2]
# the same axes initalizations as before (just now we do it for both of them)
for ax in [ax1, ax2]:
ax.set_ylim(-1.1, 1.1)
ax.set_xlim(0, 5)
ax.grid()
# initialize the data arrays
xdata, y1data, y2data = [], [], []
def run(data):
# update the data
t, y1, y2 = data
xdata.append(t)
y1data.append(y1)
y2data.append(y2)
# axis limits checking. Same as before, just for both axes
for ax in [ax1, ax2]:
xmin, xmax = ax.get_xlim()
if t >= xmax:
ax.set_xlim(xmin, 2*xmax)
ax.figure.canvas.draw()
# update the data of both line objects
line[0].set_data(xdata, y1data)
line[1].set_data(xdata, y2data)
return line
ani = animation.FuncAnimation(fig, run, data_gen, blit=True, interval=10,
repeat=False)
plt.show()

Categories