You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python与Matplotlib:实现3D空间沿Z轴堆叠的热力图与2D热力图显示效果一致

解决3D堆叠热力图与2D imshow显示一致的问题

问题的核心在于plot_surface和imshow对数据的解析逻辑不同:imshow默认把每个数据点当作色块中心,自动扩展到色块边缘;而plot_surface是通过相邻数据点连接成面,默认会少一行一列的色块。要让3D效果和2D完全匹配,这里有两种实用方案:

方案一:用pcolor3d直接匹配imshow效果

pcolor3d是matplotlib专门为离散3D色块设计的函数,行为和2D的imshow完全一致,能直接把每个数据点对应成一个独立色块,刻度也会自动落在色块中心,是最省心的解决方案。

修改后的代码如下:

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

# 生成测试数据
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)

# 2D imshow 热力图
_, (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()

# 3D 堆叠热力图(使用pcolor3d)
fig = plt.figure()
ax3d = Axes3D(fig)

# 绘制第一个热力图,固定在z=0平面
norm1 = plt.Normalize(zs1.min(), zs1.max())
face_colors1 = plt.cm.RdYlGn(norm1(zs1))
ax3d.pcolor3d(xs, ys, np.zeros_like(zs1), facecolors=face_colors1, edgecolors='none')

# 绘制第二个热力图,固定在z=1000平面
norm2 = plt.Normalize(zs2.min(), zs2.max())
face_colors2 = plt.cm.RdYlGn(norm2(zs2))
ax3d.pcolor3d(xs, ys, np.full_like(zs2, 1000), facecolors=face_colors2, edgecolors='none')

# 设置坐标轴刻度,和2D图对齐
ax3d.set_xticks(X)
ax3d.set_yticks(Y)
ax3d.set_zlim(-500, 1500)  # 调整z轴范围让两个热力图分开显示

plt.show()

方案二:调整plot_surface的网格坐标

如果你坚持使用plot_surface,需要手动扩展网格到色块边缘,并调整数据维度来模拟离散色块:

  1. 生成色块边缘坐标:原中心坐标是2-16,每个色块宽度为1,所以边缘坐标范围是1.5到16.5,共16个点(对应15个色块)。
  2. 扩展z数据的维度,让每个边缘点都有对应的高度值(这里用pad函数重复最后一行/列的数值)。

修改后的代码如下:

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

# 生成测试数据
X_center = np.arange(2, 17)
Y_center = np.arange(2, 17)
zs1 = np.random.rand(15,15)
zs2 = np.random.rand(15,15)

# 生成色块边缘的坐标轴
X_edge = np.arange(1.5, 17)  # 1.5到16.5,共16个点
Y_edge = np.arange(1.5, 17)
xs_edge, ys_edge = np.meshgrid(X_edge, Y_edge)

# 扩展z数据到边缘维度(16x16),用边缘值填充新增的行/列
zs1_extended = np.pad(zs1, ((0,1),(0,1)), mode='edge')
zs2_extended = np.pad(zs2, ((0,1),(0,1)), mode='edge')

# 2D imshow 热力图
_, (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()

# 3D 堆叠热力图(调整后的plot_surface)
fig = plt.figure()
ax3d = Axes3D(fig)
plot = ax3d.plot_surface(xs_edge, ys_edge, zs1_extended, rstride=1, cstride=1, antialiased=False, linewidth=0, cmap=plt.cm.RdYlGn)
plot = ax3d.plot_surface(xs_edge, ys_edge, zs2_extended + 1000, rstride=1, cstride=1, antialiased=False, linewidth=0, cmap=plt.cm.RdYlGn)

# 设置刻度到色块中心
ax3d.set_xticks(X_center)
ax3d.set_yticks(Y_center)

plt.show()

关键说明

  • pcolor3d方案更简洁,完全贴合离散数据的展示需求,优先推荐使用。
  • plot_surface本质是绘制连续曲面,需要手动扩展数据来模拟离散色块,适合必须使用曲面绘制的场景。
  • 两种方案都需要将xticks和yticks设置为原中心坐标(2-16),确保刻度落在每个色块的中间位置。

内容的提问来源于stack exchange,提问作者LucasStromberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 15:07:30