繁体   English   中英

Python、Matplotlib:在 3D 中沿 z 轴堆叠多个热图

[英]Python, Matplotlib: Stack multiple heatmaps on top of each other along z-axis in 3D

我有两个要在 3D 视图中叠加显示的热图。 热图是用 imshow() 绘制的,它正确地将数据的每个元素设置为 plot 中的彩色方块。 但是,当使用 plot_surface() 绘制相同的热图时,现在每个角都代表每个元素。 请注意,第一个图有 15x15 的方格,每个方格的中间都有刻度,第二个图只有 14x14 的方格,每个方格的角落都有刻度。 由于我正在处理离散数据并且对每个 (x,y) 组合感兴趣,因此第二个表示没有意义。

如何使 3D 中的热图以与 2D 热图相同的方式显示? 也就是说,我怎样才能 plot a 3D plot 在每个正方形中间设置 x 和 y 刻度,并正确绘制 15x15 元素? (请注意,热图中的 colors 目前与 2D 到 3D 的情况不同,这很好)

代码

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


# Dummy data
X = range(2, 16+1)
Y = range(2, 16+1)
xs, ys = np.meshgrid(X, Y)

zs1 = np.random.rand(15,15)
zs2 = np.random.rand(15,15)

# Imshow 2D plot
_, (ax1, ax2) = plt.subplots(2,1)
plot = ax1.imshow(np.flip(zs1, 0), cmap=plt.cm.RdYlGn, interpolation='none', extent=[1.5, 16.5, 1.5, 16.5])
plot = ax2.imshow(np.flip(zs2, 0), cmap=plt.cm.RdYlGn, interpolation='none', extent=[1.5, 16.5, 1.5, 16.5])
plt.draw()

# Surface 3D plot
fig = plt.figure()
ax2 = Axes3D(fig)
plot = ax2.plot_surface(xs, ys, zs1, rstride=1, cstride=1,
                    antialiased=False, linewidth=0, cmap=plt.cm.RdYlGn)
plot = ax2.plot_surface(xs, ys, zs2 + 1000, rstride=1, cstride=1,
                    antialiased=False, linewidth=0, cmap=plt.cm.RdYlGn)


plt.show()

二维热图

二维热图。 请注意,每个正方形的中间有 15x15 个元素和刻度。

3D 热图

3D 热图。 请注意,每个正方形的角只有 14x14 个元素和刻度。 我希望这些以与 2D 热图相同的方式显示!!

我想你已经理解了核心问题: plot_surface是指 plot 表面,而不是倾斜的热图。 例如,您大幅增加 z 范围以在 3 个维度上“展平”两个曲面,因为这些曲面的值分别在 [0, 1] 和 [1000, 1001] 区间内。

因为plot_surfaces是用于表面的,它会将您的样本解释为点估计值,然后在点估计值之间进行插值以计算点之间平均表面高度的估计值。 因此,一个 15x15 的点估计数组会产生 14x14 的表面,尽管您正在应用相同的颜色图,但 colors 都不匹配。 如果有这种行为,我会推荐著名/臭名昭著的文章“像素不是小正方形!像素不是小正方形!像素不是小正方形!(体素不是小立方体!)”以供进一步阅读对你来说似乎不合逻辑。

了解了为什么plot_surfaces以这种方式处理数据后,很明显一种解决方案是对数据进行上采样:

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

# Dummy data
zs1 = np.random.rand(15,15)
zs2 = np.random.rand(15,15)

# Imshow 2D plot
fig, (ax1, ax2) = plt.subplots(2,1)
plot = ax1.imshow(np.flip(zs1, 0), cmap=plt.cm.RdYlGn, interpolation='none', extent=[1.5, 16.5, 1.5, 16.5], vmin=0, vmax=1)
plot = ax2.imshow(np.flip(zs2, 0), cmap=plt.cm.RdYlGn, interpolation='none', extent=[1.5, 16.5, 1.5, 16.5], vmin=0, vmax=1)
plt.draw()

# Surface 3D plot
upsample_by = 20
X = np.linspace(2, 16, 15*upsample_by)
Y = np.linspace(2, 16, 15*upsample_by)
xs, ys = np.meshgrid(X, Y)
zs1 = np.repeat(np.repeat(zs1, upsample_by, axis=0), upsample_by, axis=1)
zs2 = np.repeat(np.repeat(zs2, upsample_by, axis=0), upsample_by, axis=1)

fig3 = plt.figure()
ax3 = Axes3D(fig3)
plot = ax3.plot_surface(xs, ys, zs1, rstride=1, cstride=1,
                        antialiased=False, linewidth=0, cmap=plt.cm.RdYlGn, vmin=0, vmax=1)
plot = ax3.plot_surface(xs, ys, zs2 + 1000, rstride=1, cstride=1,
                        antialiased=False, linewidth=0, cmap=plt.cm.RdYlGn, vmin=1000, vmax=1001)


plt.show()

在此处输入图像描述

在此处输入图像描述

我不建议这样做,因为总而言之,您的解决方案非常老套,我的调整只会让情况变得更糟。 就个人而言,我会将每个单独的像素绘制为 3D 中的一个小正方形,并适当着色。 此 matplotlib 教程演示了如何使用 3-D 投影向轴添加 2-D 补丁。

基于此,我们可以编写一个小 function 来绘制 3D 中定义高度的热图:

#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import mpl_toolkits.mplot3d.art3d as art3d


def tilted_heatmap_in_3d(arr, z, cmap=plt.cm.RdYlGn, ax=None):
    if ax is None:
        fig = plt.figure()
        ax = fig.add_subplot(projection='3d')

    for ii, row in enumerate(arr):
        for jj, value in enumerate(row):
            r = Rectangle((ii-0.5, jj-0.5), 1, 1, color=cmap(value))
            ax.add_patch(r)
            art3d.pathpatch_2d_to_3d(r, z=z, zdir="z")

    ax.set_xlim(-1, ii+1)
    ax.set_ylim(-1, jj+1)
    ax.set_zlim(0, 2*z)
    ax.get_figure().canvas.draw()


if __name__ == '__main__':

    tilted_heatmap_in_3d(np.random.rand(15, 15), z=5)
    plt.show()

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM