Animating points in a matplotlib scatter plot - python

So I have a bunch of 3D data relating to a shoulder (x3, y3, z3), an elbow(x2, y2, z2), and a wrist(x1, y1, z1). What i'd like to achieve is an animation which replicates the motion of the arm in a plot using matplotlib. Essentially, all I need to do is remove a set of three points on a 3D scatter plot and replace them on the same plot with the next set after a very short delay. I can't seem to find any questions which have covered this type of animation. Any help would be greatly appreciated. Current code below gives me the first set of points and that is all:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(len(x1)):
wrist = ax.scatter(x1.pop(), y1.pop(), z1.pop(), s=20)
elbow = ax.scatter(x2.pop(), y2.pop(), z2.pop(), s=20)
shoulder = ax.scatter(x3.pop(), y3.pop(), z3.pop(), s=20)
plt.show()
time.sleep(0.01)
wrist.remove()
elbow.remove()
shoulder.remove()

Related

Matplotlib: Grid over the surface of a 3D plot with three (1D) arrays

I have 3(1D) arrays so to plot a surface as far as I know the only thing I can use is TRISURF. But I don´t know how to put a grid over the surface,like this plot
This is the part of my code I´m having problem with:
fig = plt.figure()
ax = Axes3D(fig)
surf = ax.plot_trisurf(x, y, z,cmap=plt.cm.get_cmap('jet',256), linewidth=0.5, antialiased=True)
Thanks, a lot.

How to plot a plane without afecting the scale of the main plot?

I'm trying to plot a plane over a plot without having the main plot out of scale or overwritten. Ideally what I want is to aid visualization of a potential barrier in the complex visualization of a wave function, this could be done by somehow creating a shaded volume between the region of the potential... I thought I could make two planes for this purpose but they are plotted taking out of sight my main plot. This are two pictures of my resulting plots without and with the planes that I tried respectively:
I think I may be overwritting the main plot but I can't find a clear solution to this problem, this is the code I'm using to make the plot and an animation(If needed I can share the whole code):
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([1.5, 0.7, 0.7, 1]))
line,=ax.plot(x,IMAG[0,:],REAL[0,:],"r",linewidth=0.5)
ax.set_xlabel('Posició (nm)')
ax.set_ylabel('$Im[\psi(x,t)]$')
ax.set_zlabel('$Re[\psi(x,t)]$')
#Here are the two planes
yy, zz = np.meshgrid(range(-2,2), range(-2,2))
ax2 = plt.subplot(projection='3d')
ax2.plot_surface(-l, yy, zz,color='b',alpha=0.2)
ax2.plot_surface(l, yy, zz,color='b',alpha=0.2)
def animacio(i):
ax.collections.clear()
line.set_data(REAL[i,:],IMAG[i,:])
line.set_3d_properties(x, 'x')
return line,
ani=animation.FuncAnimation(fig,animacio,interval=50, frames=Nt,repeat=True)
ani.save(f'Evolució_[{V0},{L},{l},{xi},{sigmax},{T}].mp4', writer="ffmpeg", dpi=300)
plt.show()
You are plotting the planes on a separate subplot from the wave function. Instead of
ax2 = plt.subplot(projection='3d')
ax2.plot_surface(-l, yy, zz,color='b',alpha=0.2)
ax2.plot_surface(l, yy, zz,color='b',alpha=0.2)
Try
ax.plot_surface(-l, yy, zz, color='b', alpha=0.2)
ax.plot_surface(l, yy, zz, color='b', alpha=0.2)
And you should see the planes on the same plot as the wave function. You may also need to remove
ax.collections.clear()
From the animation function, using set_data and set_3d_properties should suffice to animate the wave function without altering the planes.
Edit
To keep the plotting of the planes from affecting the scale of the plot you can set the limits of the plot ahead of time, i.e.
ax.set_xlim([-20, 20])
ax.set_ylim([-0.6, 0.6])
ax.set_zlim([-0.6, 0.6])
Note, however, that the full extent of the planes will be plotted regardless of this - it is necessary to perform the creation properly to account for this. The following should do so
yy, zz = np.meshgrid(np.linspace(-1,1), np.linspace(-1,1))
I have encountered issues with using set_data and set_3d_properties in the past, if those do not work then you must simply clear the axis and replot the wavefunction and bounding planes, along with resetting the limits on each iteration.
Below is a complete example which illustrates the above
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D
n = 200
fig = plt.figure(figsize=(16,6))
ax = fig.add_subplot(111, projection='3d')
yy, zz = np.meshgrid(np.linspace(-1,1), np.linspace(-1,1))
def update(i):
# Show propagation of demonstration wavefunction in +x direction
i *= 22
p = np.zeros((3, n*4))
p[0,:] = np.linspace(-np.pi*16, np.pi*16, n*4)
x = p[0,i:i+n*2]
p[1,i:i+n*2] = np.sin(2*x + np.pi/2) * np.sin(x/16 + np.pi/2)/2
p[2,i:i+n*2] = np.sin(2*x) * np.cos(x/16)/2
# Plotting
plt.cla()
ax.set_xlim([-np.pi*16, np.pi*16])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
ax.plot_surface(np.full_like(yy, -np.pi*16), yy, zz, color='b', alpha=0.2)
ax.plot_surface(np.full_like(yy, np.pi*16), yy, zz, color='b', alpha=0.2)
plot, = ax.plot(p[0,:], p[1,:], p[2,:], color='red', lw=1)
return plot,
anim = animation.FuncAnimation(fig, update, frames=n//10, interval=2000/(n//10))
anim.save('wavefunc.gif', writer='imagemagick')

Plotting histograms on the back planes of 3D plots in matplotlib

Using matplotlib, I am attempting to display the histograms of 2 sets of data simultaneously on the side walls of a 3D plot, using this Matlab code and plot from wikipedia as my guide: https://commons.wikimedia.org/wiki/File:MultivariateNormal.png
I am able to plot my raw data on the base plane and have created and plotted my Gaussian fits on the side walls using the 'zdir' kwarg.
This example is able to leverage the 'zdir' kwarg to force where the curves are plotted,
Matplotlib 2d Plot on Faces of 3d Plot
but the matplotlib documentation confirms my AttributeErrors: Unknown property zdir; hist and hist2d don't support this argument.
This example seems to be plotting bars manually on the figure
plotting 3d histogram/barplot in python matplotlib as a way around the problem.
I've tried both .hist and .hist2d with and without zdir=''.
# data is a 2D np.array defined elsewhere
# define plot limits
X = np.linspace(0, np.amax(data), 100)
Y = np.linspace(0, np.amax(data), 100)
# initialize data into x and y sets
x_data = data[:, 0]
y_data = data[:, 1]
# fit a gaussian to both sets
x_mean, x_std = norm.fit(x_data)
x_gauss = norm.pdf(X, x_mean, x_std)
y_mean, y_std = norm.fit(y_data)
y_gauss = norm.pdf(Y, y_mean, y_std)
# initialize plot
figure = plt.figure()
ax = figure.add_subplot(111, projection='3d')
# label axes
ax.set_xlabel('Delta X (um)')
ax.set_ylabel('Delta Y (um)')
ax.set_zlabel('P (X,Y)')
# plot data on base plane
ax.scatter3D(x_data, y_data, zdir='z', zs=0.0, c='k', marker='.')
# plot histograms on walls
ax.hist((x_data, x_gauss), bins=30) #these 2 lines
ax.hist((y_data, y_gauss), bins=30) #are where I'm looking for help
# plot gaussians on walls
ax.plot3D(X, x_gauss, zdir='y', zs=np.amax(data), c='b')
ax.plot3D(Y, y_gauss, zdir='x', zs=np.amax(data), c='g')
# show plot
plt.show()
Is there a direct match in matplotlib for the method Matlab that draws histograms on a specific plane of a 3D plot? Thank you for your help! I am very new to plotting and welcome any other idiomatic or depreciated changes you can see. I always like to see how other coders think.

Contour plot lines striking through inline labels

I'm making a contour plot with three arrays: xdata, ydata, and phi. I'd like the face-on axes to correspond to xdata and ydata, and make contours out of phi.
After looking through the matplotlib contour plot example page, I wrote:
X, Y = np.meshgrid(xdata, ydata)
Z1, Z2 = np.meshgrid(phi, phi)
plt.figure(figsize=(10,8))
plt.scatter(xdata, ydata, s=200, c='white', edgecolor='grey', zorder=2)
plt.xlabel("x (degrees)")
plt.ylabel("y (degrees)")
plt.title("Obscuration ellipse $\phi$ (radians)")
CS = plt.contour(X, Y, Z1, zorder=1)
plt.clabel(CS, inline=True, inline_spacing=3, rightside_up=True, fontsize=18)
plt.show()
Here, I'm using zorder to force the scatter points to show up on top of the contour.
On the plot I get, the contours strike through the inline labels:
Some of these inline labels also appear to be stacked on top of others, and in two cases the labels obscure scatter points, despite zorder assignment.
How do I fix my code such that the labels are not strikethrough (as is the case in matplotlib's example page) and zorder is preserved?

Python Pyplot won't plot surface fully

In the attached picture you can see two plots of the same data. The left one is plotted with plot_wireframe() the right one with plot_surface()
Like this:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(xm, ym, Values)
fig2 = plt.figure()
ax2 = fig2.add_subplot(111, projection='3d')
ax2.plot_wireframe(xm, ym, Values)
plt.show()
Why is this? The plot_wireframe is correct. Why is for example the peak in the left upper corner not shown on the surface plot? Thanks
Here, another example with the data matrix:
Given what the discussion in the comments, I think what's going on is you have a normal wireframe that looks as it should, but the surface plot is a 2D projection in that 3D axes object. I noticed that you are missing a line that shows up in the matplotlib surface example: X, Y = np.meshgrid(X, Y). Assuming I'm on the right track, you need to insert an analogous statement prior to the axes.plot_surface() call.

Categories