Matplotlib bug:使用twinx()时数据显示在脊柱上方

3 投票
2 回答
762 浏览
提问于 2025-04-18 01:53

我正在尝试绘制两个数据集,它们的数值差别很大。经过一些研究(其实只需在谷歌上搜索一下),我发现了一个叫做 twinx() 的函数。

不过,在使用这个函数时我遇到了一些麻烦。我想用这个工具制作出可以发表的高质量图表,但有些地方让我感到困扰。

我的系统是:matplotlib v1.3.1,python v2.7.4,运行在Ubuntu上。

看看下面这段代码:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from matplotlib.ticker import AutoMinorLocator


time = np.arange(0, 365, 1)
y1 = np.random.rand(len(time)) * np.exp(-0.03 * time)
y2 = 0.001 * np.random.rand(len(time)) * np.exp(-0.02 * time)

for i in range(30):
    y2[-i] = 0

fig = plt.figure(figsize=(8,6), dpi=300)
fig.subplots_adjust(hspace=0.0, right=0.9, top=0.94, left=0.12, bottom=0.07)
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()

for tl in ax1.get_yticklabels():
    tl.set_color('r')
for tl in ax2.get_yticklabels():
    tl.set_color('b')

for ax in [ax1, ax2]:
    ax.yaxis.set_major_locator(MaxNLocator(
        nbins=5,
        steps=[1,2,3,4,5,10],
        integer=False,
        symmetric=False,
        prune=None))
    ax.yaxis.set_minor_locator(AutoMinorLocator(4))

ax2.plot(time, y1, 'b-')
ax1.plot(time, y2, 'r-')
ax1.set_ylabel(r"y value", labelpad=15)
ax1.set_xlabel(r"x value")
ax1.set_xlim(0,365)
ax1.set_xticks(range(0,370,30))
ax1.xaxis.set_minor_locator(AutoMinorLocator(3))

fig.savefig("Errorplot.png", dpi=60)

现在,当我查看这个低分辨率的输出时,没发现什么奇怪的地方:

错误图

但是,当我放大到高分辨率(比如说,dpi=300)时,我看到的情况是这样的:

错误图的放大部分

很明显,第二个坐标轴的线条覆盖在第一个坐标轴的边框上。

我该如何解决这个问题呢?有没有办法重新绘制坐标轴的边框?

我尝试过的办法

  • 改变 plot() 函数的调用顺序
  • zorder 参数设置为 -1 或 -10。即使第二个坐标轴的 zorder 小于第一个,我仍然遇到这个问题。
  • 结合上面的做法: ax2.spines['bottom'].set_zorder(10)

2 个回答

2

你可以在matplotlib中使用 zorder 这个参数 来改变对象绘制的顺序。这里有一个示例代码 可以查看

下面我提供了两个例子(为了节省空间,我删减了大部分代码)。在一个例子中,我把 zorder 设置为一个较大的值,这意味着这个对象会最后绘制(所以会在最上面)。在另一个例子中,我把 zorder 设置为-1,这意味着这个对象会最先绘制(所以会在下面)。

ax2.plot(time, y1, 'b-', zorder=5)
ax1.plot(time, y2, 'r-', zorder=5)

示例图

ax2.plot(time, y1, 'b-', zorder=-1)
ax1.plot(time, y2, 'r-', zorder=-1)

示例图 2

4

这个帖子似乎没有很多人看,但我觉得它很有帮助,所以想分享另一种改变 zorder 的方法,希望能帮到其他人。这个帖子更贴近我想做的事情,但最重要的是改变 zorder,这是 @Ffisegydd 提到的。还有一种方法可以做到这一点,代码如下:

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.set_zorder(ax2.get_zorder()+1) # put ax1 in front of ax2 
ax1.patch.set_visible(False) # hide the 'canvas' 

ax1.plot(data1[:,0], data1[:,0])  # plot whatever data you want
ax2.plot(data2[:,0], data2[:,0])  # plot commands can precede the set_zorder command

我知道这并没有直接解决提问者的问题,但希望它能帮助到像我一样,正在寻找相关问题解决方案的人。

撰写回答