Use ax2 = ax1.twinx() to add an independent right-hand y-axis that shares the original plot’s x-axis. Plot each bar series on its own axes, offset their x positions so the bars sit side by side, and label both scales clearly.
Build a two-y-axis bar plot
This example uses Matplotlib’s object-oriented interface and ordinary Axes.bar calls. The sample places two measures at the same categories but gives each measure its own y scale.
import matplotlib.pyplot as plt
categories = ["A", "B", "C"]
left_values = [12, 18, 15]
right_values = [120, 90, 150]
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
x = range(len(categories))
width = 0.38
ax1.bar([i - width / 2 for i in x], left_values, width=width,
color="tab:blue", label="Left-scale measure")
ax2.bar([i + width / 2 for i in x], right_values, width=width,
color="tab:orange", label="Right-scale measure")
ax1.set_xticks(list(x), categories)
ax1.set_xlabel("Category")
ax1.set_ylabel("Left-scale measure", color="tab:blue")
ax1.tick_params(axis="y", labelcolor="tab:blue")
ax2.set_ylabel("Right-scale measure", color="tab:orange")
ax2.tick_params(axis="y", labelcolor="tab:orange")
fig.tight_layout()
plt.show()
What the code does
fig, ax1 = plt.subplots()creates the figure and first Axes. Its y-axis appears on the left.ax2 = ax1.twinx()creates a second Axes with an independent y scale on the right while sharing the x-axis.- Each
bar()call draws on its own Axes. The x positions are shifted in opposite directions by half the bar width, keeping bars for each category side by side rather than covering one another. - The y-axis labels and tick colors match their bar series, helping readers identify which scale goes with which bars.
fig.tight_layout()adjusts spacing to help keep labels, including the right-side label, inside the figure.
When two independent y-axes make sense
A twin axis is appropriate when the two measures are distinct and need different numeric ranges. The scales are independent: a bar’s visual height on the left cannot be compared numerically with a bar’s height on the right. Label each axis with the measure and its units, and make the relationship between the two datasets clear. Matplotlib’s guide to plots with different scales distinguishes independent scales from a secondary axis.
If the right-hand values are a known conversion of the left-hand quantity—for example, the same quantity expressed in another unit—use Matplotlib’s secondary-axis approach rather than presenting the converted values as an unrelated dataset. That communicates the mathematical relationship between the scales.
Recommended Free Tools
#1 Best Overall
Why the bars need separate x positions
Both Axes share the same x-axis locations. If you draw both bar series at the same category positions, one set can obscure the other. The example offsets the first series left and the second right around each category. This is a manual layout using the documented bar(x, height, width=...) interface: bar positions and widths are supplied explicitly. See the Axes.bar API.
If the series represent different categories, time points, or other x values rather than paired measures, do not imply a pairing through offsets unless that relationship is real. Make the x positions and labels reflect what the data actually mean.
Rank #2
Choose the right plotting approach
- Use
twinx()for two distinct measures that share the same x positions but require independent y scales. - Use a secondary axis when one scale is a known mathematical conversion of the other.
- Prefer a single scale or separate plots if independent axes would make the comparison difficult to interpret or could imply a relationship the data do not support.
When readers need the two y-axis tick marks to line up, Matplotlib’s twinx documentation notes that a LinearLocator can be used. The axes can otherwise use their own locators and formatters. See the Axes.twinx API.
Version and interaction notes
The standard Axes.bar method supports explicit positions and widths for manually grouped bars. The current Matplotlib 3.11.2 documentation also lists Axes.grouped_bar, added in 3.11, but marks it provisional. Check the documentation for your installed version before relying on that newer API: Axes.grouped_bar API.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →For interactive plots, pick events with twin axes are delivered only for artists in the top-most Axes, as noted in the Matplotlib 3.9.2 Axes.twinx documentation. This can affect which plotted bars respond to picking.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Adding another y-axis
Matplotlib’s multiple-y-axis spine example shows how to create an additional twin Axes, hide or reposition spines, and reserve more space at the figure’s right edge. A third scale adds visual complexity, so use it only when its labels and relationship to the plotted data remain easy to follow. Matplotlib’s parasite-axis demonstration also describes the standard Axes-and-spines approach as preferable to its parasite-axis approach.
Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




