0

I am trying to make a plot like this

enter image description here

using matplotlib.

Currently I have this plot:

enter image description here

which was generated using this code:

import matplotlib.pyplot as plt
import matplotlib as mpl
import pandas as pd

width = 0.4
mpl.rcParams.update({'font.size':15})
mpl.rcParams.update({'legend.columnspacing':0.5})

##############################################################
# BEGIN: Prepare data                                        #
##############################################################
energy_cm = 1550835.86856494
energy_fm = 1456129.29966378
energy_cm_trad = 1393026.50949191
energy_fm_trad = 1314814.95236864
energy_cm_hw = 1200000
energy_fm_hw = 1100000

data_energy = { 'Algorithm' : ['Algorithm 1', 'Algorithm 2'],
       'SW' : [energy_cm, energy_fm],
       'HW' : [energy_cm_hw, energy_fm_hw],
       'Trad' : [energy_cm_trad, energy_fm_trad]
    }

df_energy = pd.DataFrame(data_energy)

##############################################################
# END: Prepare data                                          #
##############################################################

##############################################################
# BEGIN: Split the bars into two halves                      #
##############################################################
fig, (ax, ax2) = plt.subplots(2, 1, sharex=True)

df_energy[['Algorithm', 'SW', 'Trad',
           'HW']].set_index('Algorithm').plot(kind='bar', legend=True,
                                              width=width, rot=0,
                                              ax=ax,
                                              color=('sandybrown','rosybrown',
                                                     'goldenrod','indianred','tomato','r'))

df_energy[['Algorithm', 'SW', 'Trad',
           'HW']].set_index('Algorithm').plot(kind='bar',
                                              legend=False,
                                              width=width, rot=0,
                                              ax=ax2,
                                              color=('sandybrown','rosybrown',
                                                     'goldenrod','indianred','tomato','r'))

max_lengths = sorted(df_energy.max(axis=0).values[1:])
min_lengths = sorted(df_energy.min(axis=0).values[1:])

# zoom-in / limit the view to different portions of the data
ax.set_ylim(max_lengths[-2] * 0.8, max_lengths[-1] * 1.1)  # outliers only
ax2.set_ylim(0, min_lengths[0] * 1.1)  # most of the data

# hide the spines between ax and ax2
ax.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax.xaxis.tick_top()
ax.tick_params(labeltop='off')  # don't put tick labels at the top
ax2.xaxis.tick_bottom()

d = .01  # how big to make the diagonal lines in axes coordinates
# arguments to pass to plot, just so we don't keep repeating them
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((-d, +d), (-d, +d), **kwargs)        # top-left diagonal
ax.plot((1 - d, 1 + d), (-d, +d), **kwargs)  # top-right diagonal

kwargs.update(transform=ax2.transAxes)  # switch to the bottom axes
ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs)  # bottom-left diagonal
ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs)  # bottom-right diagonal

##############################################################
# END: Split the bars into two halves                        #
##############################################################

##############################################################
# BEGIN: Labels                                              #
##############################################################
# Remove X lables from the upper half of the plot
ax.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
    top=False,         # ticks along the top edge are off
    labeltop=False,
    labelbottom=False) # labels along the bottom edge are off

ax.set_ylabel('Energy in nJ')
ax.set_xlabel("")
ax2.set_xlabel("")

##############################################################
# END: Labels                                                #
##############################################################

##############################################################
# BEGIN: Scaling                                             #
##############################################################
mf = mpl.ticker.ScalarFormatter(useMathText=True)
mf.set_powerlimits((-2,2))
ax.yaxis.set_major_formatter(mf)
ax2.yaxis.set_major_formatter(mf)

##############################################################
# END: Scaling                                               #
##############################################################

fig.tight_layout()
plt.show()

I am following matplotlibs example code for the broken axis and I am using code from the answer to my previous question in order to scale the numbers on the y-axis.

I would like to:

  • Remove the scaling label from the lower subplot
  • Reduce the distance between the upper and the lower sublot
  • Shift the y-label down so that it applies to both the upper and the lower subplots

Thus the result would look like the fist image in this question.

How can I achieve these three things? Any help is appreciated, even if it doesn't solve all three problems...

1 Answers1

1
  • use fig.subplots_adjust() to change the spacing between the two axes.
  • I replaced the ylabel with fig.text() to center the label vertically in the figure
  • use ax2.yaxis.get_offset_text().set_visible(False) to hide the exponent on the bottom axis.

Note that the limits of your axes are poorly chosen as the top of the HW bar in the "Count Min" category is visible in both the bottom and top axes.

complete code:

import matplotlib.pyplot as plt
import matplotlib as mpl
import pandas as pd

width = 0.4
mpl.rcParams.update({'font.size':15})
mpl.rcParams.update({'legend.columnspacing':0.5})


# BEGIN: Prepare data 
energy_cm = 1550835.86856494
energy_fm = 1456129.29966378
energy_cm_trad = 1393026.50949191
energy_fm_trad = 1314814.95236864
energy_cm_hw = 1200000
energy_fm_hw = 1100000

data_energy = { 'Algorithm' : ['Algorithm 1', 'Algorithm 2'],
       'SW' : [energy_cm, energy_fm],
       'HW' : [energy_cm_hw, energy_fm_hw],
       'Trad' : [energy_cm_trad, energy_fm_trad]
    }

df_energy = pd.DataFrame(data_energy)


# BEGIN: Split the bars into two halves
fig, (ax, ax2) = plt.subplots(2, 1, sharex=True)
kw = dict(width=width, rot=0,
          color=('sandybrown','rosybrown','goldenrod','indianred','tomato','r'))
df_energy[['Algorithm', 'SW', 'Trad',
           'HW']].set_index('Algorithm').plot(kind='bar', legend=True, ax=ax, **kw)

df_energy[['Algorithm', 'SW', 'Trad',
           'HW']].set_index('Algorithm').plot(kind='bar', legend=False, ax=ax2, **kw)

max_lengths = sorted(df_energy.max(axis=0).values[1:])
min_lengths = sorted(df_energy.min(axis=0).values[1:])

# zoom-in / limit the view to different portions of the data
ax.set_ylim(max_lengths[-2] * 0.8, max_lengths[-1] * 1.1)  # outliers only
ax2.set_ylim(0, min_lengths[0] * 1.1)  # most of the data

# hide the spines between ax and ax2
ax.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax.xaxis.tick_top()
ax.tick_params(labeltop='off')  # don't put tick labels at the top
ax2.xaxis.tick_bottom()

d = .01  # how big to make the diagonal lines in axes coordinates
# arguments to pass to plot, just so we don't keep repeating them
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((-d, +d), (-d, +d), **kwargs)        # top-left diagonal
ax.plot((1 - d, 1 + d), (-d, +d), **kwargs)  # top-right diagonal

kwargs.update(transform=ax2.transAxes)  # switch to the bottom axes
ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs)  # bottom-left diagonal
ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs)  # bottom-right diagonal



# BEGIN: Labels
# Remove X lables from the upper half of the plot
ax.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
    top=False,         # ticks along the top edge are off
    labeltop=False,
    labelbottom=False) # labels along the bottom edge are off

fig.text(0,0.5,'Energy in nJ', rotation=90, va='center', ha='left')
ax.set_xlabel("")
ax2.set_xlabel("")


# BEGIN: Scaling
ax.ticklabel_format(style='sci', axis='y', useOffset=True, scilimits=(0,0))
ax2.ticklabel_format(style='sci', axis='y', useOffset=True, scilimits=(0,0))
ax2.yaxis.get_offset_text().set_visible(False)


fig.tight_layout()
fig.subplots_adjust(hspace=0.05)
plt.show()

enter image description here

Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75