Multiple Y-axes with Spines on Multiple Bar Plot

I have some data that I would like to represent on a multiple Y-axes bar plot. Currently, I am only able to represent them on a line plot as shown in fig 1.

Fig 1: Current Line Plot

Below is my code:

import matplotlib.pyplot as plt import numpy as np import pandas as pd  def make_patch_spines_invisible(ax):     ax.set_frame_on(True)     ax.patch.set_visible(False)     for sp in ax.spines.values():         sp.set_visible(False)  dataset = pd.read_csv('Model Selection (Humidity)_csv.csv')  feature1 = dataset.iloc[:5, 2].values feature2 = dataset.iloc[:5, 3].values feature3 = dataset.iloc[:5, 4].values feature4 = dataset.iloc[:5, 5].values xaxis = dataset.iloc[:5,1].values  fig, f1 = plt.subplots(figsize= (25,15)) fig.subplots_adjust(right=0.75)  f2 = f1.twinx() f3 = f1.twinx() f4 = f1.twinx()  # Offset the right spine of par2.  The ticks and label have already been # placed on the right by twinx above. f3.spines["right"].set_position(("axes", 1.1)) f4.spines["left"].set_position(("axes", -0.1))  # Having been created by twinx, par2 has its frame off, so the line of its # detached spine is invisible.  First, activate the frame but make the patch # and spines invisible. make_patch_spines_invisible(f3) make_patch_spines_invisible(f4)  # Second, show the right spine. f3.spines["right"].set_visible(True) f4.spines["left"].set_visible(True) f4.yaxis.set_label_position('left') f4.yaxis.set_ticks_position('left')  p1, = f1.plot(xaxis, feature1, 'r-', label="Adjusted R2") p2, = f2.plot(xaxis, feature2, 'g-', label="Max Absolute Error") p3, = f3.plot(xaxis, feature3, 'b-', label="Max Error") p4, = f4.plot(xaxis, feature4, 'y-', label="Root Mean Square Error")  f1.set_ylim(0, 1) f2.set_ylim(0, 2) f3.set_ylim(7, 25) f4.set_ylim(1, 3)  f1.set_xlabel("Model") f1.set_ylabel("Adjusted R2") f2.set_ylabel("Max Absolute Error") f3.set_ylabel("Max Error") f4.set_ylabel("Root Mean Square Error")  f1.yaxis.label.set_color(p1.get_color()) f2.yaxis.label.set_color(p2.get_color()) f3.yaxis.label.set_color(p3.get_color()) f4.yaxis.label.set_color(p4.get_color())  tkw = dict(size=4, width=1.5) f1.tick_params(axis='y', colors=p1.get_color(), **tkw) f2.tick_params(axis='y', colors=p2.get_color(), **tkw) f3.tick_params(axis='y', colors=p3.get_color(), **tkw) f4.tick_params(axis='y', colors=p4.get_color(), **tkw) f1.tick_params(axis='x', **tkw)  lines = [p1, p2, p3, p4]  f1.legend(lines, [l.get_label() for l in lines])  plt.show() 

I would like to achieve something similar to Figure 2 below, but with multiple Y-axes each corresponding to their respective colored bars. Appreciate any help that I can get. Thanks!

Fig 2: Example of Multiple Bar Plot

Add Comment
1 Answer(s)
  • The issue stems from the way the matplotlib api returns different objects depending on the type of plot (e.g. plot & bar have different returns).
  • The line data was probably plotted using Multiple Yaxis With Spines, which doesn’t work with bar
  • I offer you two options:
    1. Plot the bar with one y-axis and then set it to log scale to compensate for the variation in the value range
    2. Plot the bar using a secondary y-axis and set one it to a log scale.
  • Leave the data in the dataframe to make plotting easier.

Setup dataframe

import pandas as pd import matplotlib.pyplot as plt import numpy as np  # test data np.random.seed(10) rows = 5 feature1 = np.random.randint(10, size=(rows,)) / 10 feature2 = np.random.randint(20, size=(rows,)) / 10 feature3 = np.random.randint(8, 25, size=(rows,)) feature4 = np.random.randint(1, 3, size=(rows,)) xaxis = range(rows)  # create dataframe df = pd.DataFrame({'Adj. R2': feature1, 'Max Abs. Error': feature2, 'Max Error': feature3, 'RMS Error': feature4},                   index=['SVR', 'DTR', 'RFR', 'PR', 'MLR'])  # display(df)      Adj. R2  Max Abs. Error  Max Error  RMS Error SVR      0.9             1.6         18          2 DTR      0.4             1.7         16          2 RFR      0.0             0.8         12          1 PR       0.1             0.9         24          1 MLR      0.9             0.0         12          2 

Plotting

secondary_y

ax = df.plot(secondary_y=['Max Error', 'RMS Error'], kind='bar') ax.right_ax.set_yscale('log') plt.show() 

enter image description here

single y-axis

df.plot(kind='bar', logy=True) plt.show() 

enter image description here

Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.