How to Invert the Y-Axis in Matplotlib (invert_yaxis, ylim, imshow, twinx)

To invert the y-axis in Matplotlib, call ax.invert_yaxis() on your axes, or plt.gca().invert_yaxis() if you use the pyplot interface. On an inverted y-axis the values grow downward instead of upward, so the smallest value is at the top. That’s what you want for rankings (1 at the top), depth, screen/pixel coordinates and horizontal bar charts that should list the first item on top. This guide covers every way to do it, including imshow() images, the x-axis, subplots and secondary y-axes.

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

Invert the y-axis with invert_yaxis()

import matplotlib.pyplot as plt

years = [2020, 2021, 2022, 2023, 2024]
sales = [12, 18, 15, 24, 30]

fig, (normal, inverted) = plt.subplots(1, 2, figsize=(9, 3.5))
normal.plot(years, sales, marker="o")
normal.set_title("Normal y-axis")

inverted.plot(years, sales, marker="o", color="tab:red")
inverted.invert_yaxis()                      # <- flip the y-axis
inverted.set_title("After ax.invert_yaxis()")
for ax in (normal, inverted):
    ax.set_xticks(years)                     # whole years on the x-axis

print("normal:   ylim =", *normal.get_ylim(), "| inverted:", normal.yaxis_inverted())
print("inverted: ylim =", *inverted.get_ylim(), "| inverted:", inverted.yaxis_inverted())
plt.tight_layout()
plt.show()

Output:

normal:   ylim = 11.1 30.9 | inverted: False
inverted: ylim = 30.9 11.1 | inverted: True
Matplotlib invert y axis: the same line plot with a normal y-axis on the left and an inverted y-axis after ax.invert_yaxis() on the right
Left: normal. Right: the same data after ax.invert_yaxis(), with 30 at the bottom.

get_ylim() now returns the limits top-value-first, and yaxis_inverted() tells you whether an axis is flipped. Both methods are listed in the Matplotlib API reference for Axes.invert_yaxis.

With pyplot (plt)

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [10, 20, 25, 40])
plt.gca().invert_yaxis()          # gca() = "get current axes"
print(*plt.ylim())                # the larger value comes first now

Output:

41.5 8.5

All the ways to invert or reverse the y-axis

import matplotlib.pyplot as plt

def fresh():
    fig, ax = plt.subplots()
    ax.plot([0, 1, 2], [0, 5, 10])
    return ax

ax = fresh(); ax.invert_yaxis()
print("invert_yaxis()            ", *ax.get_ylim())

ax = fresh(); ax.set_ylim(10.5, -0.5)          # top value first
print("set_ylim(top, bottom)     ", *ax.get_ylim())

ax = fresh(); ax.set_ylim(ax.get_ylim()[::-1])  # reverse whatever the limits are
print("set_ylim(get_ylim()[::-1])", *ax.get_ylim())

ax = fresh(); ax.yaxis.set_inverted(True)      # explicit: safe to call more than once
print("yaxis.set_inverted(True)  ", *ax.get_ylim())

plt.figure(); plt.plot([0, 1, 2], [0, 5, 10]); plt.ylim(10, 0)
print("plt.ylim(10, 0)           ", *plt.ylim())

Output:

invert_yaxis()             10.5 -0.5
set_ylim(top, bottom)      10.5 -0.5
set_ylim(get_ylim()[::-1]) 10.5 -0.5
yaxis.set_inverted(True)   10.5 -0.5
plt.ylim(10, 0)            10.0 0.0
MethodWhen to use it
ax.invert_yaxis()Simplest. Toggles: calling it twice flips back.
ax.yaxis.set_inverted(True)Explicit on/off, safe to call repeatedly.
ax.set_ylim(top, bottom) / plt.ylim(top, bottom)When you also want fixed limits.
ax.set_ylim(ax.get_ylim()[::-1])Reverse whatever the current limits are.

Common gotchas

invert_yaxis() toggles

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([0, 1, 2], [0, 5, 10])

ax.invert_yaxis()
print("after 1st invert_yaxis():", ax.yaxis_inverted())
ax.invert_yaxis()
print("after 2nd invert_yaxis():", ax.yaxis_inverted())   # flipped back!

ax.yaxis.set_inverted(True)
ax.yaxis.set_inverted(True)
print("after set_inverted(True) twice:", ax.yaxis_inverted())

Output:

after 1st invert_yaxis(): True
after 2nd invert_yaxis(): False
after set_inverted(True) twice: True

If a function might be called twice (for example when you redraw a figure), use ax.yaxis.set_inverted(True).

Later set_ylim() calls can undo it

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([0, 1, 2], [0, 5, 10])
ax.invert_yaxis()

ax.plot([0, 1, 2], [2, 12, 20])          # more data after inverting
print("after plotting more data: ylim =", *ax.get_ylim(), "| inverted:", ax.yaxis_inverted())

ax.set_ylim(0, 25)                       # limits given bottom-first
print("after set_ylim(0, 25):    ylim =", *ax.get_ylim(), "| inverted:", ax.yaxis_inverted())

Output:

after plotting more data: ylim = 21.0 -1.0 | inverted: True
after set_ylim(0, 25):    ylim = 0.0 25.0 | inverted: False

Adding data keeps the axis inverted, but set_ylim(0, 25) sets the direction too. Pass the limits top-first (set_ylim(25, 0)) or call set_ylim() first and invert_yaxis() after.

Invert the x-axis (or both axes)

The x-axis works the same way with invert_xaxis(), xaxis.set_inverted(True) or set_xlim(right, left):

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [1, 4, 9, 16, 25]

fig, axes = plt.subplots(1, 3, figsize=(10, 3))
titles = ["invert_xaxis()", "invert_yaxis()", "both"]
for ax, title in zip(axes, titles):
    ax.plot(x, y, marker="o")
    ax.set_title(title)

axes[0].invert_xaxis()
axes[1].invert_yaxis()
axes[2].invert_xaxis()
axes[2].invert_yaxis()

for ax in axes:
    print(f"{ax.get_title():15} x inverted: {ax.xaxis_inverted()!s:5}  y inverted: {ax.yaxis_inverted()}")
plt.tight_layout()
plt.show()

Output:

invert_xaxis()  x inverted: True   y inverted: False
invert_yaxis()  x inverted: False  y inverted: True
both            x inverted: True   y inverted: True
Matplotlib invert x axis, invert y axis and invert both axes shown side by side for the same curve
Left to right: x inverted, y inverted, both inverted.

Practical examples

Rankings: position 1 at the top

import matplotlib.pyplot as plt

weeks = range(1, 9)
song_a = [8, 5, 3, 2, 1, 1, 2, 4]      # chart position: 1 is best
song_b = [2, 1, 1, 3, 4, 6, 7, 9]

fig, ax = plt.subplots(figsize=(7, 3.8))
ax.plot(weeks, song_a, marker="o", label="Song A")
ax.plot(weeks, song_b, marker="s", label="Song B")
ax.invert_yaxis()                       # rank 1 at the top
ax.set_yticks(range(1, 10))
ax.set_xlabel("Week")
ax.set_ylabel("Chart position")
ax.legend()
ax.grid(alpha=0.3)
plt.show()
Matplotlib line chart with inverted y-axis so chart position 1 is at the top, comparing two songs over 8 weeks
Inverting puts rank 1 at the top, where readers expect it.

Depth profile

import matplotlib.pyplot as plt

depth_m = [0, 10, 20, 50, 100, 200, 500, 1000]
temperature_c = [26, 25.5, 24, 20, 15, 11, 7, 4.5]

fig, ax = plt.subplots(figsize=(4.5, 5))
ax.plot(temperature_c, depth_m, marker="o")
ax.invert_yaxis()                       # depth grows downward
ax.set_xlabel("Temperature (°C)")
ax.set_ylabel("Depth (m)")
ax.set_title("Ocean temperature profile")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()
Ocean temperature profile in Matplotlib with an inverted y-axis showing depth in metres increasing downward
Depth increases downward, like a real water column.

Horizontal bar chart: first category on top

barh() draws the first category at the bottom. Inverting the y-axis lists the categories in the order you gave them:

import matplotlib.pyplot as plt

languages = ["Python", "JavaScript", "Java", "C#", "Go"]
share = [29.9, 19.1, 16.3, 6.9, 4.1]

fig, (before, after) = plt.subplots(1, 2, figsize=(10, 3.2))
before.barh(languages, share)
before.set_title("barh() default: first item at the bottom")

after.barh(languages, share, color="tab:green")
after.invert_yaxis()                     # first item at the top
after.set_title("After invert_yaxis(): first item on top")
plt.tight_layout()
plt.show()
Matplotlib barh chart before and after invert_yaxis: the first category Python moves from the bottom to the top
Left: default order. Right: after invert_yaxis().

Invert the y-axis in imshow()

imshow() is the exception: it already draws images with an inverted y-axis, because row 0 of an image is the top row. Use origin="lower" (or invert_yaxis()) to put row 0 at the bottom, like a normal graph:

import matplotlib.pyplot as plt
import numpy as np

data = np.arange(12).reshape(3, 4)      # row 0 = [0, 1, 2, 3]

fig, axes = plt.subplots(1, 3, figsize=(11, 3))
axes[0].imshow(data)                         # default origin="upper"
axes[0].set_title('default (origin="upper")')
axes[1].imshow(data, origin="lower")         # row 0 at the bottom
axes[1].set_title('origin="lower"')
axes[2].imshow(data)
axes[2].invert_yaxis()                       # flip the default back
axes[2].set_title("imshow() + invert_yaxis()")

for ax in axes:
    for (r, c), v in np.ndenumerate(data):
        ax.text(c, r, v, ha="center", va="center", color="w")
    print(f"{ax.get_title():27} ylim = {ax.get_ylim()[0]:4}, {ax.get_ylim()[1]:4}   inverted: {ax.yaxis_inverted()}")
plt.tight_layout()
plt.show()

Output:

default (origin="upper")    ylim =  2.5, -0.5   inverted: True
origin="lower"              ylim = -0.5,  2.5   inverted: False
imshow() + invert_yaxis()   ylim = -0.5,  2.5   inverted: False
Matplotlib imshow invert y axis: default origin upper, origin lower, and imshow with invert_yaxis compared on a 3x4 array
The default puts row 0 on top; origin="lower" and invert_yaxis() both move it to the bottom.

Do the two options produce exactly the same picture? Rendering both and comparing the pixels:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

data = np.random.default_rng(0).random((20, 30))

def render(**kwargs):
    fig, ax = plt.subplots(figsize=(3, 2))
    ax.imshow(data, origin=kwargs.get("origin", "upper"))
    if kwargs.get("invert"):
        ax.invert_yaxis()
    ax.set_axis_off()
    fig.canvas.draw()
    return np.asarray(fig.canvas.buffer_rgba()).astype(int)

a, b = render(origin="lower"), render(invert=True)
diff = np.abs(a - b).max(axis=2) > 0
print(f"different pixels: {diff.sum()} of {diff.size} ({diff.mean():.2%})")
print("rows that differ:", *np.unique(np.nonzero(diff)[0]), "of", diff.shape[0])
print("default origin:", matplotlib.rcParams["image.origin"])

Output:

different pixels: 453 of 60000 (0.76%)
rows that differ: 62 139 of 200
default origin: upper

Almost: the image content is identical, but the top and bottom edge rows come out one pixel different because of rounding. Prefer origin="lower": it says what you mean and doesn’t depend on calling order. You can change the default for every image with plt.rcParams["image.origin"] = "lower". To label the axes with real coordinates instead of pixel indices, add extent:

import matplotlib.pyplot as plt
import numpy as np

data = np.arange(12).reshape(3, 4)
fig, ax = plt.subplots()
ax.imshow(data, origin="lower", extent=[0, 40, 0, 30])   # x from 0-40, y from 0-30
print("xlim:", *ax.get_xlim(), "| ylim:", *ax.get_ylim(), "| inverted:", ax.yaxis_inverted())

Output:

xlim: 0.0 40.0 | ylim: 0.0 30.0 | inverted: False

Invert the y-axis in subplots

import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True)
ax1.plot([1, 2, 3], [1, 4, 9])
ax2.plot([1, 2, 3], [9, 4, 1])

ax1.invert_yaxis()                              # shared y: both flip
print(ax1.yaxis_inverted(), ax2.yaxis_inverted())

fig, axes = plt.subplots(2, 2)                  # not shared: flip each one
for ax in axes.flat:
    ax.plot([1, 2, 3], [1, 4, 9])
    ax.invert_yaxis()
print(*[ax.yaxis_inverted() for ax in axes.flat])

Output:

True True
True True True True

With sharey=True the subplots share one y-axis, so inverting one inverts all of them. Otherwise, loop over axes.flat.

Invert a secondary y-axis

A second y-axis made with twinx() is independent, so you can flip just that one:

import matplotlib.pyplot as plt

altitude_km = [0, 2, 4, 6, 8, 10]
temperature_c = [15, 2, -11, -24, -37, -50]
pressure_hpa = [1013, 795, 616, 472, 356, 264]

fig, ax1 = plt.subplots(figsize=(7, 4))
ax1.plot(altitude_km, temperature_c, "o-", color="tab:red")
ax1.set_xlabel("Altitude (km)")
ax1.set_ylabel("Temperature (°C)", color="tab:red")

ax2 = ax1.twinx()                                # secondary y-axis on the right
ax2.plot(altitude_km, pressure_hpa, "s--", color="tab:blue")
ax2.set_ylabel("Pressure (hPa), inverted", color="tab:blue")
ax2.invert_yaxis()                               # only the right axis flips

print("left inverted:", ax1.yaxis_inverted(), "| right inverted:", ax2.yaxis_inverted())
plt.tight_layout()
plt.show()

Output:

left inverted: False | right inverted: True
Matplotlib invert secondary y axis: temperature on the left y-axis and inverted pressure on the right twinx y-axis versus altitude
Only the right-hand pressure axis is inverted.

A secondary_yaxis() is different: it is a converted view of the main axis, so it follows the main axis’s direction automatically:

import matplotlib.pyplot as plt

depth_m = [0, 50, 100, 200, 400]
oxygen = [7.8, 7.1, 5.9, 4.2, 3.5]

fig, ax = plt.subplots()
ax.plot(oxygen, depth_m, marker="o")
ax.invert_yaxis()
ax.set_ylabel("Depth (m)")

# a secondary axis showing the same depth in feet follows the main axis
feet = ax.secondary_yaxis("right", functions=(lambda m: m * 3.281, lambda ft: ft / 3.281))
feet.set_ylabel("Depth (ft)")
fig.canvas.draw()
print("main (m):      ", [round(v) for v in ax.get_ylim()], "inverted:", ax.yaxis_inverted())
print("secondary (ft):", [round(v) for v in feet.get_ylim()], "inverted:", feet.yaxis_inverted())

Output:

main (m):       [420, -20] inverted: True
secondary (ft): [1378, -66] inverted: True

More on second axes: Matplotlib secondary y-axis.

Log scale and colormaps

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [1, 10, 100, 1000])
ax.set_yscale("log")
ax.invert_yaxis()
print("ylim:", *[round(v, 2) for v in ax.get_ylim()], "| inverted:", ax.yaxis_inverted())

Output:

ylim: 1412.54 0.71 | inverted: True

A reversed colormap is a different thing from a reversed axis: add _r to its name.

import matplotlib.pyplot as plt

print("viridis   at 0.0:", [round(float(c), 3) for c in plt.get_cmap("viridis")(0.0)[:3]])
print("viridis_r at 1.0:", [round(float(c), 3) for c in plt.get_cmap("viridis_r")(1.0)[:3]])   # "_r" = reversed

Output:

viridis   at 0.0: [0.267, 0.005, 0.329]
viridis_r at 1.0: [0.267, 0.005, 0.329]

Continue with these Matplotlib axis guides:

Frequently asked questions

What does an inverted y-axis mean?

The values increase downward instead of upward: the smallest value is at the top of the plot and the largest at the bottom.

How do I invert the y-axis in Matplotlib?

Call ax.invert_yaxis(), or plt.gca().invert_yaxis() with pyplot. ax.set_ylim(top, bottom) also works.

Why did my y-axis flip back?

invert_yaxis() toggles, so a second call undoes the first, and a later set_ylim(bottom, top) sets the normal direction again. Use ax.yaxis.set_inverted(True) or call invert_yaxis() last.

How do I flip the y-axis in imshow?

Pass origin="lower" to imshow(). The default origin="upper" puts row 0 at the top.

How do I invert the x-axis?

ax.invert_xaxis() or plt.gca().invert_xaxis().

How do I check if an axis is inverted?

ax.yaxis_inverted() and ax.xaxis_inverted() return True or False.