如何绘制3D矩阵

8 投票
2 回答
15858 浏览
提问于 2025-04-18 18:29

我正在尝试可视化三维数据。这是一个完整的三维矩阵:每个 (x,y,z) 坐标都有一个值,不像表面或单独的数据向量集合。我想要实现的方式是绘制一个不透明的立方体,立方体的每条边显示在正交维度上的数据总和。

这里有一些示例数据——基本上是一个以 (3,5,7) 为中心的块状物:

import numpy as np
(x,y,z) = np.mgrid[0:10,0:10, 0:10]
data = np.exp(-((x-3)**2 + (y-5)**2 + (z-7)**2)**(0.5))
edge_yz = np.sum(data,axis=0)
edge_xz = np.sum(data,axis=1)
edge_xy = np.sum(data,axis=2)

所以我的想法是生成一个三维图,显示一个立方体;立方体的每个面会显示相应的二维矩阵 edge_*。这就像在适当的三维位置绘制三个四边形(如果把立方体的背面也算上,就是六个),不过每个多边形实际上是一个要用颜色绘制的值的矩阵。

目前我最好的尝试是计算包含扭曲版本的边缘的大矩阵,并把这些矩阵拼接成一个更大的二维矩阵,然后用 imshow() 显示这个更大的矩阵。这看起来有点笨拙,而且做了很多工作,而 matplotlib 或 m3plot 之类的工具肯定已经能做到这些。它还只能在一个固定的视角下查看静态图像,但这不是我现在需要解决的问题。

有没有好的方法可以使用现有的 Python 工具在真正的三维图中绘制这些立方体的边?有没有更好的方法来绘制三维矩阵?

2 个回答

2

你可以看看 MayaVI。里面的 contour3d() 函数可能正是你需要的。

这里有一个我之前回答过的类似问题,里面有代码示例和生成的图表,链接在这里 https://stackoverflow.com/a/24784471/3419537

8

Falko建议使用contourf这个方法,经过一些调整后可以用。不过这个方法有点局限性,因为我用的版本有一些小问题,有时候它会把本该在后面的平面画到前面去。不过现在只需要画出立方体的三个前面或三个后面就可以了:

import numpy as np
import math
import matplotlib.pyplot as plot
import mpl_toolkits.mplot3d.axes3d as axes3d

def cube_marginals(cube, normalize=False):
    c_fcn = np.mean if normalize else np.sum
    xy = c_fcn(cube, axis=0)
    xz = c_fcn(cube, axis=1)
    yz = c_fcn(cube, axis=2)
    return(xy,xz,yz)

def plotcube(cube,x=None,y=None,z=None,normalize=False,plot_front=False):
    """Use contourf to plot cube marginals"""
    (Z,Y,X) = cube.shape
    (xy,xz,yz) = cube_marginals(cube,normalize=normalize)
    if x == None: x = np.arange(X)
    if y == None: y = np.arange(Y)
    if z == None: z = np.arange(Z)

    fig = plot.figure()
    ax = fig.gca(projection='3d')

    # draw edge marginal surfaces
    offsets = (Z-1,0,X-1) if plot_front else (0, Y-1, 0)
    cset = ax.contourf(x[None,:].repeat(Y,axis=0), y[:,None].repeat(X,axis=1), xy, zdir='z', offset=offsets[0], cmap=plot.cm.coolwarm, alpha=0.75)
    cset = ax.contourf(x[None,:].repeat(Z,axis=0), xz, z[:,None].repeat(X,axis=1), zdir='y', offset=offsets[1], cmap=plot.cm.coolwarm, alpha=0.75)
    cset = ax.contourf(yz, y[None,:].repeat(Z,axis=0), z[:,None].repeat(Y,axis=1), zdir='x', offset=offsets[2], cmap=plot.cm.coolwarm, alpha=0.75)

    # draw wire cube to aid visualization
    ax.plot([0,X-1,X-1,0,0],[0,0,Y-1,Y-1,0],[0,0,0,0,0],'k-')
    ax.plot([0,X-1,X-1,0,0],[0,0,Y-1,Y-1,0],[Z-1,Z-1,Z-1,Z-1,Z-1],'k-')
    ax.plot([0,0],[0,0],[0,Z-1],'k-')
    ax.plot([X-1,X-1],[0,0],[0,Z-1],'k-')
    ax.plot([X-1,X-1],[Y-1,Y-1],[0,Z-1],'k-')
    ax.plot([0,0],[Y-1,Y-1],[0,Z-1],'k-')

    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    plot.show()

plot_front=True plot_front=True plot_front=False plot_front=False 其他数据(未显示) 其他数据(未显示)

撰写回答