[英]How do I remove overflow along the z-axis for a 3D matplotlib surface?
[英]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 热图。 请注意,每个正方形的角只有 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.