How to Set the Axis Range in Matplotlib (ylim, xlim, set_ylim)

To set the axis range in Matplotlib, use plt.ylim(bottom, top) and plt.xlim(left, right), or ax.set_ylim() and ax.set_xlim() when you work with an Axes object. ax.axis([xmin, xmax, ymin, ymax]) sets both at once. This guide shows each method with its output, plus how to set only one limit, keep subplots on the same range, and set ranges for dates, log scales, imshow() images, secondary y-axes and pandas plots.

Every example was run with Python 3.12.5, Matplotlib 3.11.2, NumPy 2.5.3 and pandas 3.0.6; the printed output and the charts come from those runs.

Set the y-axis range

import matplotlib.pyplot as plt

months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
score = [71, 74, 73, 77, 79, 78]

fig, (auto, fixed) = plt.subplots(1, 2, figsize=(9, 3.5))
auto.plot(months, score, marker="o")
auto.set_title("Automatic y-axis range")

fixed.plot(months, score, marker="o", color="tab:green")
fixed.set_ylim(0, 100)                     # <- set the y-axis range
fixed.set_title("After set_ylim(0, 100)")

print("automatic:", *auto.get_ylim())
print("fixed:    ", *fixed.get_ylim())
plt.tight_layout()
plt.show()

Output:

automatic: 70.6 79.4
fixed:     0.0 100.0
Matplotlib set y axis range: the same line chart with the automatic y-axis range on the left and set_ylim(0, 100) on the right
Left: Matplotlib picks the range from the data. Right: set_ylim(0, 100).

A fixed range is useful when you compare several charts, or when an automatic range exaggerates small changes (71 to 79 looks huge on the left). All parameters are in the Matplotlib reference for Axes.set_ylim.

Set the x-axis and y-axis range with pyplot

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [10, 25, 18, 30])
plt.ylim(0, 40)           # y-axis from 0 to 40
plt.xlim(0, 5)            # x-axis from 0 to 5
print("ylim:", *plt.ylim(), "| xlim:", *plt.xlim())

Output:

ylim: 0.0 40.0 | xlim: 0.0 5.0

Called without arguments, plt.ylim() and plt.xlim() return the current range. More x-axis examples: Matplotlib xlim.

Set both axes at once with axis()

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [10, 25, 18, 30])
ax.axis([0, 5, 0, 40])                 # [xmin, xmax, ymin, ymax] in one call
print("axis():", *ax.axis())
print("get_xlim():", *ax.get_xlim(), "| get_ylim():", *ax.get_ylim())

Output:

axis(): 0.0 5.0 0.0 40.0
get_xlim(): 0.0 5.0 | get_ylim(): 0.0 40.0
FunctionSetsReads
plt.ylim(b, t) / ax.set_ylim(b, t)y rangeplt.ylim() / ax.get_ylim()
plt.xlim(l, r) / ax.set_xlim(l, r)x rangeplt.xlim() / ax.get_xlim()
plt.axis([l, r, b, t]) / ax.axis(...)bothax.axis()
ax.set(xlim=..., ylim=...)both (with other properties)–

Set only the lower (or upper) limit

Pass only bottom or top (left or right for x) and Matplotlib keeps the other side as it was:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [12, 25, 18, 30])
print("automatic:        ", *ax.get_ylim())

ax.set_ylim(bottom=0)                 # only the lower limit
print("set_ylim(bottom=0):", *ax.get_ylim())

ax.set_ylim(top=50)                   # only the upper limit
print("set_ylim(top=50):  ", *ax.get_ylim())

ax.set_xlim(left=0)                   # same idea for x: left / right
print("set_xlim(left=0):  ", *ax.get_xlim())

Output:

automatic:         11.1 30.9
set_ylim(bottom=0): 0.0 30.9
set_ylim(top=50):   0.0 50.0
set_xlim(left=0):   0.0 4.15

set_ylim(bottom=0) is the usual way to make a bar or line chart start at zero.

Fixed range vs. autoscaling

Setting a range switches autoscaling off for that axis, so data you add later can fall outside the visible range:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([0, 1, 2], [0, 5, 10])
ax.set_ylim(0, 12)
print("autoscale on?", ax.get_autoscaley_on())

ax.plot([0, 1, 2], [0, 20, 40])        # new data goes above the limit
print("after new data:", *ax.get_ylim())  # the range stays fixed

ax.autoscale(axis="y")                 # let Matplotlib fit the data again
print("after autoscale():", *ax.get_ylim())

Output:

autoscale on? False
after new data: 0.0 12.0
after autoscale(): -2.0 42.0

If you only want some space around the data rather than exact numbers, use margins():

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([0, 10], [0, 100])
print("default margins:", *ax.get_ylim())

ax.margins(y=0.2)                      # 20% padding instead of fixed numbers
print("margins(y=0.2):  ", *ax.get_ylim())

ax.margins(y=0)                        # no padding: the line touches the edges
print("margins(y=0):    ", *ax.get_ylim())

Output:

default margins: -5.0 105.0
margins(y=0.2):   -20.0 120.0
margins(y=0):     0.0 100.0

Same axis range for all subplots

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
fig, axes = plt.subplots(1, 3, figsize=(11, 3), sharey=True)   # one shared y-range
for ax, k in zip(axes, [1, 2, 3]):
    ax.plot(x, k * np.sin(x))
    ax.set_title(f"{k} * sin(x)")
axes[0].set_ylim(-4, 4)                # setting one sets all (sharey=True)
for ax in axes:
    print(ax.get_title(), *ax.get_ylim())
plt.tight_layout()
plt.show()

Output:

1 * sin(x) -4.0 4.0
2 * sin(x) -4.0 4.0
3 * sin(x) -4.0 4.0
Matplotlib subplots with the same y-axis range using sharey=True and set_ylim(-4, 4) for three sine curves
With sharey=True, one set_ylim() call applies to every subplot.

For a different range per subplot, loop over the axes:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 2)
ranges = [(0, 10), (0, 100), (-1, 1), (50, 60)]
for ax, (low, high) in zip(axes.flat, ranges):
    ax.plot([0, 1], [low, high])
    ax.set_ylim(low, high)
for ax in axes.flat:
    print(*ax.get_ylim())

Output:

0.0 10.0
0.0 100.0
-1.0 1.0
50.0 60.0

Axis range with dates

Pass datetime or date objects directly to set_xlim():

from datetime import date
import matplotlib.pyplot as plt

days = [date(2026, 9, d) for d in range(1, 31)]
visits = [100 + (d * 7) % 45 for d in range(1, 31)]

fig, ax = plt.subplots()
ax.plot(days, visits)
ax.set_xlim(date(2026, 9, 10), date(2026, 9, 20))   # zoom to 10-20 September
low, high = ax.get_xlim()                           # numbers: days since 1970-01-01
import matplotlib.dates as mdates
print(mdates.num2date(low).date(), "to", mdates.num2date(high).date())

Output:

2026-09-10 to 2026-09-20

Axis range on a log scale

import warnings
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 10, 100, 1000], [1, 50, 2500, 90000])
ax.set_yscale("log")

ax.set_ylim(1, 1e6)
print("log range:", *ax.get_ylim())

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    ax.set_ylim(0, 1e6)                           # 0 is impossible on a log axis
print("after set_ylim(0, 1e6):", *ax.get_ylim())
print("warning:", caught[0].message)

Output:

log range: 1.0 1000000.0
after set_ylim(0, 1e6): 1.0 1000000.0
warning: Attempt to set non-positive ylim on a log-scaled axis will be ignored.

On a log axis, both limits must be positive; Matplotlib ignores a limit of 0 and warns.

Set the axis range of an imshow() image

By default imshow() uses pixel coordinates. extent=[left, right, bottom, top] maps the image to data coordinates, and set_xlim() / set_ylim() then zoom in on part of it:

import matplotlib.pyplot as plt
import numpy as np

y, x = np.mgrid[0:100, 0:150]
image = np.sin(x / 12) * np.cos(y / 9)

fig, axes = plt.subplots(1, 3, figsize=(12, 3.2))
axes[0].imshow(image)
axes[0].set_title("pixel coordinates")

axes[1].imshow(image, extent=[0, 15, 0, 10])      # data coordinates: x 0-15, y 0-10
axes[1].set_title("extent=[0, 15, 0, 10]")

axes[2].imshow(image, extent=[0, 15, 0, 10])
axes[2].set_xlim(5, 10)                            # zoom in on part of the image
axes[2].set_ylim(2, 6)
axes[2].set_title("extent + set_xlim/set_ylim (zoom)")

for ax in axes:
    print(f"{ax.get_title():36} x:", *ax.get_xlim(), " y:", *ax.get_ylim())
plt.tight_layout()
plt.show()

Output:

pixel coordinates                    x: -0.5 149.5  y: 99.5 -0.5
extent=[0, 15, 0, 10]                x: 0.0 15.0  y: 0.0 10.0
extent + set_xlim/set_ylim (zoom)    x: 5.0 10.0  y: 2.0 6.0
Matplotlib imshow set axis range: pixel coordinates, extent=[0, 15, 0, 10], and a zoomed view with set_xlim and set_ylim
Left: pixel coordinates. Middle: extent. Right: extent plus set_xlim(5, 10) and set_ylim(2, 6).

Note the y-range of the first image: imshow() draws row 0 at the top, so its y-axis is inverted. See invert the y-axis in Matplotlib for origin="lower".

Set the range of a secondary y-axis

An axis created with twinx() has its own range:

import matplotlib.pyplot as plt

months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
revenue = [120, 135, 150, 160, 172, 190]          # thousand $
margin = [8.5, 9.1, 9.8, 10.2, 10.1, 11.0]        # percent

fig, ax1 = plt.subplots(figsize=(7, 4))
ax1.bar(months, revenue, color="tab:blue", alpha=0.7)
ax1.set_ylabel("Revenue (k$)", color="tab:blue")
ax1.set_ylim(0, 250)                               # left axis range

ax2 = ax1.twinx()                                  # secondary y-axis
ax2.plot(months, margin, "o-", color="tab:red")
ax2.set_ylabel("Margin (%)", color="tab:red")
ax2.set_ylim(0, 15)                                # right axis range, independent

print("left:", *ax1.get_ylim(), "| right:", *ax2.get_ylim())
plt.tight_layout()
plt.show()

Output:

left: 0.0 250.0 | right: 0.0 15.0
Matplotlib secondary y axis range with twinx: revenue bars on the left axis from 0 to 250 and margin line on the right axis from 0 to 15
Left axis 0–250, right axis 0–15, set independently.

A secondary_yaxis() is a converted view of the main axis. You don’t set its range; it follows the main axis through the conversion functions:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([0, 1, 2, 3], [0, 20, 30, 40])            # degrees Celsius
ax.set_ylim(0, 50)

fahrenheit = ax.secondary_yaxis("right", functions=(lambda c: c * 9 / 5 + 32, lambda f: (f - 32) * 5 / 9))
fig.canvas.draw()
print("Celsius range:   ", *ax.get_ylim())
print("Fahrenheit range:", *[round(v, 1) for v in fahrenheit.get_ylim()])

Output:

Celsius range:    0.0 50.0
Fahrenheit range: 32.0 122.0

Set the axis range in a pandas plot

import matplotlib
matplotlib.use("Agg")
import pandas as pd

df = pd.DataFrame({"temp": [18, 21, 24, 23, 19]}, index=["Mon", "Tue", "Wed", "Thu", "Fri"])
ax = df.plot(ylim=(0, 30))               # pandas passes ylim to Matplotlib
print(*ax.get_ylim())

ax = df.plot()
ax.set_ylim(10, 30)                      # or set it on the returned Axes
print(*ax.get_ylim())

Output:

0.0 30.0
10.0 30.0

Common mistakes

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([0, 10], [0, 100])

ax.set_ylim(40)                  # one number = the bottom limit only
print("set_ylim(40):   ", *ax.get_ylim())

ax.set_ylim(100, 0)              # top first flips the axis
print("set_ylim(100, 0):", *ax.get_ylim(), "inverted:", ax.yaxis_inverted())

fig, ax = plt.subplots()
ax.set_ylim(0, 5)                # limits set BEFORE plotting...
ax.plot([0, 10], [0, 100])
print("set before plot:", *ax.get_ylim())   # ...are kept, even if data is outside

Output:

set_ylim(40):    40.0 105.0
set_ylim(100, 0): 100.0 0.0 inverted: True
set before plot: 0.0 5.0

A single number sets only the bottom limit. Passing the larger number first flips the axis. Limits set before plotting stay fixed, so the data can be cut off.

More Matplotlib axis tutorials:

Frequently asked questions

How do I set the y-axis range in Matplotlib?

plt.ylim(bottom, top) with pyplot, or ax.set_ylim(bottom, top) on an Axes, for example ax.set_ylim(0, 100).

How do I set the x-axis and y-axis range together?

plt.axis([xmin, xmax, ymin, ymax]), or ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax)).

How do I set only the lower limit of an axis?

ax.set_ylim(bottom=0) (or left= for the x-axis). The other limit stays automatic.

How do I get the current axis range?

ax.get_ylim() and ax.get_xlim(), or plt.ylim() / plt.xlim() without arguments.

Why doesn’t my axis range change when I add more data?

Setting limits turns autoscaling off. Call ax.autoscale() to fit the data again, or set the limits after plotting everything.

How do I give all subplots the same y-axis range?

Create them with plt.subplots(..., sharey=True) and call set_ylim() once, or loop over axes.flat.