Matplotlib方形主/次网格,用于具有不同限制的轴

时间:2018-08-01 11:24:37

标签: python-3.x matplotlib

我有一个带有背景网格的图。即使X轴和Y轴的限制不同,我也需要网格单元(主网格单元和次网格单元)都为正方形。

我当前的代码如下:

import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
import numpy as np

data = [0.014,  0.84,  0.95, -0.42, -0.79,  0.84, 0.98,  1.10,   0.56, -0.49]


fig, ax = plt.subplots(figsize=(20, 5))
ax.minorticks_on()

# Set major and minor grid lines on X
ax.set_xticks(np.arange(0, 10, 0.2))
ax.xaxis.set_minor_locator(plticker.MultipleLocator(base=0.2 / 5.))
for xmaj in ax.xaxis.get_majorticklocs():
        ax.axvline(x=xmaj, ls='-', color='red', linewidth=0.8)
for xmin in ax.xaxis.get_minorticklocs():
    ax.axvline(x=xmin, ls=':', color='red', linewidth=0.6)

# Set major and minor grid lines on Y
ylim = int(np.ceil(max(abs(min(data)), max(data))))
yticks = np.arange(-ylim, ylim + 0.5, 0.5)
ax.set_yticks(yticks)
ax.yaxis.set_minor_locator(plticker.MultipleLocator(base=0.5 / 5.))
for ymaj in ax.yaxis.get_majorticklocs():
        ax.axhline(y=ymaj, ls='-', color='red', linewidth=0.8)
for ymin in ax.yaxis.get_minorticklocs():
    ax.axhline(y=ymin, ls=':', color='red', linewidth=0.6)

ax.axis([0, 10, -ylim, ylim])
fig.tight_layout()

# Plot
ax.plot(data)

# Set equal aspect ratio NOT WORKING
plt.gca().set_aspect('equal', adjustable='box')
plt.show()

哪个生成以下图: enter image description here

大型网格单元每个包含5个较小的单元。但是,大网格的纵横比不是1。 问题:如何确定大网格为正方形?

编辑 当前方法是设置与@ImportanceOfBeingErnest建议的相同的刻度位置,但更改Y标签:

ylim = int(np.ceil(max(abs(min(data)), max(data))))
yticks = np.arange(-ylim, ylim + 0.2, 0.2)

ax.set_yticks(yticks)

labels = ['{:.1f}'.format(v if abs(v) < 1e-3 else (1 if v > 0 else -1)*((0.5 - abs(v)%0.5) + abs(v))) 
          if i%2==0 else "" for i, v in enumerate(np.arange(-ylim, ylim, 0.2))]
ax.set_yticklabels(labels)

结果:似乎太过分了。 enter image description here

2 个答案:

答案 0 :(得分:1)

当使用相等的纵横比并瞄准正方形网格时,两个轴都需要使用相同的刻度间距。这可以通过MultipleLocator来实现,其中x和y轴的间隔必须相同。

通常,可以使用grid命令创建网格。

import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np

data = [0.014,  0.84,  0.95, -0.42, -0.79,  0.84, 0.98,  1.10,   0.56, -0.49]


fig, ax = plt.subplots(figsize=(20, 5))
ax.minorticks_on()

# Set major and minor grid lines on X
ax.xaxis.set_major_locator(mticker.MultipleLocator(base=.5))
ax.xaxis.set_minor_locator(mticker.MultipleLocator(base=0.5 / 5.))

ax.yaxis.set_major_locator(mticker.MultipleLocator(base=.5))
ax.yaxis.set_minor_locator(mticker.MultipleLocator(base=0.5 / 5.))

ax.grid(ls='-', color='red', linewidth=0.8)
ax.grid(which="minor", ls=':', color='red', linewidth=0.6)

## Set limits
ylim = int(np.ceil(max(abs(min(data)), max(data))))
ax.axis([0, 10, -ylim, ylim])
plt.gca().set_aspect('equal', adjustable='box')
fig.tight_layout()

# Plot
ax.plot(data)

plt.show()

enter image description here

如果您想在网格中的方形主要单元格上具有不同的刻度间距,则需要放弃相等的长宽比,而是将其设置为刻度间距的商。

import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np

data = [0.014,  0.84,  0.95, -0.42, -0.79,  0.84, 0.98,  1.10,   0.56, -0.49]


fig, ax = plt.subplots(figsize=(20, 5))
ax.minorticks_on()

xm = 0.2
ym = 0.25

# Set major and minor grid lines on X
ax.xaxis.set_major_locator(mticker.MultipleLocator(base=xm))
ax.xaxis.set_minor_locator(mticker.MultipleLocator(base=xm / 5.))

ax.yaxis.set_major_locator(mticker.MultipleLocator(base=ym))
ax.yaxis.set_minor_locator(mticker.MultipleLocator(base=ym / 5.))

ax.grid(ls='-', color='red', linewidth=0.8)
ax.grid(which="minor", ls=':', color='red', linewidth=0.6)

## Set limits
ylim = int(np.ceil(max(abs(min(data)), max(data))))
ax.axis([0, 10, -ylim, ylim])
plt.gca().set_aspect(xm/ym, adjustable='box')
fig.tight_layout()


# Plot
ax.plot(data)

plt.show()

enter image description here

要摆脱第二个刻度标签,可以使用

fmt = lambda x,p: "%.2f" % x if not x%(2*ym) else ""
ax.yaxis.set_major_formatter(mticker.FuncFormatter(fmt))

答案 1 :(得分:0)

您应该能够通过对两个轴使用相同的定位器来实现此目的。但是matplotlib当前有一个限制,所以这是一种解决方法:

# matplotlib doesnt (currently) allow two axis to share the same locator
# so make two wrapper locators and combine their view intervals
def share_locator(locator):
    class _SharedLocator(matplotlib.ticker.Locator):
        def tick_values(self, vmin, vmax):
            return locator.tick_values(vmin, vmax)

        def __call__(self):
            min0, max0 = shared_locators[0].axis.get_view_interval()
            min1, max1 = shared_locators[1].axis.get_view_interval()
            return self.tick_values(min(min0, min1), max(max0, max1))

    shared_locators = (_SharedLocator(), _SharedLocator())
    return shared_locators

使用方式:

lx, ly = share_locator(matplotlib.ticker.AutoLocator())  # or any other locator
ax.xaxis.set_major_locator(lx)
ax.yaxis.set_major_locator(ly)