【问题标题】:Make matplotlib autoscaling ignore some of the plots使 matplotlib 自动缩放忽略一些图
【发布时间】:2011-09-12 11:03:33
【问题描述】:

我使用 matplotib 的 Axes API 来绘制一些图形。我绘制的其中一条线代表理论预期线。它在原始 y 和 x 限制之外没有任何意义。我想要的是 matlplotlib 在自动缩放限制时忽略它。我以前做的是检查当前限制是多少,然后绘制并重置限制。问题是,当我绘制第三张图时,限制与理论线一起重新计算,这确实扩大了图表。

# Boilerplate
from matplotlib.figure import Figure
from matplotlib.backends.backend_pdf import FigureCanvasPdf
from numpy import sin, linspace


fig = Figure()
ax = fig.add_subplot(1,1,1)

x1 = linspace(-1,1,100)
ax.plot(x1, sin(x1))
ax.plot(x1, 3*sin(x1))
# I wish matplotlib would not consider the second plot when rescaling
ax.plot(x1, sin(x1/2.0))
# But would consider the first and last

canvas_pdf = FigureCanvasPdf(fig)
canvas_pdf.print_figure("test.pdf")

【问题讨论】:

  • 是否可以调整绘制每条曲线的顺序? “理论上的”情节能否出现在最后?
  • @Yann ,我不能保证情节的顺序。这就是为什么在绘图之前保留 xlim 和 ylim 没有帮助。

标签: python matplotlib


【解决方案1】:

显而易见的方法是手动设置您想要的限制。 (例如ax.axis([xmin, xmax, ymin, ymax])

如果您不想手动找出限制,您有几个选择...

正如几个人(tillsten、Yann 和 Vorticity)所提到的,如果您可以绘制最后要忽略的函数,那么您可以在绘制之前禁用自动缩放或将 scaley=False kwarg 传递给 plot

import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
x1 = np.linspace(-1,1,100)

ax.plot(x1, np.sin(x1))
ax.plot(x1, np.sin(x1 / 2.0))
ax.autoscale(False)         #You could skip this line and use scalex=False on
ax.plot(x1, 3 * np.sin(x1)) #the "theoretical" plot. It has to be last either way

fig.savefig('test.pdf')

请注意,如果您想控制它,您可以调整最后一个图的 zorder,使其绘制在“中间”。

如果您不想依赖顺序,并且只想指定要自动缩放的行列表,那么您可以执行以下操作:(注意:这是一个简化版本,假设您重新处理 Line2D 对象,而不是一般的 matplotlib 艺术家。)

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms

def main():
    fig, ax = plt.subplots()
    x1 = np.linspace(-1,1,100)

    line1, = ax.plot(x1, np.sin(x1))
    line2, = ax.plot(x1, 3 * np.sin(x1))
    line3, = ax.plot(x1, np.sin(x1 / 2.0))
    autoscale_based_on(ax, [line1, line3])

    plt.show()

def autoscale_based_on(ax, lines):
    ax.dataLim = mtransforms.Bbox.unit()
    for line in lines:
        xy = np.vstack(line.get_data()).T
        ax.dataLim.update_from_data_xy(xy, ignore=False)
    ax.autoscale_view()

if __name__ == '__main__':
    main()

【讨论】:

  • 哇!谢谢,这正是我要找的。唯一的问题是范围大于单位 bbox 的隐含假设。我通过在第一行添加update_from_data_xy 调用并在循环之前添加ignore = True 来解决它。
【解决方案2】:

使用 scalex/scaley kw 参数:

plot(x1, 3*sin(x1), scaley=False)

【讨论】:

  • 那里的文档相当具有误导性……这与您认为的不同。它会暂时关闭自动缩放,但下次绘图将根据 all 以前的绘图重新缩放。换句话说,它只有在最后一次调用 plot 时才会起作用。
  • 鉴于此,有没有更好的解决方案?关闭轴实例的自动缩放会起作用吗? ax.autoscale(enable=False, axis='both')
  • 嗯,这已经是对“忽略最后一个情节”问题的改进,谢谢您。但是,正如@Joe-Kington 所说,它并不能解决问题。我确实希望本着这种精神找到一个解决方案,或者也许与给 matplotlib 一个我希望它在重新缩放时考虑的艺术家列表有关。
  • 是的,现在乔提到它,我记得有完全相同的问题,我想我使用了手动预订限制。希望有更好的解决方案。
【解决方案3】:

LineCollection objects 可以通过使用autolim=False 参数来忽略:

from matplotlib.collections import LineCollection

fig, ax = plt.subplots()
x1 = np.linspace(-1,1,100)

# Will update limits
ax.plot(x1, np.sin(x1))

# Will not update limits
col = LineCollection([np.column_stack((x1, 3 * np.sin(x1)))], colors='g')
ax.add_collection(col, autolim=False)

# Will still update limits
ax.plot(x1, np.sin(x1 / 2.0))

【讨论】:

    【解决方案4】:

    无论绘图顺序如何,都可以通过创建另一个轴来完成此操作。

    在这个版本中,我们创建了一个双轴并禁用该双轴上的自动缩放。通过这种方式,绘图会根据原始坐标轴中绘制的任何内容进行缩放,但不会根据放置在双轴上的任何内容进行缩放。

    import numpy as np
    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots()
    x1 = np.linspace(-1,1,100)
    twin_ax = ax.twinx()  # Create a twin axes.
    twin_ax.autoscale(False)  # Turn off autoscaling on the twin axes.
    twin_ax.set_yticks([])  # Remove the extra tick numbers from the twin axis.
    
    ax.plot(x1, np.sin(x1))
    twin_ax.plot(x1, 3 * np.sin(x1), c='green')  # Plotting the thing we don't want to scale on in the twin axes.
    ax.plot(x1, np.sin(x1 / 2.0))
    
    twin_ax.set_ylim(ax.get_ylim())  # Make sure the y limits of the twin matches the autoscaled of the original.
    
    fig.savefig('test.pdf')
    

    注意,以上仅防止未缠绕的轴自动缩放(在上述情况下为 y)。为了让它对 x 和 y 都起作用,我们可以对 x 和 y 执行孪生过程(或从头开始创建新轴):

    import numpy as np
    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots()
    x1 = np.linspace(-1,1,100)
    x2 = np.linspace(-2,2,100)  # Would extend the x limits if auto scaled
    twin_ax = ax.twinx().twiny()  # Create a twin axes.
    twin_ax.autoscale(False)  # Turn off autoscaling on the twin axes.
    twin_ax.set_yticks([])  # Remove the extra tick numbers from the twin axis.
    twin_ax.set_xticks([])  # Remove the extra tick numbers from the twin axis.
    
    ax.plot(x1, np.sin(x1))
    twin_ax.plot(x2, 3 * np.sin(x2), c='green')  # Plotting the thing we don't want to scale on in the twin axes.
    ax.plot(x1, np.sin(x1 / 2.0))
    
    twin_ax.set_ylim(ax.get_ylim())  # Make sure the y limits of the twin matches the autoscaled of the original.
    twin_ax.set_xlim(ax.get_xlim())  # Make sure the x limits of the twin matches the autoscaled of the original.
    
    fig.savefig('test.png')
    

    【讨论】:

      【解决方案5】:

      作为jam's answer的概括,可以从matplotlib的任何绘图函数中获取一个集合对象,然后用autolim=False重新添加。例如,

      fig, ax = plt.subplots()
      x1 = np.linspace(-1,1,100)
      
      # Get hold of collection
      collection = ax.plot(x1, np.sin(x1))
      
      # Remove collection from the plot
      collection.remove()
      
      # Rescale
      ax.relim()
      
      # Add the collection without autoscaling
      ax.add_collection(collection, autolim=False)
      

      【讨论】:

      • 我得到TypeError: remove() takes exactly one argument (0 given) - 应该传递什么?
      • 我认为是这样,但限制仍然被所有地块缩放:collection[0].remove() ... ax.add_collection(collection[0], autolim=False)
      猜你喜欢
      • 1970-01-01
      • 2021-06-22
      • 1970-01-01
      • 2015-02-12
      • 2020-09-16
      • 2016-10-14
      • 1970-01-01
      • 1970-01-01
      • 2016-01-12
      相关资源
      最近更新 更多