I'm trying to make a 3D surface plot from a 2D array, using the values of my 2D array as the z value:
I want to do a surface plot like in the next pic:
This is my code
import numpy as npimport matplotlib.pyplot as pltimport math as mtfrom mpl_toolkits import mplot3d
ax = plt.axes(projection="3D")
z = [[248, 236,289,300,267,225,266,265,259,279,269,335],[246,253,241,232,276,213,198,201,222,229,193,237],[182,180,200,192,233,211,227,220,174,187,181,197],[124,102,137,130,144,168,149,164,168,156,90,156],[117,124,133,119,155,140,133,120,130,134,138,102],[155,140,137,125,146,102,129,114,119,113,132,122],[104,117,119,138,137,118,117,128,131,133,119,133],[136,115,108,105,133,104,121,135,136,127,135,112],[84,87,93,116,123,110,90,123,112,115,92,107],[118,94,100,83,132,90,111,91,98,116,100,95],[101,76,115,121,108,102,94,80,83,104,101,81],[86,86,109,105,95,75,18,87,92,99,101,128]]
y = np.arange(len(z))x = np.arange(len(z[0]))
(x ,y) = np.meshgrid(x,y)
ax.plot3D(x,y,z)plt.show()
This is what I tried but gives me an error.
you need to use plot_surface:
ax = plt.axes(projection="3d")
z = np.array([[248, 236,289,300,267,225,266,265,259,279,269,335],[246,253,241,232,276,213,198,201,222,229,193,237],[182,180,200,192,233,211,227,220,174,187,181,197],[124,102,137,130,144,168,149,164,168,156,90,156],[117,124,133,119,155,140,133,120,130,134,138,102],[155,140,137,125,146,102,129,114,119,113,132,122],[104,117,119,138,137,118,117,128,131,133,119,133],[136,115,108,105,133,104,121,135,136,127,135,112],[84,87,93,116,123,110,90,123,112,115,92,107],[118,94,100,83,132,90,111,91,98,116,100,95],[101,76,115,121,108,102,94,80,83,104,101,81],[86,86,109,105,95,75,18,87,92,99,101,128]])
y = np.arange(len(z))
x = np.arange(len(z[0]))
(x ,y) = np.meshgrid(x,y)
ax.plot_surface(x,y,z)
plt.show()
I have a spreadsheet file that I would like to input to create a 3D surface graph using Matplotlib in Python.
I used plot_trisurf and it worked, but I need the projections of the contour profiles onto the graph that I can get with the surface function, like this example.
I'm struggling to arrange my Z data in a 2D array that I can use to input in the plot_surface method. I tried a lot of things, but none seems to work.
Here it is what I have working, using plot_trisurf
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd
df=pd.read_excel ("/Users/carolethais/Desktop/Dissertação Carol/Códigos/Resultados/res_02_0.5.xlsx")
fig = plt.figure()
ax = fig.gca(projection='3d')
# I got the graph using trisurf
graf=ax.plot_trisurf(df["Diametro"],df["Comprimento"], df["temp_out"], cmap=matplotlib.cm.coolwarm)
ax.set_xlim(0, 0.5)
ax.set_ylim(0, 100)
ax.set_zlim(25,40)
fig.colorbar(graf, shrink=0.5, aspect=15)
ax.set_xlabel('Diâmetro (m)')
ax.set_ylabel('Comprimento (m)')
ax.set_zlabel('Temperatura de Saída (ºC)')
plt.show()
This is a part of my df, dataframe:
Diametro Comprimento temp_out
0 0.334294 0.787092 34.801994
1 0.334294 8.187065 32.465551
2 0.334294 26.155976 29.206090
3 0.334294 43.648591 27.792126
4 0.334294 60.768219 27.163233
... ... ... ...
59995 0.437266 14.113660 31.947302
59996 0.437266 25.208851 30.317583
59997 0.437266 33.823035 29.405461
59998 0.437266 57.724209 27.891616
59999 0.437266 62.455890 27.709298
I tried this approach to use the imported data with plot_surface, but what I got was indeed a graph but it didn't work, here it's the way the graph looked with this approach:
Thank you so much
A different approach, based on re-gridding the data, that doesn't require that the original data is specified on a regular grid [deeply inspired by this example;-].
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as tri
from mpl_toolkits.mplot3d import Axes3D
np.random.seed(19880808)
# compute the sombrero over a cloud of random points
npts = 10000
x, y = np.random.uniform(-5, 5, npts), np.random.uniform(-5, 5, npts)
z = np.cos(1.5*np.sqrt(x*x + y*y))/(1+0.33*(x*x+y*y))
# prepare the interpolator
triang = tri.Triangulation(x, y)
interpolator = tri.LinearTriInterpolator(triang, z)
# do the interpolation
xi = yi = np.linspace(-5, 5, 101)
Xi, Yi = np.meshgrid(xi, yi)
Zi = interpolator(Xi, Yi)
# plotting
fig = plt.figure()
ax = fig.gca(projection='3d')
norm = plt.Normalize(-1,1)
ax.plot_surface(Xi, Yi, Zi,
cmap='inferno',
norm=plt.Normalize(-1,1))
plt.show()
plot_trisurf expects x, y, z as 1D arrays while plot_surface expects X, Y, Z as 2D arrays or as x, y, Z with x, y being 1D array and Z a 2D array.
Your data consists of 3 1D arrays, so plotting them with plot_trisurf is immediate but you need to use plot_surface to be able to project the isolines on the coordinate planes... You need to reshape your data.
It seems that you have 60000 data points, in the following I assume that you have a regular grid 300 points in the x direction and 200 points in y — but what is important is the idea of regular grid.
The code below shows
the use of plot_trisurf (with a coarser mesh), similar to your code;
the correct use of reshaping and its application in plot_surface;
note that the number of rows in reshaping corresponds to the number
of points in y and the number of columns to the number of points in x;
and 4. incorrect use of reshaping, the resulting subplots are somehow
similar to the plot you showed, maybe you just need to fix the number
of row and columns.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
x, y = np.arange(30)/3.-5, np.arange(20)/2.-5
x, y = (arr.flatten() for arr in np.meshgrid(x, y))
z = np.cos(1.5*np.sqrt(x*x + y*y))/(1+0.1*(x*x+y*y))
fig, axes = plt.subplots(2, 2, subplot_kw={"projection" : "3d"})
axes = iter(axes.flatten())
ax = next(axes)
ax.plot_trisurf(x,y,z, cmap='Reds')
ax.set_title('Trisurf')
X, Y, Z = (arr.reshape(20,30) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 20×30')
X, Y, Z = (arr.reshape(30,20) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 30×20')
X, Y, Z = (arr.reshape(40,15) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 40×15')
plt.tight_layout()
plt.show()
i want to create a surface plot of the lists shown in the code. It's a simplification of data i'll import from an excel file once i figured out how to plot it.
x and y should represent the plane from which the z-values emerge. I created a random matrix to pair up with the 3x10 values from x,y.
This is the error Message:
ValueError: shape mismatch: objects cannot be broadcast to a single shape
import matplotlib.pyplot as plt
import numpy as np
x = [0,1,2,3,4,5,6,7,8,9,10] #creating random data
y = [0,1,2,3]
a = np.random.rand (3, 10)
z = np.array(a, ndmin=2) #not really sure if this piece is necessary.
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
x, y = np.meshgrid(x, y)
ax.plot_surface(x, y, z)
plt.show()
ValueError: shape mismatch: objects cannot be broadcast to a single shape
I've already tried to leave z = np.array(a, ndmin=2) out. Didn't work either.
The problem is two-fold:
First, you have 4x11 points and not 3x10 points
Second, you need to import Axes3D for enabling the 3d plotting. You don't need to use additionally z = np.array(a, ndmin=2) I think
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
x = [0,1,2,3,4,5,6,7,8,9,10] #creating random data
y = [0,1,2,3]
a = np.random.rand(4, 11)
x, y = np.meshgrid(x, y)
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
ax.plot_surface(x, y, a)
plt.show()