0

我正在尝试生成稀疏数组的二维图,imshow()并使用plt.text()文本框将其覆盖。我想出了一个额外的选项,使用plt.scatter(). 在第二种情况下,彩色图块和文本框太小,无法缩放。在第一种情况下,由imshow()和文本框生成的彩色图块的大小具有不连贯的大小,并且只有在使用对话框窗口的缩放功能时,绘图才看起来很好。下面的代码说明了这一点。

import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np

#https://matplotlib.org/gallery/images_contours_and_fields/image_annotated_heatmap.html

P=[1, 4, 11, 18, 20, 39, 40, 41, 41, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 73, 73, 73, 74, 74, 74, 74, 74, 74, 75, 75, 75, 71]
N=[2, 3, 11, 19, 25, 49, 48, 50, 54, 101, 102, 103, 103, 106, 106, 100, 103, 106, 106, 107, 109, 105, 106, 109, 104, 107, 109, 110, 111, 112, 108, 109, 109, 101]
B=np.random.rand(34)

# crate the array to use with imshow()
A=np.zeros((max(N)+1,max(N)+1))
for i,j,k in zip(N,P,B):
     A[i,j]=k

def plot_map(N,P,A):     
    fig, ax = plt.subplots()     
    plt.imshow(A,norm=colors.LogNorm(),cmap='jet',origin='lower')
    plt.colorbar()
    for n,p in zip(N,P):
            ax.text(p,n, "\n%s\n%s\n%5.1E"%(p,n,A[n,p]),
                ha="center", va="center",
            bbox=dict(fc="none",boxstyle = "square"))
    plt.tight_layout()
    plt.show()

# call the plot function
plot_map(N,P,A)    

# attempt tow using plt.scatter() 
plt.scatter(N,P,c=B,marker='s',s=70,norm=colors.LogNorm(),cmap='jet')
for n,p in zip(N,P):
         plt.text(n,p, "\n%s\n%s"%(p,n), size=3,
             va="center", ha="center", multialignment="left",
             bbox=dict(fc="none",boxstyle = "square"))
plt.colorbar()
plt.show()

理想情况下,我想制作这样的东西

我的绘图程序生成的东西看起来不太好,彩色瓷砖和注释框都是不连续的。
因此,我将感谢您的帮助。

4

1 回答 1

2

以下方法用于mplcursors在屏幕上显示信息,并保存可以打印的图像文件。

在 A4 纸上打印时,每个小正方形大约为 2x2 毫米,因此一台好的打印机和一个镜子会很有帮助。您可能想尝试使用字体大小。

在屏幕上,mplcursors单击一个小方块时会显示一个弹出注释。放大时,需要双击以避免干扰缩放 UI。mplcursors也有一个“悬停”模式,但放大时不显示任何信息。

一些代码来演示它是如何工作的:

import matplotlib.pyplot as plt
import matplotlib.colors as colors
import mplcursors
import numpy as np

P = [1, 4, 11, 18, 20, 39, 40, 41, 41, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 73, 73, 73, 74, 74, 74, 74, 74, 74, 75, 75, 75, 71]
N = [2, 3, 11, 19, 25, 49, 48, 50, 54, 101, 102, 103, 103, 106, 106, 100, 103, 106, 106, 107, 109, 105, 106, 109, 104,  107, 109, 110, 111, 112, 108, 109, 109, 101]
B = np.random.rand(34)

# create the array to use with imshow()
A = np.zeros((max(N) + 1, max(N) + 1))
for i, j, k in zip(N, P, B):
    A[i, j] = k

fig, ax = plt.subplots(figsize=(21, 15))
img = ax.imshow(A, norm=colors.LogNorm(), cmap='jet', origin='lower')
plt.colorbar(img)
for n, p in zip(N, P):
    plt.text(p, n, "%s\n%s\n%5.1E"%(p,n,A[n,p]), size=2,
             va="center", ha="center", multialignment="left")

cursor = mplcursors.cursor(img, hover=False)
@cursor.connect("add")
def on_add(sel):
    i,j = sel.target.index
    if A[i][j] == 0:
        sel.annotation.set_visible(False)
    else:
        sel.annotation.set_text(f'P: {j}\nN: {i}\n{A[i][j]:.3f}')

plt.tight_layout()
plt.savefig('test.png', dpi=300)
plt.show()

左侧是放大并双击正方形时在屏幕上的外观。右侧是要打印的图像在放大时的外观。

结果图

如本文TextPath所述,需要放大时获得更大的文本。由于并没有真正处理多行和对齐,代码计算位置。此外,根据框的颜色,文本在白色时更易于阅读。您需要测试在您的情况和颜色图中哪些值是好的截止值。TextPath

为了应对空白,您可以放大到 3 个有数据的地方。下面的代码为这些区域中的每一个创建了一个子图。

import matplotlib.pyplot as plt
import matplotlib.colors as colors
from matplotlib.textpath import TextPath
from matplotlib.patches import PathPatch
from matplotlib.ticker import MaxNLocator
import numpy as np

P = [1, 4, 11, 18, 20, 39, 40, 41, 41, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 73, 73, 73, 74, 74, 74, 74, 74, 74, 75, 75, 75, 71]
N = [2, 3, 11, 19, 25, 49, 48, 50, 54, 101, 102, 103, 103, 106, 106, 100, 103, 106, 106, 107, 109, 105, 106, 109, 104,  107, 109, 110, 111, 112, 108, 109, 109, 101]
B = np.random.rand(34)

# create the array to use with imshow()
A = np.zeros((max(N) + 1, max(N) + 1))
for i, j, k in zip(N, P, B):
    A[i, j] = k

plot_limits = [[[0, 19], [1, 20]],
               [[38, 42], [47 - 1, 55 + 2]],  # second subplot with higher y-range to better fit with the rest
               [[70, 76], [99, 113]],
               [[0, 0.05], [0, 1]]]  # separate subplot for the colorbar

width_ratios = [(lim[0][1] - lim[0][0] ) / (lim[1][1] - lim[1][0]) for lim in plot_limits]

fig, ax = plt.subplots(figsize=(16, 8), ncols=4, gridspec_kw={'width_ratios': width_ratios})
for i in range(3):
    img = ax[i].imshow(A, norm=colors.LogNorm(), cmap='jet', origin='lower')
    for n, p in zip(N, P):
        textsize = 0.3
        for line, label in zip((n + 0.2, n - 0.1, n - 0.4), (f"{p}", f"{n}", f"{A[n, p]:.3f}")):
            tp = TextPath((p - 0.4, line), label, size=0.3)
            ax[i].add_patch(PathPatch(tp, color="black" if 0.08 < A[n, p] < 0.7 else "white"))
    ax[i].xaxis.set_major_locator(MaxNLocator(integer=True))
    ax[i].yaxis.set_major_locator(MaxNLocator(integer=True))
    ax[i].set_xlim(plot_limits[i][0])
    ax[i].set_ylim(plot_limits[i][1])

plt.colorbar(img, cax=ax[3])
plt.tight_layout()
plt.show()

这是它的样子:

三重情节版本

于 2020-01-27T16:24:50.803 回答