Matplotlib中的3D离散热图

15
我有一个包含三维数据的Python元组列表,每个元组形式为:(x, y, z, data_value),即我在每个(x, y, z)坐标处都有数据值。我想要制作一个3D离散热图,其中颜色表示我的元组列表中data_value的值。这里给出一个这样的热图示例,其适用于具有列表(x, y, data_value)元组的2D数据集:
import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
from random import randint

# x and y coordinates
x = np.array(range(10))
y = np.array(range(10,15))
data = np.zeros((len(y),len(x)))

# Generate some discrete data (1, 2 or 3) for each (x, y) pair
for i,yy in enumerate(y):
    for j, xx in enumerate(x):
        data[i,j] = randint(1,3)

# Map 1, 2 and 3 to 'Red', 'Green' qnd 'Blue', respectively
colormap = colors.ListedColormap(['Red', 'Green', 'Blue'])
colorbar_ticklabels = ['1', '2', '3']

# Use matshow to create a heatmap
fig, ax = plt.subplots()
ms = ax.matshow(data, cmap = colormap, vmin=data.min() - 0.5, vmax=data.max() + 0.5, origin = 'lower')

# x and y axis ticks
ax.set_xticklabels([str(xx) for xx in x])
ax.set_yticklabels([str(yy) for yy in y])
ax.xaxis.tick_bottom()

# Put the x- qnd y-axis ticks at the middle of each cell 
ax.set_xticks(np.arange(data.shape[1]), minor = False)
ax.set_yticks(np.arange(data.shape[0]), minor = False)

# Set custom ticks and ticklabels for color bar
cbar = fig.colorbar(ms,ticks = np.arange(np.min(data),np.max(data)+1))
cbar.ax.set_yticklabels(colorbar_ticklabels)

plt.show()

这将生成如下图所示的绘图: enter image description here 如果我的数据有第三个维度,我该如何在三维空间中制作类似的绘图(即具有z轴)?例如,如果...
# x and y and z coordinates
x = np.array(range(10))
y = np.array(range(10,15))
z = np.array(range(15,20))
data = np.zeros((len(y),len(x), len(y)))

# Generate some random discrete data (1, 2 or 3) for each (x, y, z) triplet. 
# Am I defining i, j and k correctly here?
for i,yy in enumerate(y):
    for j, xx in enumerate(x):
        for k, zz in enumerate(z):
            data[i,j, k] = randint(1,3)

我觉得mplot3d中的plot_surface应该能够做到这一点,但是这个函数的输入参数z实际上是数据在(x, y)坐标处的值,即(x, y, z = data_value),与我的情况不同,即(x, y, z, data_value)。


你想要一个三维表面,其中绘图的颜色是x、y和z的函数吗? - James
正确!但要使用离散的色条。 - user3076813
你可能想了解一下mayavicontour3d,它可以让你在三维空间中绘制标量场的等值面。 - berna1111
如果您不介意语言转换和许可问题,Mathematica内置了Image3DContourPlot3D - Kh40tiK
1
Mathematica中的Image3D听起来非常像我想要的东西。我只希望在python/matplotlib中有一个相当的等价物。 - user3076813
2个回答

24

新答案:

看起来我们真的想在这里玩一个三维俄罗斯方块游戏 ;-)

所以这里有一种方法可以绘制不同颜色的立方体,以填充由数组 (x,y,z) 给出的空间。

from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm
import matplotlib.colorbar
import matplotlib.colors

def cuboid_data(center, size=(1,1,1)):
    # code taken from
    # https://dev59.com/U4vda4cB1Zd3GeqPdb-g
    # suppose axis direction: x: to left; y: to inside; z: to upper
    # get the (left, outside, bottom) point
    o = [a - b / 2 for a, b in zip(center, size)]
    # get the length, width, and height
    l, w, h = size
    x = [[o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in bottom surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in upper surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],  # x coordinate of points in outside surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]]]  # x coordinate of points in inside surface
    y = [[o[1], o[1], o[1] + w, o[1] + w, o[1]],  # y coordinate of points in bottom surface
         [o[1], o[1], o[1] + w, o[1] + w, o[1]],  # y coordinate of points in upper surface
         [o[1], o[1], o[1], o[1], o[1]],          # y coordinate of points in outside surface
         [o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]]    # y coordinate of points in inside surface
    z = [[o[2], o[2], o[2], o[2], o[2]],                        # z coordinate of points in bottom surface
         [o[2] + h, o[2] + h, o[2] + h, o[2] + h, o[2] + h],    # z coordinate of points in upper surface
         [o[2], o[2], o[2] + h, o[2] + h, o[2]],                # z coordinate of points in outside surface
         [o[2], o[2], o[2] + h, o[2] + h, o[2]]]                # z coordinate of points in inside surface
    return x, y, z

def plotCubeAt(pos=(0,0,0), c="b", alpha=0.1, ax=None):
    # Plotting N cube elements at position pos
    if ax !=None:
        X, Y, Z = cuboid_data( (pos[0],pos[1],pos[2]) )
        ax.plot_surface(X, Y, Z, color=c, rstride=1, cstride=1, alpha=0.1)

def plotMatrix(ax, x, y, z, data, cmap="jet", cax=None, alpha=0.1):
    # plot a Matrix 
    norm = matplotlib.colors.Normalize(vmin=data.min(), vmax=data.max())
    colors = lambda i,j,k : matplotlib.cm.ScalarMappable(norm=norm,cmap = cmap).to_rgba(data[i,j,k]) 
    for i, xi in enumerate(x):
            for j, yi in enumerate(y):
                for k, zi, in enumerate(z):
                    plotCubeAt(pos=(xi, yi, zi), c=colors(i,j,k), alpha=alpha,  ax=ax)



    if cax !=None:
        cbar = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap,
                                norm=norm,
                                orientation='vertical')  
        cbar.set_ticks(np.unique(data))
        # set the colorbar transparent as well
        cbar.solids.set(alpha=alpha)              



if __name__ == '__main__':

    # x and y and z coordinates
    x = np.array(range(10))
    y = np.array(range(10,15))
    z = np.array(range(15,20))
    data_value = np.random.randint(1,4, size=(len(x), len(y), len(z)) )
    print data_value.shape

    fig = plt.figure(figsize=(10,4))
    ax = fig.add_axes([0.1, 0.1, 0.7, 0.8], projection='3d')
    ax_cb = fig.add_axes([0.8, 0.3, 0.05, 0.45])
    ax.set_aspect('equal')

    plotMatrix(ax, x, y, z, data_value, cmap="jet", cax = ax_cb)

    plt.savefig(__file__+".png")
    plt.show()

这里输入图片描述 我发现这里的东西真的很难看清楚,但这可能是品味问题,现在希望也能回答问题。


原始答案:

看来我误解了问题。因此以下内容不回答问题。暂时保留下面的评论供其他人使用。

我认为plot_surface对于指定的任务是可以的。

基本上,您将在3D中绘制由点X,Y,Z给出形状的表面,并使用data_values中的值进行着色,如下面的代码所示。

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np

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

# as plot_surface needs 2D arrays as input
x = np.arange(10)
y = np.array(range(10,15))
# we make a meshgrid from the x,y data
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

# data_value shall be represented by color
data_value = np.random.rand(len(y), len(x))
# map the data to rgba values from a colormap
colors = cm.ScalarMappable(cmap = "viridis").to_rgba(data_value)


# plot_surface with points X,Y,Z and data_value as colors
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors,
                       linewidth=0, antialiased=True)

plt.show()

enter image description here


1
这不完全是我想要的。在你的例子中,每个(x,y)对只有一个z值。在我的情况下,z是一个独立的坐标,即对于每个(x,y)对,我将拥有所有可能的z值(而不仅仅是一个值)。如果我漏掉了什么,如果您使用我在示例代码中提供的3D数据修改示例,那将非常有帮助。 - user3076813
1
@user3076813 我可能完全误解了你的问题。但如果是这样,我认为你首先需要定义你的映射。我的确是从 R x R -> R (2D -> 1D 映射) 进行映射。如果你真的想要一个 R x R x R -> R 的映射,那么结果图应该是什么样子的呢?一堆半透明的立方体吗?一旦你定义好了,我们就可以看看是否可以用 matplotlib 实现。 - ImportanceOfBeingErnest
1
我实际上想要一个R x R x R --> Z+ 的映射(其中Z+是非负整数集)。对于我上面给出的2D示例,每个(x,y)点都有一个彩色正方形。对于3D情况,我希望每个(x,y,z)点都有一个(如果可能的话是半透明的)彩色立方体。 - user3076813
1
好的,有点令人困惑的是,您在评论中回答了“您想要一个3D表面[...]”的问题,并回答“正确”。我非常怀疑您想要的实际上是提供一个信息丰富的图。 - ImportanceOfBeingErnest
1
抱歉造成困惑。我想我误解了那个问题。所以,在Python中没有办法做这样的事情吗?有什么建议可以制作更具信息性的图表吗? - user3076813
显示剩余6条评论

1

我已经更新了上面的代码,使其与较新版本的matplot lib兼容。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colorbar
from matplotlib import cm

viridis = cm.get_cmap('plasma', 8) #Our color map

def cuboid_data(center, size=(1,1,1)):
    # code taken from
    # https://dev59.com/U4vda4cB1Zd3GeqPdb-g?noredirect=1&lq=1
    # suppose axis direction: x: to left; y: to inside; z: to upper
    # get the (left, outside, bottom) point
    o = [a - b / 2 for a, b in zip(center, size)]
    # get the length, width, and height
    l, w, h = size
    x =  np.array([[o[0], o[0] + l, o[0] + l, o[0], o[0]],      # x coordinate of points in bottom surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],                # x coordinate of points in upper surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]],                # x coordinate of points in outside surface
         [o[0], o[0] + l, o[0] + l, o[0], o[0]]])               # x coordinate of points in inside surface
    y =  np.array([[o[1], o[1], o[1] + w, o[1] + w, o[1]],      # y coordinate of points in bottom surface
         [o[1], o[1], o[1] + w, o[1] + w, o[1]],                # y coordinate of points in upper surface
         [o[1], o[1], o[1], o[1], o[1]],                        # y coordinate of points in outside surface
         [o[1] + w, o[1] + w, o[1] + w, o[1] + w, o[1] + w]])   # y coordinate of points in inside surface
    z =  np.array([[o[2], o[2], o[2], o[2], o[2]],              # z coordinate of points in bottom surface
         [o[2] + h, o[2] + h, o[2] + h, o[2] + h, o[2] + h],    # z coordinate of points in upper surface
         [o[2], o[2], o[2] + h, o[2] + h, o[2]],                # z coordinate of points in outside surface
         [o[2], o[2], o[2] + h, o[2] + h, o[2]]])               # z coordinate of points in inside surface
    return x, y, z

def plotCubeAt(pos=(0,0,0), c="b", alpha=0.1, ax=None):
    # Plotting N cube elements at position pos
    if ax !=None:
        X, Y, Z = cuboid_data( (pos[0],pos[1],pos[2]) )
        ax.plot_surface(X, Y, Z, color=c, rstride=1, cstride=1, alpha=0.1)

def plotMatrix(ax, x, y, z, data, cmap=viridis, cax=None, alpha=0.1):
    # plot a Matrix 
    norm = matplotlib.colors.Normalize(vmin=data.min(), vmax=data.max())
    colors = lambda i,j,k : matplotlib.cm.ScalarMappable(norm=norm,cmap = cmap).to_rgba(data[i,j,k]) 
    for i, xi in enumerate(x):
            for j, yi in enumerate(y):
                for k, zi, in enumerate(z):
                    plotCubeAt(pos=(xi, yi, zi), c=colors(i,j,k), alpha=alpha,  ax=ax)



    if cax !=None:
        cbar = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap,
                                norm=norm,
                                orientation='vertical')  
        cbar.set_ticks(np.unique(data))
        # set the colorbar transparent as well
        cbar.solids.set(alpha=alpha)              



if __name__ == '__main__':

    # x and y and z coordinates
    x = np.array(range(10))
    y = np.array(range(10,15))
    z = np.array(range(15,20))
    data_value = np.random.randint(1,4, size=(len(x), len(y), len(z)) )
    print(data_value.shape)

    fig = plt.figure(figsize=(10,4))
    ax = fig.add_axes([0.1, 0.1, 0.7, 0.8], projection='3d')
    ax_cb = fig.add_axes([0.8, 0.3, 0.05, 0.45])
    ax.set_aspect('auto')

    plotMatrix(ax, x, y, z, data_value, cmap=viridis, cax = ax_cb)

    plt.savefig(__file__+".png")
    plt.show()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接