Python 3D Plot from dictonary values - python
I do have a script, which creates a dict with XYZ-values. Following dict includes values at x from -2 to 2, with y from 0 to 2.
my_dict = {
-2:{0:1,1:1,2:0},
-1:{0:3,1:1,2:0},
0:{0:6,1:1,2:9},
1:{0:-2,1:1,2:2},
2:{0:1,1:1,2:6}}
Now, I don't now how to create a 3D plot out of this. I am aware of the matplotlib library,but I am not sure how to generate my Z-Data. I tried to write a function, to get my Z-data in a mesh, but it doesn't work. This is what I got so far:
x = np.arange(-2, 2, 1)
y = np.arange(0, 2, 1)
X, Y = np.meshgrid(x, y)
Z = f(X,Y) #HERE, the function f is what I am searching for.
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 50, cmap='binary')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
Is there any numpy or pythonic way to do this?
Is this what you're looking for?
my_dict = {
-2:{0:1,1:1,2:0},
-1:{0:3,1:1,2:0},
0:{0:6,1:1,2:9},
1:{0:-2,1:1,2:2},
2:{0:1,1:1,2:6}}
x = np.arange(-2, 3, 1)
y = np.arange(0, 3, 1)
X, Y = np.meshgrid(x, y)
def f(x, y):
z = np.zeros(X.reshape(-1).shape) # Create an "empty" tensor that matches the "flattened" meshgrid
c = 0 # To index over our "z"
for i in y:
for j in x:
z[c] = my_dict[j][i] # Fill the empty tensor with its corresponding values from the dictionary (depending on x and y)
c += 1
z = z.reshape(X.shape) # Reshape it back to match meshgrid's shape
return z
Z = f(x, y)
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 50, cmap='binary')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
I believe you can get away with accessing the correct value in your dict via:
Z = mydict[x][y]
Related
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
Python: Appending 2D arrays from meshgrid
I'm plotting two surface plots in python obtained from np.meshgrid, which I want to append to create only one surface plot. For instance: fig = plt.figure(figsize=(8,4)) ax = fig.add_subplot(projection='3d') # First surface: x1 = np.linspace(0,1,100) y1 = np.linspace(0,1,100) X1,Y1 = np.meshgrid(x1,y1) Z1 = 2*Y1 solid = ax.plot_surface(X1,Y1,Z1,cmap=cm.coolwarm,linewidth=0, antialiased=True) # Second surface: x2 = np.linspace(0,1,100) y2 = np.linspace(1,2,100) X2,Y2 = np.meshgrid(x2,y2) Z2 = 2*Y2 solid = ax.plot_surface(X2,Y2,Z2,cmap=cm.coolwarm,linewidth=0, antialiased=True) My first idea was to append them with np.append, such that: X = np.append(X1,X2) Y = np.append(Y1,Y2) Z = np.append(Z1,Z2) solid = ax.plot_surface(X,Y,Z,cmap=cm.coolwarm,linewidth=0, antialiased=True) But as expected, I got an error since Z was flattened and, moreover, I'm not sure if X and Y are right. In the example above it's trivial how could I create only one surface since both of them have the same angle, but I want to solve for the general case where they could have different inclinations. How can I overcome this problem? Thank you in advance!
I would simply fill only one Z array, for example: fig = plt.figure(figsize=(8,4)) ax = fig.add_subplot(projection='3d') x = np.linspace(0,1,100) y = np.linspace(0,2,100) X, Y = np.meshgrid(x, y) Z = np.empty_like(Y) Z[Y < 1] = 2 * Y[Y < 1] Z[Y >= 1] = 10 * Y[Y >= 1] - 8 # different slope solid = ax.plot_surface(X , Y, Z, cmap=plt.cm.coolwarm, linewidth=0, antialiased=True)
3D points on a surface PYTHON
I am trying to plot some points on a surface. The result I want to get is (the plot at the right): I tried the following code (x_k contains the coordinates of the points I want to plot): f = lambda x : x[0]**2+x[1]**3+3*x[0]*x[1] x_axis = [x_k[i][0] for i in range(0, len(x_k))] y_axis = [x_k[i][1] for i in range(0, len(x_k))] z_axis = [f(x_k[i]) for i in range(0, len(x_k))] x = np.linspace(-6, 6, 30) y = np.linspace(-6, 6, 30) X, Y = np.meshgrid(x, y) Z = f(np.array([X, Y])) ax = plt.axes(projection='3d') ax.plot_surface(X, Y, Z,alpha=0.8,cmap='viridis',edgecolor='white',linewidth=0.3) ax.scatter(x_axis, y_axis, z_axis, zdir='z',marker='.', s=10, c='black', depthshade=False) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_zlabel('z') ax.set_aspect("equal") plt.tight_layout() plt.show() And I get this: See that points are transparent because of the surface... I want them to be black, so as to see them better. How could I do that? Thank you!
Plot 4th dimension with Python
I would to know if there is the possibility to plot in four dimensions using python. In particular I would to have a tridimensional mesh X, Y, Z and f(X,Y,Z) = 1 or f(X,Y,Z) = 0. So I need to a symbol (for example "o" or "x") for some specific point (X,Y,Z). I don't need to a color scale. Note that I have 100 matrices (512*512) composed by 1 or 0: so my mesh should be 512*512*100. I hope I have been clear! Thanks. EDIT: This is my code: X = np.arange(W.shape[2]) Y = np.arange(W.shape[1]) Z = np.arange(W.shape[0]) X, Y, Z = np.meshgrid(X, Y, Z) fig = plt.figure() ax = fig.gca(projection='3d') for z in range(W.shape[0]): indexes = np.where(W[z]) ax.scatter(X[indexes], Y[indexes], ???, marker='.') ax.set_xlabel('X = columns') ax.set_ylabel('Y = rows') ax.set_zlabel('Z') plt.show() W is my tridimensional matrix, so: W[0], W[1], etc are 512x512 matrices. My question is: what have I to write insted of ??? in my code. I know I shouldn't ask this, but I can't understand the idea.
You could create inspect the value of f(x,y,z) for layers of z to see if they are non-zero or not, and then scatterplot the function based on this. e.g. for nz layers of (n,n) matrices, each a slice of a sphere: import numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt n, nz = 48, 24 x, y = np.linspace(-n//2,n//2-1,n), np.linspace(-n//2,n//2-1,n) X, Y = np.meshgrid(x, y) def f(x,y,z): return (X**2 + Y**2 + (z-nz//2)**2) < (n*0.2)**2 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') for z in range(nz): layer = f(X, Y, z) indexes = np.where(layer) ax.scatter(X[indexes], Y[indexes], layer[indexes]*(z-nz//2), marker='.') ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') plt.show() For random non-zero elements of f(x,y,z): import numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt n, nz = 12, 10 x, y, z = np.linspace(0,n-1,n), np.linspace(0,n-1,n), np.linspace(0,nz-1,nz) X, Y, Z = np.meshgrid(x, y, z) f = np.random.randint(2, size=(n,n,nz)) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') for z in range(nz): indexes = np.where(f[...,z]) ax.scatter(X[indexes], Y[indexes], f[indexes]+z, marker='.') ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') plt.show() But with your large arrays, you may run into problems (a) with memory and the speed of the plotting and (b) being able to resolve detail in the "central" block of the plot.
How do you create a 3D surface plot with missing values matplotlib?
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()