14

I'm trying to draw on an existing axis without extending or modifying its limits.

For example:

import numpy as np
import matplotlib.pyplot as plt

xy = np.random.randn(100, 2)

plt.scatter(xy[:,0], xy[:,1])

Makes a fine plot with well-fitting axis limits.

However, when I try to draw a line on top of it:

xlim = plt.gca().get_xlim()
plt.plot(xlim, xlim, 'k--')

the axis limits are extended, presumably to create padding around the new data.

How can I draw a line without this padding?

4 Answers 4

12

Setting plt.autoscale(False) prevents autoscaling from happening.

import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt

xy = np.random.randn(100, 2)
# By default plots are autoscaled. 
plt.scatter(xy[:,0], xy[:,1])

#Turn autoscaling off
plt.autoscale(False)
xlim = plt.gca().get_xlim()
plt.plot(xlim, xlim, 'k--')

plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

10 Comments

This is the answer. Unfortunately tight=True appears to be "retroactive", messing up the scaling of the data that has already been plotted. Whereas enabled=False does not appear to undo the initial autoscaling.
No, autoscaling is not retroactive. It will concern everything that is plotted after setting it.
In that case there appears to be a bug in matplotlib. Look what happens if you write plt.autoscale(tight=True) instead of plt.autoscale(False) in your code. It actually changes the scaling around the scatter points.
Well, that's expected, right? Because enable is True by default. Maybe I'm not completely understanding what you are trying to achieve.
My point is that the behavior is inconsistent. One parameter is retroactive and the other is not. It appears that setting up initial scaling might be a special case.
|
4

You can use the autoscale property of Axes objects:

Per the documentation:

Axes.autoscale(enable=True, axis='both', tight=None)

Autoscale the axis view to the data (toggle).

Convenience method for simple axis view autoscaling. It turns autoscaling on or off, and then, if autoscaling for either axis is on, it performs the autoscaling on the specified axis or axes. Parameters:

enable : bool or None, optional
         True (default) turns autoscaling on, False turns it off. None leaves the autoscaling state unchanged.
axis : {'both', 'x', 'y'}, optional
       which axis to operate on; default is 'both'
tight: bool or None, optional
       If True, set view limits to data limits; if False, let the locator
       and margins expand the view limits; if None, use tight scaling
       if the only artist is an image, otherwise treat tight as False.
       The tight setting is retained for future autoscaling until it is
       explicitly changed.
fig, ax = plt.subplots()
ax.plot(np.random.normal(size=(100,)),np.random.normal(size=(100,)),'bo')
ax.autoscale(tight=True)
xlim = ax.get_xlim()
plt.plot(xlim, xlim, 'k--')

enter image description here

4 Comments

Will setting tight=True "retroactively" tighten the axis scaling? I like the initial non-tight autoscaling, but I want the autoscaling to become tight only for drawing the line.
It also looks like I can use Axes.autoscale_view here but I'm not sure
I just checked. tight=True appears to be retroactive, whereas enabled=False does not.
@shadowtalker setting only tight=True keeps the autoscaling enabled, and so on every new plot changes the limits to fit all data, while enabled=False disables scaling altogether, and so keeps the limits fixed.
4

One brute-force solution is to keep track of the axis limits before drawing, and reset them after.

Like so:

from contextlib import contextmanager

@contextmanager
def preserve_limits(ax=None):
    """ Plot without modifying axis limits """
    if ax is None:
        ax = plt.gca()

    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    try:
        yield ax
    finally:
        ax.set_xlim(xlim)
        ax.set_ylim(ylim)

Now compare

plt.scatter(xy[:,0], xy[:,1])

xlim = plt.gca().get_xlim()
plt.plot(xlim, xlim, 'k--')

with

plt.scatter(xy[:,0], xy[:,1])

with preserve_limits():
    xlim = plt.gca().get_xlim()
    plt.plot(xlim, xlim, 'k--')

Comments

1

If you set the x-axis limits separately, they won't be overwritten until you change them, regardless of what is plotted. to make it mesh with your code, try:

plt.xlim(xlim)

when you get xlim, it gets the current limits, but once you 'set' them, they're locked until you change them again. This works for the y-axis as well, if you want those to be fixed too (just swap 'x' for 'y' and add the code).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.