How do you create a 3D surface plot with missing values matplotlib? - python
I am trying to create a 3D surface energy diagram where an x,y position on a grid contains an associated z level. The issue is that the grid is not uniform (ie, there is not a z component for every x,y position). Is there a way to refrain from plotting those values by calling them NaN in the corresponding position in the array?
Here is what I have tried so far:
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import pylab
from matplotlib import cm
#Z levels
energ = np.array([0,3.5,1,-0.3,-1.5,-2,-3.4,-4.8])
#function for getting x,y associated z values?
def fun(x,y,array):
return array[x]
#arrays for grid
x = np.arange(0,7,0.5)
y = np.arange(0,7,0.5)
#create grid
X, Y = np.meshgrid(x,y)
zs = np.array([fun(x,y,energ) for x in zip(np.ravel(X))])
Z = zs.reshape(X.shape)
plt3d = plt.figure().gca(projection='3d')
#gradients now with respect to x and y, but ideally with respect to z only
Gx, Gz = np.gradient(X * Y)
G = (Gx ** 2 + Gz ** 2) ** .5 # gradient magnitude
N = G / G.max() # normalize 0..1
plt3d.plot_surface(X, Y, Z, rstride=1, cstride=1,
facecolors=cm.jet(N), edgecolor='k', linewidth=0, antialiased=False, shade=False)
plt.show()
I cannot post image here of this plot but if you run the code you will see it
But I would like to not plot certain x,y pairs, so the figure should triangle downward to the minimum. Can this be accomplished by using nan values? Also would like spacing between each level, to be connected by lines.
n = np.NAN
#energ represents the z levels, so the overall figure should look like a triangle.
energ = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,2.6,n,2.97,n,2.6,n,2.97,n,2.6,n,3.58,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,1.09,n,1.23,n,1.09,n,1.23,n,1.7,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,-0.65,n,-0.28,n,-0.65,n,0.33,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,-2.16,n,-2.02,n,-1.55,n,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,-3.9,n,-2.92,n,n,n,n,n,],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,n,-4.8,n,n,n,n,n,n,]])
plt3d = plt.figure().gca(projection='3d')
Gx, Gz = np.gradient(X * energ) # gradients with respect to x and z
G = (Gx ** 2 + Gz ** 2) ** .5 # gradient magnitude
N = G / G.max() # normalize 0..1
x = np.arange(0,13,1)
y = np.arange(0,13,1)
X, Y = np.meshgrid(x,y)
#but the shapes don't seem to match up
plt3d.plot_surface(X, Y, energ, rstride=1, cstride=1,
facecolors=cm.jet(N), edgecolor='k',
linewidth=0, antialiased=False, shade=False
)
Using masked arrays generates the following error: local Python[7155] : void CGPathCloseSubpath(CGMutablePathRef): no current point.
n = np.NAN
energ = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,2.6,n,2.97,n,2.6,n,2.97,n,2.6,n,3.58,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,1.09,n,1.23,n,1.09,n,1.23,n,1.7,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,-0.65,n,-0.28,n,-0.65,n,0.33,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,-2.16,n,-2.02,n,-1.55,n,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,-3.9,n,-2.92,n,n,n,n,n,],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,n,-4.8,n,n,n,n,n,n,]])
x = np.arange(0,13,1)
y = np.arange(0,13,1)
X, Y = np.meshgrid(x,y)
#create masked arrays
mX = ma.masked_array(X, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])
mY = ma.masked_array(Y, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])
m_energ = ma.masked_array(energ, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]])
plt3d = plt.figure().gca(projection='3d')
plt3d.plot_surface(mX, mY, m_energ, rstride=1, cstride=1, edgecolor='k', linewidth=0, antialiased=False, shade=False)
plt.show()
I was playing around with the code from this forum post, and I was able to make the graph have missing values. You can try the code yourself! I got it to work using float("nan") for the missing values.
import plotly.graph_objects as go
import numpy as np
x = np.arange(0.1,1.1,0.1)
y = np.linspace(-np.pi,np.pi,10)
#print(x)
#print(y)
X,Y = np.meshgrid(x,y)
#print(X)
#print(Y)
result = []
for i,j in zip(X,Y):
result.append(np.log(i)+np.sin(j))
result[0][0] = float("nan")
upper_bound = np.array(result)+1
lower_bound = np.array(result)-1
fig = go.Figure(data=[
go.Surface(z=result),
go.Surface(z=upper_bound, showscale=False, opacity=0.3,colorscale='purp'),
go.Surface(z=lower_bound, showscale=False, opacity=0.3,colorscale='purp')])
fig.show()
Related
3D graphing the complex values of a function in Python
This is the real function I am looking to represent in 3D: y = f(x) = x^2 + 1 The complex function would be as follows: w = f(z) = z^2 + 1 Where z = x + iy and w = u + iv. These are four dimentions (x, y, u, v), but one can use u for 3D graphing. We get: f(x + iy) = x^2 + 2xyi - y^2 + 1 So: u = x^2 - y^2 + 1 and v = 2xy This u is what is being used in the code below. import numpy as np import matplotlib.pyplot as plt x = np.linspace(-100, 101, 150) y = np.linspace(-100, 101, 150) X, Y = np.meshgrid(x,y) U = (X**2) - (Y**2) + 1 fig = plt.figure(dpi = 300) ax = plt.axes(projection='3d') ax.plot_surface(X, Y, Z) plt.show() The following images are the side-view of the 3D function and the 2D plot for reference. I do not think they are alike. Likewise, here is the comparison between the 3 side-view and the 2D plot of w = z^3 + 1. They seem to differ as well. I have not been able to find too many resources regarding plotting in 3D using complex numbers. Because of this and the possible discrepancies mentioned before, I think the code must be flawed, but I can't figure out why. I would be grateful if you could correct me or advise me on any changes. The inspiration came from Welch Labs' 'Imaginary Numbers are Real' YouTube series where he shows a jaw-dropping representation of the complex values of the function I have been tinkering with. I was just wondering if anybody could point out any flaws in my reasoning or the execution of my idea since this code would be helpful in explaining the importance of complex numbers to HS students. Thank you very much for your time.
The f(z) = z^2 + 1 projection (that is, side-view) looks OK to me. You can use this technique to add the projections; this code: import numpy as np import matplotlib.pyplot as plt from matplotlib import cm def f(z): return z**2 + 1 def freal(x, y): return x**2 - y**2 + 1 x = np.linspace(-100, 101, 150) y = np.linspace(-100, 101, 150) yproj = 0 # value of y for which to project xu axes xproj = 0 # value of x to project onto yu axes X, Y = np.meshgrid(x,y) Z = X + 1j * Y W = f(Z) U = W.real fig = plt.figure() ax = plt.axes(projection='3d') ## surface ax.plot_surface(X, Y, U, alpha=0.7) # xu projection xuproj = freal(x, yproj) ax.plot(x, xuproj, zs=101, zdir='y', color='red', lw=5) ax.plot(x, xuproj, zs=yproj, zdir='y', color='red', lw=5) # yu projection yuproj = freal(xproj, y) ax.plot(y, yuproj, zs=101, zdir='x', color='green', lw=5) ax.plot(y, yuproj, zs=xproj, zdir='x', color='green', lw=5) # partially reproduce https://www.youtube.com/watch?v=T647CGsuOVU&t=107s x = np.linspace(-3, 3, 150) y = np.linspace(0, 3, 150) X, Y = np.meshgrid(x,y) U = f(X + 1j*Y).real fig = plt.figure() ax = plt.axes(projection='3d') ## surface ax.plot_surface(X, Y, U, cmap=cm.jet) ax.set_box_aspect( (np.diff(ax.get_xlim())[0], np.diff(ax.get_ylim())[0], np.diff(ax.get_zlim())[0])) #ax.set_aspect('equal') plt.show() gives this result: and The axis ticks don't look very good: you can investigate plt.xticks or ax.set_xticks (and yticks, zticks) to fix this. There is a way to visualize complex functions using colour as a fourth dimension; see complex-analysis.com for examples.
Surface Plot of 3D Arrays using matplotlib
I have a function of the form f(x,y,z) and want to create a surface plot for it (level sets) using matplotlib. The problem I have is that plot_surface only accepts 3 arguments, whereas the type of plot I want to do is create a grid of x,y,z values and then plot the value of my function f at each of those points. Here is a minimal example: import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D import numpy as np import matplotlib.pyplot as plt bounds = [1,1,1] numpoints = 25 x = np.linspace(-bounds[0], bounds[0], numpoints) y = np.linspace(-bounds[1], bounds[1], numpoints) z = np.linspace(-bounds[2], bounds[2], numpoints) X, Y, Z = np.meshgrid(x, y, z) s = X.shape Ze = np.zeros(s) Zp = np.zeros(s) DT = np.zeros((numpoints**3,3)) # convert mesh into point vector for which the model can be evaluated c = 0 for i in range(s[0]): for j in range(s[1]): for k in range(s[2]): DT[c,0] = X[i,j,k] DT[c,1] = Y[i,j,k] DT[c,2] = Z[i,j,k] c = c+1; # this could be any function that returns a shape (numpoints**3,) Ep = np.square(DT)[:,0] c = 0 for i in range(s[0]): for j in range(s[1]): for k in range(s[2]): Zp[i,j,k] = Ep[c] c = c+1; Now I would like to plot Zp as level sets in matplotlib. Is this possible?
The only way to represent 4 variables (x, y, x, f(x, y, z)) I could think in matplotlib is scatter the grid of x, y, z and give a color to the points that is proportional to f(x, y, z): bounds = [1,1,1] numpoints = 11 x = np.linspace(-bounds[0], bounds[0], numpoints) y = np.linspace(-bounds[1], bounds[1], numpoints) z = np.linspace(-bounds[2], bounds[2], numpoints) X, Y, Z = np.meshgrid(x, y, z) For exaple let's say taht f(x,y,z)=sin(x+y)+cos(y+z): f_xyz = np.sin(X+Y)+np.cos(Y+Z) Now let's scatter: plt.figure(figsize=(7,7)) ax = plt.subplot(projection="3d") ax.scatter(X, Y, Z, s=10, alpha=.5, c=f_xyz, cmap="RdBu") plt.show() As you can see the result is a bit confusing and not very clear, but it strongly depends on what function you want to plot. I hope you could find a better way
How to remove a specific point from matplotlib surfaceplot
I am trying to make a surface plot of a point cloud which is saved in text file in x,y,z format. when i try to a surface plot, there is a point near the origin which is ruining the plot as the surface gets altered. I tried to find the lowest value from the array of x,y,z ,turns out they are not the reason. How can i get a clean surface plot. Here is the code- from mpl_toolkits.mplot3d.axes3d import * import matplotlib.pyplot as plt from scipy.interpolate import griddata import numpy as np def distance(x, y, z): return math.sqrt(x**2 + y**2 + z**2) lim = 0.5 fig1 = plt.figure(1) ar = Axes3D(fig1)#fig1.gca()# ar.set_xlim(-lim,lim) ar.set_ylim(-lim,lim) ar.set_zlim(-lim,lim) pointz= np.loadtxt(fname = "goddo_table_1.txt",delimiter=",", dtype=str) x = np.empty([len(pointz),1],dtype=float) y = np.empty([len(pointz),1],dtype=float) z =np.empty([len(pointz),1],dtype=float) for i in range(len(pointz) ): if distance(float(pointz[i][0]), float(pointz[i][1]), float(pointz[i][2])) > 0.1 and float(pointz[i][2])>0.1: x = np.append(x,float(pointz[i][0])) y = np.append(y,float(pointz[i][1])) z = np.append(z,float(pointz[i][2])) else: #print(float(pointz[i][0]), float(pointz[i][1]), float(pointz[i][2])) pass xj = np.asarray(x) yj = np.asarray(z) zj = np.asarray(y) print(np.amin(abs(xj))) print(np.amin(abs(yj))) print(np.amin(abs(zj))) print(" ") print(np.argmin(abs(xj))) print(np.argmin(abs(yj))) print(np.argmin(abs(zj))) xi = np.linspace(min(xj), max(xj)) yi = np.linspace(min(yj), max(yj)) X, Y = np.meshgrid(xi, yi) Z = griddata((xj, yj), zj, (X, Y), method='nearest') #ar.contour(X, Y, Z) ar.plot_surface(X, Y, Z,cmap='jet', edgecolor='none') #surf = ar.plot_trisurf(x, z, y,cmap='viridis', edgecolor='none') surf = ar.scatter(xj, yj, zj,s=1,c='r') #ar.scatter(x,y,z,s=1) ar.set_xlabel('Horizontal - axis') ar.set_ylabel('Depth - axis') ar.set_zlabel('Vertical - axis') plt.show() here is the text file -goddo_table_1.txt Here is an image of the plot -Surface_plot How can remove the point near the origin?What am i missing? BTW - the point cloud made using an intel d435 camera and meshlab was used to make txt file from .ply file
That's because you use np.empty, with this command you interfere wrong extra points to your surface. just use X=[ ] Y=[ ] Z=[ ] and then append your points to them.
How to take into account the data's uncertainty (standard deviation) when fitting with scipy.linalg.lstsq?
I am trying to surface fit 3d data (z is a function of x and y). I have assymetrical error bars for each point. I would like the fit to take this uncertainty into account. I am using scipy.linalg.lstsq(). It does not have any option for uncertainties in its arguments. I am trying to adapt some code found on this page. import numpy as np import scipy.linalg from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt # Create data with x and y random over [-2, 2], and z a Gaussian function of x and y. np.random.seed(12345) x = 2 * (np.random.random(500) - 0.5) y = 2 * (np.random.random(500) - 0.5) def f(x, y): return np.exp(-(x + y ** 2)) z = f(x, y) data = np.c_[x,y,z] # regular grid covering the domain of the data mn = np.min(data, axis=0) mx = np.max(data, axis=0) X,Y = np.meshgrid(np.linspace(mn[0], mx[0], 20), np.linspace(mn[1], mx[1], 20)) XX = X.flatten() YY = Y.flatten() # best-fit quadratic curve (2nd-order) A = np.c_[np.ones(data.shape[0]), data[:,:2], np.prod(data[:,:2], axis=1), data[:,:2]**2] C,_,_,_ = scipy.linalg.lstsq(A, data[:,2]) # evaluate it on a grid Z = np.dot(np.c_[np.ones(XX.shape), XX, YY, XX*YY, XX**2, YY**2], C).reshape(X.shape) # plot points and fitted surface using Matplotlib fig = plt.figure(figsize=(10, 10)) ax = fig.gca(projection='3d') ax.plot_surface(X, Y, Z, rstride=1, cstride=1, alpha=0.2) ax.scatter(data[:,0], data[:,1], data[:,2], c='r', s=50) plt.xlabel('X') plt.ylabel('Y') ax.set_zlabel('Z') ax.axis('equal') ax.axis('tight')
Plotting function of 3 dimensions over given domain with matplotlib
I am trying to visualize a function of 3 parameters over a cube in R^3 to get an idea of the smoothness of the function. An example of this problem is shown in the sample code below %pylab from mpl_toolkits.mplot3d import Axes3D import itertools x = np.linspace(0,10,50) y = np.linspace(0,15,50) z = np.linspace(0,8,50) points = [] for element in itertools.product(x, y, z): points.append(element) def f(vals): return np.cos(vals[0]) + np.sin(vals[1]) + vals[2]**0.5 fxyz = map(f, points) xi, yi, zi = zip(*points) fig = plt.figure(figsize=(12, 8)) ax = fig.add_subplot(111, projection='3d') ax.scatter(xi, yi, zi, c=fxyz, alpha=0.5) plt.show() The problem with this approach is that the inside of the cube cannot be visualized. Is there a better way to graph a function over some dense subset of R^3?
As #HYRY and #nicoguaro suggested in the comments above, Mayavi is much better suited for this type of work. There is a good set of examples here that I used for reference. Here is what I came up with import numpy as np from mayavi import mlab x = np.linspace(0,10,50) y = np.linspace(0,15,50) z = np.linspace(0,8,50) X, Y, Z = np.meshgrid(x, y, z) s = np.cos(X) + np.sin(Y) + Z**0.5 b1 = np.percentile(s, 20) b2 = np.percentile(s, 80) mlab.pipeline.volume(mlab.pipeline.scalar_field(s), vmin=b1, vmax=b2) mlab.axes() mlab.show() After which I rotated the figure to desired angles with the GUI and saved desired views