在 matplotlib imshow 中调整网格线和刻度线

Posted

技术标签:

【中文标题】在 matplotlib imshow 中调整网格线和刻度线【英文标题】:Adjusting gridlines and ticks in matplotlib imshow 【发布时间】:2016-12-22 18:25:35 【问题描述】:

我正在尝试绘制一个值矩阵,并希望添加网格线以使值之间的边界更清晰。 不幸的是,imshow 决定将刻度线定位在每个体素的中间。有没有可能

a) 删除刻度,但将标签保留在同一位置,并且 b) 在像素边界之间添加网格线?

import matplotlib.pyplot as plt
import numpy as np

im = plt.imshow(np.reshape(np.random.rand(100), newshape=(10,10)),
                    interpolation='none', vmin=0, vmax=1, aspect='equal');
ax = plt.gca();
ax.set_xticks(np.arange(0, 10, 1));
ax.set_yticks(np.arange(0, 10, 1));
ax.set_xticklabels(np.arange(1, 11, 1));
ax.set_yticklabels(np.arange(1, 11, 1));

图像没有网格线并且刻度线位置错误

ax.grid(color='w', linestyle='-', linewidth=2)

网格线位置错误的图像:

【问题讨论】:

设置图像范围似乎可以解决问题;默认情况下,网格坐标系会稍微偏离自然原点:plt.imshow(..., extent=(0, 10, 10, 0)); 【参考方案1】:

这是一种 hack,但我喜欢使用它,因为它不需要我移动 xticks 或 yticks。

import numpy as np
import matplotlib.pyplot as plt

plt.matshow(np.random.random(size=(10,10)))

plt.hlines(y=np.arange(0, 10)+0.5, xmin=np.full(10, 0)-0.5, xmax=np.full(10, 10)-0.5, color="black")
plt.vlines(x=np.arange(0, 10)+0.5, ymin=np.full(10, 0)-0.5, ymax=np.full(10, 10)-0.5, color="black")

plt.show()

【讨论】:

【参考方案2】:

Serenity 建议的解决方案代码:

plt.figure()
im = plt.imshow(np.reshape(np.random.rand(100), newshape=(10,10)),
                interpolation='none', vmin=0, vmax=1, aspect='equal')

ax = plt.gca();

# Major ticks
ax.set_xticks(np.arange(0, 10, 1))
ax.set_yticks(np.arange(0, 10, 1))

# Labels for major ticks
ax.set_xticklabels(np.arange(1, 11, 1))
ax.set_yticklabels(np.arange(1, 11, 1))

# Minor ticks
ax.set_xticks(np.arange(-.5, 10, 1), minor=True)
ax.set_yticks(np.arange(-.5, 10, 1), minor=True)

# Gridlines based on minor ticks
ax.grid(which='minor', color='w', linestyle='-', linewidth=2)

生成的图像:

【讨论】:

【参考方案3】:

使用plt.pcolorplt.pcolormesh 会更容易:

data = np.random.rand(10, 10)
plt.pcolormesh(data, edgecolors='k', linewidth=2)
ax = plt.gca()
ax.set_aspect('equal')

不过,它们和plt.imshow 有一些区别,最明显的就是图像被Y 轴交换了(不过,你可以通过添加ax.invert_yaxis() 轻松地将其反转回来)。如需进一步讨论,请参见此处:When to use imshow over pcolormesh?

【讨论】:

这更容易,不需要手动处理刻度/标签。这应该是公认的答案。【参考方案4】:

您可以通过将extent 参数传递给imshow 来移动像素。 extent 是一个 4 元素的标量列表(左、右、下、上):

foo = np.random.rand(35).reshape(5, 7)
# This keeps the default orientation (origin at top left):
extent = (0, foo.shape[1], foo.shape[0], 0)
_, ax = plt.subplots()
ax.imshow(foo, extent=extent)
ax.grid(color='w', linewidth=2)
ax.set_frame_on(False)

【讨论】:

【参考方案5】:

尝试移动坐标轴刻度:

ax = plt.gca()
ax.set_xticks(np.arange(-.5, 10, 1))
ax.set_yticks(np.arange(-.5, 10, 1))
ax.set_xticklabels(np.arange(1, 12, 1))
ax.set_yticklabels(np.arange(1, 12, 1))

【讨论】:

谢谢,这非常适合网格线。有没有办法让标签同时保持在像素的中心? 将主要刻度位置设置为每个带有标签的正方形的中间。然后将次要刻度设置到每个没有标签的正方形的边缘。显示网格后仅在次要刻度中显示。 您可以发布自己的代码作为新答案(带/不带图像),供任何挑战此类问题的人使用。

以上是关于在 matplotlib imshow 中调整网格线和刻度线的主要内容,如果未能解决你的问题,请参考以下文章

matplotlib pyplot imshow 图像之间的紧密间距

如何在 matplotlib 中调整(偏移)颜色条标题

matplotlib 网格线不要覆盖柱状图

在 python (Matplotlib) 中使用 subplot 和 imshow 时删除白色边框

Matplotlib:带有第二个 y 轴的 imshow

Matplotlib:imshow中cmap的作用是啥?