如何在不更改覆盖 ax.text() 句柄位置的情况下在 matplotlib ax.imshow() 中使用 `extent`?
How to use `extent` in matplotlib ax.imshow() without changing the positions of the overlayed ax.text() handles?
我正在尝试注释热图。 matplotlib docs present an example, which suggests creating a helper function to format the annotations. I feel there must be a simpler way to do what I want. I can annotate inside the boxes of the heatmap, but these texts change position when editing the 。我的问题是如何在 ax.imshow(...)
中使用 extent
,同时还使用 ax.text(...)
来注释正确的位置。下面是一个例子:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
def get_manhattan_distance_matrix(coordinates):
shape = (coordinates.shape[0], 1, coordinates.shape[1])
ct = coordinates.reshape(shape)
displacement = coordinates - ct
return np.sum(np.abs(displacement), axis=-1)
x = np.arange(11)[::-1]
y = x.copy()
coordinates = np.array([x, y]).T
distance_matrix = get_manhattan_distance_matrix(coordinates)
# print("\n .. {} COORDINATES:\n{}\n".format(coordinates.shape, coordinates))
# print("\n .. {} DISTANCE MATRIX:\n{}\n".format(distance_matrix.shape, distance_matrix))
norm = Normalize(vmin=np.min(distance_matrix), vmax=np.max(distance_matrix))
这里是修改extent
的值的地方。
extent = (np.min(x), np.max(x), np.min(y), np.max(y))
# extent = None
根据matplotlib docs,默认的extent
是None
。
fig, ax = plt.subplots()
handle = ax.imshow(distance_matrix, cmap='plasma', norm=norm, interpolation='nearest', origin='upper', extent=extent)
kws = dict(ha='center', va='center', color='gray', weight='semibold', fontsize=5)
for i in range(len(distance_matrix)):
for j in range(len(distance_matrix[i])):
if i == j:
ax.text(j, i, '', **kws)
else:
ax.text(j, i, distance_matrix[i, j], **kws)
plt.show()
plt.close(fig)
一个人可以通过修改extent
生成两个数字——简单地取消注释行的注释并注释未注释行。两图如下:
可以看出,通过设置extent
,像素位置发生变化,进而改变ax.text(...)
手柄的位置。有没有一个简单的解决方案来解决这个问题 - 即设置任意 extent
并且文本句柄仍然在每个框中居中?
当extent=None
时,x和y的有效范围都是从-0.5到10.5。所以中心位于整数位置。将范围设置为 0 到 10 不与像素对齐。您必须乘以 10/11 才能使它们正确。
最好的方法是设置 extent = (np.min(x)-0.5, np.max(x)+0.5, np.min(y)-0.5, np.max(y)+0.5)
以使中心回到整数位置。
另请注意,默认情况下图像是从顶部开始显示的,并且 y 轴是反转的。如果更改范围,要使图像正立,则需要 ax.imshow(..., origin='lower')
。 (0,0 像素应该是示例图中的蓝色像素。)
要将文本放在像素的中心,您可以将水平索引加 0.5,除以以像素为单位的宽度,然后乘以 x 轴的差值。以及 y 轴的类似计算。为了获得更好的可读性,可以使文本颜色依赖于像素颜色。
# ...
extent = (np.min(x), np.max(x), np.min(y), np.max(y))
x0, x1, y0, y1 = extent
fig, ax = plt.subplots()
handle = ax.imshow(distance_matrix, cmap='plasma', norm=norm, interpolation='nearest', origin='lower', extent=extent)
kws = dict(ha='center', va='center', weight='semibold', fontsize=5)
height = len(distance_matrix)
width = len(distance_matrix[0])
for i in range(height):
for j in range(width):
if i != j:
val = distance_matrix[i, j]
ax.text(x0 + (j + 0.5) / width * (x1 - x0), y0 + (i + 0.5) / height * (y1 - y0),
f'{val}\n{i},{j}', color='white' if norm(val) < 0.6 else 'black', **kws)
plt.show()
我正在尝试注释热图。 matplotlib docs present an example, which suggests creating a helper function to format the annotations. I feel there must be a simpler way to do what I want. I can annotate inside the boxes of the heatmap, but these texts change position when editing the ax.imshow(...)
中使用 extent
,同时还使用 ax.text(...)
来注释正确的位置。下面是一个例子:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
def get_manhattan_distance_matrix(coordinates):
shape = (coordinates.shape[0], 1, coordinates.shape[1])
ct = coordinates.reshape(shape)
displacement = coordinates - ct
return np.sum(np.abs(displacement), axis=-1)
x = np.arange(11)[::-1]
y = x.copy()
coordinates = np.array([x, y]).T
distance_matrix = get_manhattan_distance_matrix(coordinates)
# print("\n .. {} COORDINATES:\n{}\n".format(coordinates.shape, coordinates))
# print("\n .. {} DISTANCE MATRIX:\n{}\n".format(distance_matrix.shape, distance_matrix))
norm = Normalize(vmin=np.min(distance_matrix), vmax=np.max(distance_matrix))
这里是修改extent
的值的地方。
extent = (np.min(x), np.max(x), np.min(y), np.max(y))
# extent = None
根据matplotlib docs,默认的extent
是None
。
fig, ax = plt.subplots()
handle = ax.imshow(distance_matrix, cmap='plasma', norm=norm, interpolation='nearest', origin='upper', extent=extent)
kws = dict(ha='center', va='center', color='gray', weight='semibold', fontsize=5)
for i in range(len(distance_matrix)):
for j in range(len(distance_matrix[i])):
if i == j:
ax.text(j, i, '', **kws)
else:
ax.text(j, i, distance_matrix[i, j], **kws)
plt.show()
plt.close(fig)
一个人可以通过修改extent
生成两个数字——简单地取消注释行的注释并注释未注释行。两图如下:
可以看出,通过设置extent
,像素位置发生变化,进而改变ax.text(...)
手柄的位置。有没有一个简单的解决方案来解决这个问题 - 即设置任意 extent
并且文本句柄仍然在每个框中居中?
当extent=None
时,x和y的有效范围都是从-0.5到10.5。所以中心位于整数位置。将范围设置为 0 到 10 不与像素对齐。您必须乘以 10/11 才能使它们正确。
最好的方法是设置 extent = (np.min(x)-0.5, np.max(x)+0.5, np.min(y)-0.5, np.max(y)+0.5)
以使中心回到整数位置。
另请注意,默认情况下图像是从顶部开始显示的,并且 y 轴是反转的。如果更改范围,要使图像正立,则需要 ax.imshow(..., origin='lower')
。 (0,0 像素应该是示例图中的蓝色像素。)
要将文本放在像素的中心,您可以将水平索引加 0.5,除以以像素为单位的宽度,然后乘以 x 轴的差值。以及 y 轴的类似计算。为了获得更好的可读性,可以使文本颜色依赖于像素颜色。
# ...
extent = (np.min(x), np.max(x), np.min(y), np.max(y))
x0, x1, y0, y1 = extent
fig, ax = plt.subplots()
handle = ax.imshow(distance_matrix, cmap='plasma', norm=norm, interpolation='nearest', origin='lower', extent=extent)
kws = dict(ha='center', va='center', weight='semibold', fontsize=5)
height = len(distance_matrix)
width = len(distance_matrix[0])
for i in range(height):
for j in range(width):
if i != j:
val = distance_matrix[i, j]
ax.text(x0 + (j + 0.5) / width * (x1 - x0), y0 + (i + 0.5) / height * (y1 - y0),
f'{val}\n{i},{j}', color='white' if norm(val) < 0.6 else 'black', **kws)
plt.show()