Animating the motion of a particle in 3D in Python - python

I have 3xN array positions that vary in time for a particle. I want to take the coordinates at each time point, plot that position as a point on a 3D axis and then repeat, creating an animation.
I can create a single image of this effect using matplotlib's Axes3D
x_a = particle_a[:,0]
y_a = particle_a[:,1]
z_a = particle_a[:,2]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x_a, y_a, z_a, c='b')
plt.show
'particle_a' is just an array of shape (N,3) where N is the number of timepoints.
How can I animate this?

Adapting this example from the matplotlib site you can represent a particle moving in 3D with:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
def make_helix(n):
theta_max = 8 * np.pi
theta = np.linspace(0, theta_max, n)
x, y, z = theta, np.sin(theta), np.cos(theta)
helix = np.vstack((x, y, z))
return helix
def update_lines(num, dataLines, lines) :
for line, data in zip(lines, dataLines) :
line.set_data(data[0:2, num-1:num])
line.set_3d_properties(data[2,num-1:num])
return lines
# Attach 3D axis to the figure
fig = plt.figure()
ax = p3.Axes3D(fig)
n = 100
data = [make_helix(n)]
lines = [ax.plot(data[0][0,0:1], data[0][1,0:1], data[0][2,0:1], 'o')[0]]
# Setthe axes properties
ax.set_xlim3d([0.0, 8*np.pi])
ax.set_xlabel('X')
ax.set_ylim3d([-1.0, 1.0])
ax.set_ylabel('Y')
ax.set_zlim3d([-1.0, 1.0])
ax.set_zlabel('Z')
ax.set_title('3D Test')
# Creating the Animation object
ani = animation.FuncAnimation(fig, update_lines, n, fargs=(data, lines),
interval=50, blit=False)
plt.show()
(replace helix with your own data and set the axes limits accordingly).

Related

How to get the slice of a plot3d object?

I have some points and I plot the surface of them using the code below:
import matplotlib.pyplot as plt
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
# Create a sphere
r = 1
pi = np.pi
cos = np.cos
sin = np.sin
phi, theta = np.mgrid[0.0:pi:20j, 0.0:2.0*pi:20j]
radis=np.random.normal(1,0.2,(20,20))
x = radis*sin(phi)*cos(theta)
y = radis*sin(phi)*sin(theta)
z = radis*cos(phi)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(
x, y, z, rstride=1, cstride=1, color='c', alpha=0.3, linewidth=0)
ax.scatter3D(x,y,z, c='r')
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
# ax.set_aspect("equal")
plt.tight_layout()
plt.show()
Then I get the 3d plot result:
The thing I want to do is that get the image of any plane, like z=0.
Is there any method or library can cover this problem?

How to do a 3D plot of gaussian using numpy?

I'm trying to plot a gaussian function using numpy.
the funtion is z=exp(-(x2+y2)/10) but I only get a 2D function
import numpy as np
from matplotlib import pyplot as plt
x=np.linspace(-10,10, num=100)
y=np.linspace(-10,10, num=100)
z=np.exp(-0.1*x**2-0.1*y**2)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(x,y,z)
I obtain:
but I want to obtain:
I'm using numpy becouse I need the set of data.
You need to obtain the correct dimensions. This can be done using meshgrid. Also, your desired plot is a surface plot, not a wireframe (though you can do that too).
# import for colormaps
from matplotlib import cm
x=np.linspace(-10,10, num=100)
y=np.linspace(-10,10, num=100)
x, y = np.meshgrid(x, y)
z = np.exp(-0.1*x**2-0.1*y**2)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x,y,z, cmap=cm.jet)
plt.show()
given the original formula of a gaussian distribution I wrote the following code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D # <--- This is important for 3d plotting
A = 1
x0 = 0
y0 = 0
sigma_X = 2
sigma_Y = 2
xg = np.linspace(-5,5,num=100)
yg = np.linspace(-5,5,num=100)
theta= np.pi
X, Y = np.meshgrid(xg,yg)
a = np.cos(theta)**2/(2*sigma_X**2) + np.sin(theta)**2/(2*sigma_Y**2);
b = -np.sin(2*theta)/(4*sigma_X**2) + np.sin(2*theta)/(4*sigma_Y**2);
c = np.sin(theta)**2/(2*sigma_X**2) + np.cos(theta)**2/(2*sigma_Y**2);
aXXdet = np.array([a*(Xi-x0)**2 for Xi in X],float)
bbXYdet = np.array([2*b*(Xi-x0)*(Y[ii]-y0) for ii,Xi in enumerate(X)],float)
cYYdet = np.array([c*(Yi-y0)**2 for Yi in Y],float)
Z = np.array([A*np.exp( - (ai + bbXYdet[i] + cYYdet[i])) for i,ai in enumerate(aXXdet)],float);
# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
which also plots the distribution. So you could play around with the parameters and see their effect!

3D animation using matplotlib

I want to make 3D animation with matplotlib, but I don't know how to. Here is my non-working code.
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import animation
from math import *
fig = plt.figure()
ax = fig.add_subplot(111) #, projection='3d'
#setting
ax.set_xlim(-5,5)
ax.set_ylim(-5,5)
#ax.set_zlim(-5,5)
ax.set_xlabel('x')
ax.set_ylabel('y')
#ax.set_zlabel('z')
ax.grid()
f1, = ax.plot([], [], "r-", lw=1) #plot1
def gen():
for phi in np.linspace(0,2*pi,100):
yield np.cos(phi), np.sin(phi), phi
def update(data):
p1, q1, psi = data
f1.set_data(p1,q1)
#f1.set_3d_properties(psi)
ani = animation.FuncAnimation(fig, update, gen, blit=False, interval=100, repeat=True)
#ani.save('matplot003.gif', writer='imagemagick')
plt.show()
I used this example http://matplotlib.org/1.4.1/examples/animation/simple_3danim.html
and modified your code:
from matplotlib import pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import animation
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
def gen(n):
phi = 0
while phi < 2*np.pi:
yield np.array([np.cos(phi), np.sin(phi), phi])
phi += 2*np.pi/n
def update(num, data, line):
line.set_data(data[:2, :num])
line.set_3d_properties(data[2, :num])
N = 100
data = np.array(list(gen(N))).T
line, = ax.plot(data[0, 0:1], data[1, 0:1], data[2, 0:1])
# Setting the axes properties
ax.set_xlim3d([-1.0, 1.0])
ax.set_xlabel('X')
ax.set_ylim3d([-1.0, 1.0])
ax.set_ylabel('Y')
ax.set_zlim3d([0.0, 10.0])
ax.set_zlabel('Z')
ani = animation.FuncAnimation(fig, update, N, fargs=(data, line), interval=10000/N, blit=False)
#ani.save('matplot003.gif', writer='imagemagick')
plt.show()
Here is the following code for a sphere moving to the right and off the screen.
You will have to run this code in a folder for tidiness, as it generates 26 .png images (and a .gif image):
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from numpy import sin, cos, pi, outer, ones, size, linspace
# Define x, y, z lists for sphere
a = linspace(0, 2 * pi)
b = linspace(0, pi)
x = 10 * outer(cos(a), sin(b))
y = 10 * outer(sin(a), sin(b))
z = 10 * outer(ones(size(a)), cos(b))
# The amount of frames in the animation
frames = 26
# Generate each frame
for n in range(frames):
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x, y, z, color=('b'))
ax.set_xticks([])
ax.set_yticks([])
ax.set_zticks([])
ax.set_xlim(-8,8)
ax.set_xlim(-8,8)
ax.set_xlim(-8,8)
plt.savefig(f"{n}.png")
plt.close()
# Add 1 to the x so the sphere moves right by 1
x += 1
# Use pillow to save all frames as an animation in a gif file
from PIL import Image
images = [Image.open(f"{n}.png") for n in range(frames)]
images[0].save('ball.gif', save_all=True, append_images=images[1:], duration=100, loop=0)
Output:

Applying colormaps to custom axis in Matplotlib 3D surface

I have timeseries data which I've segmented into hundreds of chunks. I solved the autocorrelation for each segment and plotted them:
# plot superimposed
fig = plt.figure()
color = iter(plt.cm.Set2(np.linspace(0,1,num_segs)))
seg_iterator = df.iterrows()
for index, seg in seg_iterator: # iterate over dataframe
c=next(color)
sns.plt.plot(seg, color=c)
Next, I plotted them as a 3D surface:
# plot as a surface
surfacefig = plt.figure()
surfaceax = surfacefig.gca(projection='3d')
X = np.arange(LAGS+1)
Y = np.arange(num_segs)
X, Y = np.meshgrid(X, Y)
surfaceax.plot_surface(X, Y, df, cmap=plt.cm.Set2)
plt.show()
How can I map colors to row index (rather than z-values)? I'd like to preserve the colors of the lines.
Update with result:
# updated lines. Make sure XX and YY are floats
surf = surfaceax.plot_surface(XX, YY, df, shade=False,
facecolors=plt.cm.Set2((YY-YY.min()) / (YY.max()-YY.min())),
cstride=1, rstride=5, alpha=0.7)
plt.draw() # you need this to get the edge color
line = np.array(surf.get_edgecolor())
surf.set_edgecolor(line*np.array([0,0,0,0])+1)
You can try this:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
X = np.linspace(-np.pi, np.pi, 200, endpoint=True)
Y = np.linspace(-np.pi, np.pi, 200, endpoint=True)
XX, YY = np.meshgrid(X,Y)
Z = np.cos(XX)*np.cos(YY)
fig = plt.figure()
ax1 = plt.subplot2grid((1,2), (0,0), projection='3d')
ax2 = plt.subplot2grid((1,2), (0,1), projection='3d')
surf = ax1.plot_surface(XX, YY, Z,
cmap=plt.cm.Set2)
surf2 = ax2.plot_surface(XX, YY, Z, shade=False,
facecolors=plt.cm.Set2((XX-XX.min())/(XX.max()-XX.min()))
)
Where on the second plot, you set the facecolors as being function of XX, instead of Z by default. You need to rescale your XX values between 0 and 1 or the colormap will be saturated outside 0 and 1. You also need to remove the shade which is removed when yous use cmap (in the first plot).
However, for some unknown reasons, the lines disappear.
You can add them back with:
plt.draw() # you need this to get the edge color
lines = np.array(surf2.get_edgecolor())
surf2.set_edgecolor(lines*np.array([0,0,0,0])+1) # make lines white, and keep alpha==1. It's an array of colors like this: [r,g,b,alpha]
It gives:
HTH

Using a separate function for colormap other than x,y,z for a 3D surface

I'm generating a 3D shape (X1,Y1,Z1) using the code below. A separate function, A5, has been used for a colormap. But as of now I'm only getting a blue surface. The range of values in Z1 is higher than that of A5 - could that be the reason behind the code generating a blue surface? Does it take the limit of Z1 for facecolor? If so how should I use plot_surface for this purpose?
surf = ax.plot_surface(X1,Y1,Z1,rstride=1, cstride=1, linewidth=0, facecolors=plt.cm.jet(A5),antialiased=False)
m = cm.ScalarMappable(cmap=cm.jet)
q=[-col,col]
m.set_array(q)
plt.colorbar(m)
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# generating data
x, y = np.meshgrid(np.linspace(-1,1,101), np.linspace(-1,1,101))
z = x+y # z is just a plane
r = np.sqrt(x*x+y*y)
w = 2*np.cos(5*r)**2-np.sin(6*x) # w is a funny trig func
# initializing the colormap machinery
norm = matplotlib.colors.Normalize(vmin=np.min(w),vmax=np.max(w))
c_m = matplotlib.cm.Spectral
s_m = matplotlib.cm.ScalarMappable(cmap=c_m, norm=norm)
s_m.set_array([])
# a figure and a 3d subplot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# the actual plot, note that to create the facecolors array
# I've used the norm function I instantiated above
ax.plot_surface(x, y, x+y, linewidth=0,
rstride=1, cstride=1,
facecolors=s_m.to_rgba(w))
# draw the colormap using the ScalarMappable instantiated above and shoe
plt.colorbar(s_m)
plt.show()

Categories

Resources