I'm trying to animate a curve in 3D and am having some trouble. I've successfully animated some things in 2D, so I thought I knew what I was doing. In the code below, I generate x, y, and z values parametrically to be a helix and have verified that I can plot the full curve in 3D. To animate the curve I am trying to begin by plotting only the first two data points and then use FuncAnimation to update the data so that it plots larger portions of the data. But as I said, it is not working for some reason and I have no idea why; all I get is the initial plot with the first two data points. Any help would be appreciated.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
t_max = 10
steps = 100
t = np.linspace(0, t_max, steps)
x = np.cos(t)
y = np.sin(t)
z = 0.1*t
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
line, = ax.plot(x[0:1], y[0:1], z[0:1])
def update(i):
line.set_xdata(x[0:i])
line.set_ydata(y[0:i])
line.set_zdata(z[0:i])
fig.canvas.draw()
ani = animation.FuncAnimation(fig, update, frames=t, interval=25, blit=False)
plt.show()
Okay, I finally got it to work. I had a dumb error (frames=t), but also figured out that you need to set the data in the update function differently. Here is the working code in case anyone is interested.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
t_max = 10
steps = 100
t = np.linspace(0, t_max, steps)
x = np.cos(t)
y = np.sin(t)
z = 0.1*t
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
line, = ax.plot([], [], [], lw=1)
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
ax.set_zlim(0,1)
plt.show()
def update(i):
line.set_data(x[0:i], y[0:i])
line.set_3d_properties(z[0:i])
return
ani = animation.FuncAnimation(fig, update, frames=100, interval=10, blit=True)
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 am trying to animate the numpy array C3, this is an array with one channel of electrode data and I want to plot it in real time using matplotlib.
I have created my update function but nothing is printing out, I though the the syntax is you pass i through to loop through the plots and the FuncAnimation should do the rest.
Can someone please point me in the right direction?
Much appreciated!
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
data_skip = 50
def update_plot(i):
plt.cla()
plt.plot(C3[i:i+data_skip], t[i:i+data_skip])
plt.scatter(C3[i], t[i], marker='o', color='r')
plt.tight_layout()
plt.show()
ani = FuncAnimation(plt.gcf(), update_plot, interval=1000)
plt.tight_layout()
plt.show()
Remove plt.cla(), it will clear current axes. Every time you plot something on figure, plt.cla() then clears it.
You could confirm it by the following minimul example. It plots nothing
import matplotlib.pyplot as plt
import numpy as np
C3 = np.linspace(0.5, 10, 100)
t = np.linspace(0.5, 10, 100)
plt.plot(C3, t)
plt.cla()
plt.show()
Matplotlib documentation have an example to write animation code: simple_anim.py. You'd better explicitly declare fig and ax.
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
import numpy as np
C3 = np.linspace(0.5, 10, 100)
t = np.linspace(0.5, 10, 100)
data_skip = 2
fig, ax = plt.subplots()
def update_plot(i):
ax.plot(C3[i:i+data_skip], t[i:i+data_skip])
ax.scatter(C3[i], t[i], marker='o', color='r')
ani = FuncAnimation(fig, update_plot, interval=1000)
plt.tight_layout()
plt.show()
I am trying to animate the wave function of electrons in an atom. I wrote the simplest python code following whats given in Matplotlob documentation on animation, but it does not do anything. Can anyone help?
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
import math
angles = (np.linspace(0, 2 * np.pi, 360, endpoint=False))
fig= plt.figure()
ax = fig.add_subplot(111, polar=True)
line1, =ax.plot([],[], 'g-', linewidth=1)
def update(theta):
line1.set_data(angles,energy_band(3, theta, 3))
return line1,
def init():
line1.set_data([],[])
return line1,
def energy_band(wave_number, phase_offset, energy_level):
return [math.sin(2*np.pi/360*i*wave_number+phase_offset*np.pi/360)+energy_level for i in range(360)]
ani = animation.FuncAnimation(fig, update, frames=[i for i in range(0,3600,5)], blit=True, interval=200, init_func=init)
plt.show()
The problem is with your data. Firstly, you must call set_data with single numeric. Secondly, if you divide energy function with 100 you have got data in a good scale to show. Moreover I set limits of axis. Check how I modify your code:
line1, =ax.plot([],[], 'ro')# 'g-', linewidth=1)
def update(theta):
line1.set_data(angles[int(theta)], energy_band(3, theta, 3)[int(theta)]/100)
#line1.set_data(angles,energy_band(3, theta, 3)/100)
return line1,
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
#line1.set_data([],[])
return line1,
Another thing is an interactive mode. It's usually a problem when matplotlib do nothing, especially working with jupyter notebook.
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
matplotlib.interactive(True)
plt.ion()
matplotlib.is_interactive()
The problem is that the data you want to animate lies between 2 and 4, but the polar plot shows only the range between -0.04 and 0.04. This is due to the use of an empty plot for the start. It would require you to set the limits manually. For example,
ax.set_rlim(0,5)
This is the only addition needed for your code to work.
However, one might optimize a bit more, e.g. use numpy throughout and reuse existing variables,
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
angles = (np.linspace(0, 2 * np.pi, 360, endpoint=False))
fig= plt.figure()
ax = fig.add_subplot(111, polar=True)
line1, =ax.plot([],[], 'g-', linewidth=1)
def update(theta):
line1.set_data(angles,energy_band(3, theta, 3))
return line1,
def init():
line1.set_data([],[])
ax.set_rlim(0,5)
return line1,
def energy_band(wave_number, phase_offset, energy_level):
return np.sin(angles*wave_number+phase_offset*np.pi/360)+energy_level
ani = animation.FuncAnimation(fig, update, frames=np.arange(0,3600,5),
blit=True, interval=200, init_func=init)
plt.show()
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)
I want to plot in 3D using matplotlib (python), which data is added in real time(x,y,z).
In the below code, data appends on x-axis and y-axis successfully, but on z-axis I've encountered problems.although I've searched in matplotlib's docs, I could not find any solutions.
what should be added/changed to this code to make it append data in z-axis?
what works correctly:
return plt.plot(x, y, color='g')
problem:
return plt.plot(x, y, z, color='g')
Code:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
import random
np.set_printoptions(threshold=np.inf)
fig = plt.figure()
ax1 = fig.add_subplot(111, projection='3d')
x = []
y = []
z = []
def animate(i):
x.append(random.randint(0,5))
y.append(random.randint(0,5))
z.append(random.randint(0,5))
return plt.plot(x, y, color='g')
#return plt.plot(x, y, z, color='g') => error
ani = animation.FuncAnimation(fig, animate, interval=1000)
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('z')
plt.show()
How to get this done correctly?
The plotting method you want to use for 3D plots is the one from the Axes3D. Hence you need to plot
ax1.plot(x, y, z)
However, it seems you want to update the data instead of plotting it all over again (making the line look somehow rasterized, as it would consists of all the plots).
So you can use set_data and for the third dimension set_3d_properties. Updating the plot would look like this:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
fig = plt.figure()
ax1 = fig.add_subplot(111, projection='3d')
x = []
y = []
z = []
line, = ax1.plot(x,y,z)
def animate(i):
x.append(np.random.randint(0,5))
y.append(np.random.randint(0,5))
z.append(np.random.randint(0,5))
line.set_data(x, y)
line.set_3d_properties(z)
ani = animation.FuncAnimation(fig, animate, interval=1000)
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('z')
ax1.set_xlim(0,5)
ax1.set_ylim(0,5)
ax1.set_zlim(0,5)
plt.show()