multiple axis in matplotlib with different scales

Question:

How can multiple scales can be implemented in Matplotlib? I am not talking about the primary and secondary axis plotted against the same x-axis, but something like many trends which have different scales plotted in same y-axis and that can be identified by their colors.

For example, if I have trend1 ([0,1,2,3,4]) and trend2 ([5000,6000,7000,8000,9000]) to be plotted against time and want the two trends to be of different colors and in Y-axis, different scales, how can I accomplish this with Matplotlib?

When I looked into Matplotlib, they say that they don’t have this for now though it is definitely on their wishlist, Is there a way around to make this happen?

Are there any other plotting tools for python that can make this happen?

Asked By: Jack_of_All_Trades

||

Answers:

If I understand the question, you may interested in this example in the Matplotlib gallery.

enter image description here

Yann’s comment above provides a similar example.


Edit – Link above fixed. Corresponding code copied from the Matplotlib gallery:

from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt

host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(right=0.75)

par1 = host.twinx()
par2 = host.twinx()

offset = 60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="right", axes=par2,
                                        offset=(offset, 0))

par2.axis["right"].toggle(all=True)

host.set_xlim(0, 2)
host.set_ylim(0, 2)

host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")

p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")

par1.set_ylim(0, 4)
par2.set_ylim(1, 65)

host.legend()

host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())

plt.draw()
plt.show()

#plt.savefig("Test")
Answered By: Steve Tjoa

if you want to do very quick plots with secondary Y-Axis then there is much easier way using Pandas wrapper function and just 2 lines of code. Just plot your first column then plot the second but with parameter secondary_y=True, like this:

df.A.plot(label="Points", legend=True)
df.B.plot(secondary_y=True, label="Comments", legend=True)

This would look something like below:

enter image description here

You can do few more things as well. Take a look at Pandas plotting doc.

Answered By: Shital Shah

Since Steve Tjoa’s answer always pops up first and mostly lonely when I search for multiple y-axes at Google, I decided to add a slightly modified version of his answer. This is the approach from this matplotlib example.

Reasons:

  • His modules sometimes fail for me in unknown circumstances and cryptic intern errors.
  • I don’t like to load exotic modules I don’t know (mpl_toolkits.axisartist, mpl_toolkits.axes_grid1).
  • The code below contains more explicit commands of problems people often stumble over (like single legend for multiple axes, using viridis, …) rather than implicit behavior.

Plot

import matplotlib.pyplot as plt 

# Create figure and subplot manually
# fig = plt.figure()
# host = fig.add_subplot(111)

# More versatile wrapper
fig, host = plt.subplots(figsize=(8,5), layout='constrained') # (width, height) in inches
# (see https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots.html and
# .. https://matplotlib.org/stable/tutorials/intermediate/constrainedlayout_guide.html)
    
ax2 = host.twinx()
ax3 = host.twinx()
    
host.set_xlim(0, 2)
host.set_ylim(0, 2)
ax2.set_ylim(0, 4)
ax3.set_ylim(1, 65)
    
host.set_xlabel("Distance")
host.set_ylabel("Density")
ax2.set_ylabel("Temperature")
ax3.set_ylabel("Velocity")

color1, color2, color3 = plt.cm.viridis([0, .5, .9])

p1 = host.plot([0, 1, 2], [0, 1, 2],    color=color1, label="Density")
p2 = ax2.plot( [0, 1, 2], [0, 3, 2],    color=color2, label="Temperature")
p3 = ax3.plot( [0, 1, 2], [50, 30, 15], color=color3, label="Velocity")

host.legend(handles=p1+p2+p3, loc='best')

# right, left, top, bottom
ax3.spines['right'].set_position(('outward', 60))

# no x-ticks                 
host.xaxis.set_ticks([])

# Alternatively (more verbose):
# host.tick_params(
#     axis='x',          # changes apply to the x-axis
#     which='both',      # both major and minor ticks are affected
#     bottom=False,      # ticks along the bottom edge are off)
#     labelbottom=False) # labels along the bottom edge are off
# sometimes handy:  direction='in'    

# Move "Velocity"-axis to the left
# ax3.spines['left'].set_position(('outward', 60))
# ax3.spines['left'].set_visible(True)
# ax3.spines['right'].set_visible(False)
# ax3.yaxis.set_label_position('left')
# ax3.yaxis.set_ticks_position('left')

host.yaxis.label.set_color(p1[0].get_color())
ax2.yaxis.label.set_color(p2[0].get_color())
ax3.yaxis.label.set_color(p3[0].get_color())

# For professional typesetting, e.g. LaTeX, use .pgf or .pdf
# For raster graphics use the dpi argument. E.g. '[...].png", dpi=300)'
plt.savefig("pyplot_multiple_y-axis.pdf", bbox_inches='tight')
# bbox_inches='tight': Try to strip excess whitespace
# https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.savefig.html
Answered By: Suuuehgi
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.