簡體   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