Matplotlib的3D图 [英] 3D plot with Matplotlib

查看:114
本文介绍了Matplotlib的3D图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是想在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆