Matplotlib的3D图 [英] 3D plot with Matplotlib
问题描述
我只是想在 3D 中绘制表面及其轮廓,就像 找到标量字段.
我同意 Ajean 的观点.我相信会出现此问题,因为每个matplotlib的艺术家(即 PolygonCollection
)都是单独呈现的.不可能将同一对象的不同面孔渲染到场景中另一个对象的不同侧面.
这是一段有用的代码:
来自mpl_toolkits.mplot3d的 导入axes3d导入matplotlib.pyplot作为plt从 matplotlib 导入 cm将numpy导入为npfile_path = "./3D_surface_and_contour.jpg"p = 0.05f = -0.01def get_data(p):x,y,z = axes3d.get_test_data(p)z = f * z返回x,y,zdef plot_3d_contour(p, f):行数= 4ncols = 5x,y,z = get_data(p)x_min,x_max = np.min(x),np.max(x)y_min, y_max = np.min(y), np.max(y)z_min, z_max = np.min(z), np.max(z)无花果= plt.figure(figsize =(15,10))对于范围内的 n(nrows * ncols):i = n%ncolsj = n/ncolsk = n + 1如果 j == 0:方位角 = -60 + (i - 2) * 15海拔 = 30elif j == 1:方位角= -60高度= 30 +(i-2)* 5elif j == 2:方位角 = 60 + (i - 2) * 10海拔= 30elif j == 3:方位角= 60海拔 = 30 + (i - 2) * 5ax = fig.add_subplot(nrows,ncols,k,projection='3d')ax.set_title("azim=" + str(azim) + " elev=" + str(elev))ax.tick_params(labelsize = 8)ax.view_init(azim=azim, elev=elev)ax.plot_surface(x, y, z, rstride=10, cstride=10, alpha=0.3)ax.contourf(x,y,z,zdir ='z',offset = z_min,cmap = cm.coolwarm)ax.contourf(x, y, z, zdir='x', offset=x_min, cmap=cm.coolwarm)如果j == 0或j == 1:ax.contourf(x,y,z,zdir ='y',offset = y_max,cmap = cm.coolwarm)elif j == 2或j == 3:ax.contourf(x, y, z, zdir='y', offset=y_min, cmap=cm.coolwarm)ax.set_xlabel('X')ax.set_xlim(x_min,x_max)ax.set_ylabel('Y')ax.set_ylim(y_min,y_max)ax.set_zlabel('Z')ax.set_zlim(z_min, z_max)plt.savefig(file_path,dpi = 80)plt.close()plot_3d_contour(p,f)
给出以下图像:
前两行由类似于您的代码生成.您可能会注意到,将 view_init
的高程设置为更高的值可以解决此问题.但它并不令人满意.我还确定了 z 值范围的影响(此处未显示),该错误似乎仅在此范围较小时才会出现(您可以使用 f
参数来测试它),其中解释为什么 example 没有受到影响.
我建议的解决方案是替换:
ax.contourf(x, y, scalar_field, zdir='y', offset=y_dim/2+1, cmap=cm.coolwarm)
作者:
ax.contourf(x, y, scalar_field, zdir='y', offset=-y_dim/2-1, cmap=cm.coolwarm)
在您的代码中并添加以下附加行:
ax.view_init(azim=60, elev=30)
如上图的最后两行所示,通过这种方式,您将能够避免matplotlib的异想天开.
I'm simply trying to plot a surface and its contour in 3D, exactly as in this example.
This is the code I'm using to do it:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from matplotlib import cm
import numpy
def plot_3d_contour(x_dim, y_dim, x_steps, y_steps, scalar_field, file_path):
fig = plt.figure()
x, y = numpy.mgrid[-x_dim/2:x_dim/2:x_steps*1j, -y_dim/2:y_dim/2:y_steps*1j]
v_min = numpy.min(scalar_field)
v_max = nupmy.max(scalar_field)
ax = fig.gca(projection='3d')
cset = ax.contourf(x, y, scalar_field, zdir='z', offset=v_min, cmap=cm.coolwarm)
cset = ax.contourf(x, y, scalar_field, zdir='x', offset=-x_dim/2-1, cmap=cm.coolwarm)
cset = ax.contourf(x, y, scalar_field, zdir='y', offset=y_dim/2+1, cmap=cm.coolwarm)
ax.plot_surface(x, y, scalar_field, rstride=10, cstride=10, alpha=0.3)
ax.set_xlabel('X')
ax.set_xlim(-x_dim/2-1, x_dim/2+1)
ax.set_ylabel('Y')
ax.set_ylim(-y_dim/2-1, y_dim/2+1)
ax.set_zlabel('Z')
ax.set_zlim(v_min, v_max)
plt.savefig(file_path + '.jpg')
plt.close()
scalar_field = numpy.loadtxt('../scalar_field', delimiter=",")
plot_3d_contour(12, 12, 100, 100, scalar_field, 'scalar_field3D')
However, I'm getting a weird behavior in which the a contour (zdir=y
) is being over the surface. Besides, I'm getting a weird contour in z_dir=z
(with a section missing):
I'm wondering what I'm missing. The scalar field can be found here.
I agree with Ajean. I believe the problem arises because each matplotlib's artist (i.e. PolygonCollection
) is rendered separately. There is no way different faces from the same object to be rendered on different sides of another object in the scene.
Here is a useful piece of code :
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
file_path = "./3D_surface_and_contour.jpg"
p = 0.05
f = -0.01
def get_data(p):
x, y, z = axes3d.get_test_data(p)
z = f * z
return x, y, z
def plot_3d_contour(p, f):
nrows = 4
ncols = 5
x, y, z = get_data(p)
x_min, x_max = np.min(x), np.max(x)
y_min, y_max = np.min(y), np.max(y)
z_min, z_max = np.min(z), np.max(z)
fig = plt.figure(figsize=(15, 10))
for n in range(nrows * ncols):
i = n % ncols
j = n / ncols
k = n + 1
if j == 0:
azim = -60 + (i - 2) * 15
elev = 30
elif j == 1:
azim = -60
elev = 30 + (i - 2) * 5
elif j == 2:
azim = 60 + (i - 2) * 10
elev = 30
elif j == 3:
azim = 60
elev = 30 + (i - 2) * 5
ax = fig.add_subplot(nrows, ncols, k, projection='3d')
ax.set_title("azim=" + str(azim) + " elev=" + str(elev))
ax.tick_params(labelsize=8)
ax.view_init(azim=azim, elev=elev)
ax.plot_surface(x, y, z, rstride=10, cstride=10, alpha=0.3)
ax.contourf(x, y, z, zdir='z', offset=z_min, cmap=cm.coolwarm)
ax.contourf(x, y, z, zdir='x', offset=x_min, cmap=cm.coolwarm)
if j == 0 or j == 1:
ax.contourf(x, y, z, zdir='y', offset=y_max, cmap=cm.coolwarm)
elif j == 2 or j == 3:
ax.contourf(x, y, z, zdir='y', offset=y_min, cmap=cm.coolwarm)
ax.set_xlabel('X')
ax.set_xlim(x_min, x_max)
ax.set_ylabel('Y')
ax.set_ylim(y_min, y_max)
ax.set_zlabel('Z')
ax.set_zlim(z_min, z_max)
plt.savefig(file_path, dpi=80)
plt.close()
plot_3d_contour(p, f)
which gives the following image :
The first two rows are produced by a code similar to yours. You might notice that setting the elevation with view_init
to a higher value solve the problem. But it is not satisfactory. I have also determined the influence of the range of the z-values (not shown here), the bug seems to appear only when this range is small (you can use the f
parameter to test it) which explain why the example does not suffer from it.
The solution I propose is to replace :
ax.contourf(x, y, scalar_field, zdir='y', offset=y_dim/2+1, cmap=cm.coolwarm)
by :
ax.contourf(x, y, scalar_field, zdir='y', offset=-y_dim/2-1, cmap=cm.coolwarm)
in your code and add this additional line :
ax.view_init(azim=60, elev=30)
As shown in the last two rows of the previous image, this way you will be able to avoid the whims of matplotlib.
这篇关于Matplotlib的3D图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!