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
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
| Method | When 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
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()
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()
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()
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
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
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.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn