Bar chart with two y-axis matplotlib

Sometimes, as part of a quick exploratory data analysis, you may want to make a single plot containing two variables with different scales.

One of the options is to make a single plot with two different y-axis, such that the y-axis on the left is for one variable and the y-axis on the right is for the y-variable.

If you try to plot the two variables on a same plot without having two different y-axis, the plot would not really make sense.

If the variables have very different scales, you’ll want to make sure that you plot them in different twin Axes objects. These objects can share one axis (for example, the time, or x-axis) while not sharing the other (the y-axis).

To create a twin Axes object that shares the x-axis, we use the twinx method.

Let us import Pandas.

# import pandas
import pandas as pd

We will use gapminder data from Carpentries to make the plot with two different y-axis on the same plot.

# Carpentries link for gapminder data
data_url = 'http://bit.ly/2cLzoxH'
#load gapminder data from url as pandas dataframe
gapminder = pd.read_csv(data_url)
print(gapminder.head(3))

Let us subset gapminder data by using Pandas query() function to filter for rows with United States.

gapminder_us = gapminder[gapminder.country=="United States"]

We are interested in making a plot of how lifeExp & gdpPercap changes over the years. The variable on x-axis is year and on y-axis we are interested in lifeExp & gdpPercap.
Both lifeExp and gdpPercap have different ranges. lifeExp values are below 100 and gdpPercap values are in thousands.

Naively, let us plot both on the same plot with a single y-axis.

# create figure and axis objects with subplots()
fig,ax=plt.subplots()
ax.plot(gapminder_us.year, gapminder_us.lifeExp, marker="o")
ax.set_xlabel("year")
ax.set_ylabel("lifeExp")
ax.plot(gapminder_us.year, gapminder_us["gdpPercap"], marker="o")
plt.show()

We can immediately see that this is a bad idea. The line for lifeExp over years is flat and really low. We don’t see any variation in it because of the scale of gdpPercap values.

Bar chart with two y-axis matplotlib
Bar chart with two y-axis matplotlib
Plotting variables of different scale

One of the solutions is to make the plot with two different y-axes. The way to make a plot with two different y-axis is to use two different axes objects with the help of twinx() function.

We first create figure and axis objects and make a first plot. In this example, we plot year vs lifeExp. And we also set the x and y-axis labels by updating the axis object.

# create figure and axis objects with subplots()
fig,ax = plt.subplots()
# make a plot
ax.plot(gapminder_us.year,
        gapminder_us.lifeExp,
        color="red", 
        marker="o")
# set x-axis label
ax.set_xlabel("year", fontsize = 14)
# set y-axis label
ax.set_ylabel("lifeExp",
              color="red",
              fontsize=14)

Next we use twinx() function to create the second axis object “ax2”. Now we use the second axis object “ax2” to make plot of the second y-axis variable and update their labels.

# twin object for two different y-axis on the sample plot
ax2=ax.twinx()
# make a plot with different y-axis using second axis object
ax2.plot(gapminder_us.year, gapminder_us["gdpPercap"],color="blue",marker="o")
ax2.set_ylabel("gdpPercap",color="blue",fontsize=14)
plt.show()
# save the plot as a file
fig.savefig('two_different_y_axis_for_single_python_plot_with_twinx.jpg',
            format='jpeg',
            dpi=100,
            bbox_inches='tight')

Then we can display the plot with plt.show() as before.

Now we have what we wanted. A plot with with different y-axis made with twinx in matplotlib. This definitely help us understand the relationship of the two variables against another. We can see that both lifeExp and gdpPerCap have increased over the years.

Bar chart with two y-axis matplotlib
Bar chart with two y-axis matplotlib
Plot with two different y-axis with twinx in Python

Although a plot with two y-axis does help see the pattern, personally I feel this is bit cumbersome. A better solution to use the idea of “small multiples”, two subplots with same x-axis. We will see an example of that soon.

Sometimes we want a secondary axis on a plot, for instance to convert radians to degrees on the same plot. We can do this by making a child axes with only one axis visible via axes.Axes.secondary_xaxis and axes.Axes.secondary_yaxis. This secondary axis can have a different scale than the main axis by providing both a forward and an inverse conversion function in a tuple to the functions keyword argument:

import matplotlib.pyplot as plt
import numpy as np
import datetime
import matplotlib.dates as mdates
from matplotlib.ticker import AutoMinorLocator

fig, ax = plt.subplots(constrained_layout=True)
x = np.arange(0, 360, 1)
y = np.sin(2 * x * np.pi / 180)
ax.plot(x, y)
ax.set_xlabel('angle [degrees]')
ax.set_ylabel('signal')
ax.set_title('Sine wave')


def deg2rad(x):
    return x * np.pi / 180


def rad2deg(x):
    return x * 180 / np.pi


secax = ax.secondary_xaxis('top', functions=(deg2rad, rad2deg))
secax.set_xlabel('angle [rad]')
plt.show()

Bar chart with two y-axis matplotlib

Here is the case of converting from wavenumber to wavelength in a log-log scale.

Note

In this case, the xscale of the parent is logarithmic, so the child is made logarithmic as well.

fig, ax = plt.subplots(constrained_layout=True)
x = np.arange(0.02, 1, 0.02)
np.random.seed(19680801)
y = np.random.randn(len(x)) ** 2
ax.loglog(x, y)
ax.set_xlabel('f [Hz]')
ax.set_ylabel('PSD')
ax.set_title('Random spectrum')


def one_over(x):
    """Vectorized 1/x, treating x==0 manually"""
    x = np.array(x).astype(float)
    near_zero = np.isclose(x, 0)
    x[near_zero] = np.inf
    x[~near_zero] = 1 / x[~near_zero]
    return x


# the function "1/x" is its own inverse
inverse = one_over


secax = ax.secondary_xaxis('top', functions=(one_over, inverse))
secax.set_xlabel('period [s]')
plt.show()

Bar chart with two y-axis matplotlib

Sometime we want to relate the axes in a transform that is ad-hoc from the data, and is derived empirically. In that case we can set the forward and inverse transforms functions to be linear interpolations from the one data set to the other.

Note

In order to properly handle the data margins, the mapping functions (forward and inverse in this example) need to be defined beyond the nominal plot limits.

In the specific case of the numpy linear interpolation, numpy.interp, this condition can be arbitrarily enforced by providing optional keyword arguments left, right such that values outside the data range are mapped well outside the plot limits.

fig, ax = plt.subplots(constrained_layout=True)
xdata = np.arange(1, 11, 0.4)
ydata = np.random.randn(len(xdata))
ax.plot(xdata, ydata, label='Plotted data')

xold = np.arange(0, 11, 0.2)
# fake data set relating x coordinate to another data-derived coordinate.
# xnew must be monotonic, so we sort...
xnew = np.sort(10 * np.exp(-xold / 4) + np.random.randn(len(xold)) / 3)

ax.plot(xold[3:], xnew[3:], label='Transform data')
ax.set_xlabel('X [m]')
ax.legend()


def forward(x):
    return np.interp(x, xold, xnew)


def inverse(x):
    return np.interp(x, xnew, xold)


secax = ax.secondary_xaxis('top', functions=(forward, inverse))
secax.xaxis.set_minor_locator(AutoMinorLocator())
secax.set_xlabel('$X_{other}$')

plt.show()

Bar chart with two y-axis matplotlib

A final example translates np.datetime64 to yearday on the x axis and from Celsius to Fahrenheit on the y axis. Note the addition of a third y axis, and that it can be placed using a float for the location argument

How do I make a graph with two Y axis in Matplotlib?

The easiest way to create a Matplotlib plot with two y axes is to use the twinx() function.

How do you make a graph with two different Y axis in Python?

The way to make a plot with two different y-axis is to use two different axes objects with the help of twinx() function. We first create figure and axis objects and make a first plot. In this example, we plot year vs lifeExp. And we also set the x and y-axis labels by updating the axis object.

How to plot two y values in matplotlib?

How to plot single data with two Y-axes (two units) in Matplotlib....
Set the figure size and adjust the padding between and around the subplots..
Create speed and acceleration data points using numpy..
Add a subplot to the current figure..
Plot speed data points using plot() method..
Create a twin Axes sharing the X-axis..