Search code examples
pythonmatplotlibbar-chartsubplotsubfigure

How to render subfigures with even sizes


I have been trying to make a figure like the one below. enter image description here

It contains 5 barh subplots and I want to place the legend in the last slot. There is also an annotation line and a suptitle.

The issue that I am facing is that the subplots are not even in size. The second row subplots have a different height than the others. Also, the third row subplot (subplot 5) have a different width.

The code that I am using below is looking like this:

import numpy as np

import matplotlib
import matplotlib.pyplot as plt

fig = plt.figure(constrained_layout=True)

temps = ['1', '2', '3']
colour=['#ffffff','#29d639']

subfigs = fig.subfigures(nrows=3, ncols=1)
subfigs[1].suptitle('Open defects')


[ax14, ax9] = subfigs[0].subplots(nrows=1, ncols=2)


d14_4= [15000000,15000000,15000000]
b14 = ax14.barh(temps, d14_4,height= 1, linewidth=0.5,edgecolor='black', color=colour[0])
ax14.set_xscale('log')
ax14.set_xlim(1,15000000)
ax14.margins(x=0,y=0)


d9_4= [1500000,1500000,1500000]
b9_4 = ax9.barh(temps, d9_4,height= 1, linewidth=0.5,edgecolor='black', color=colour[0])
ax9.set_xscale('log')
ax9.set_xlim(1,1500000)
ax9.margins(x=0,y=0)

ax14.annotate('', xy=(0, -0.32), xycoords='axes fraction', xytext=(2.07, -0.32), arrowprops=dict(arrowstyle="-", linestyle='-', linewidth=1.5, color='grey'))


[ax1, ax8] = subfigs[1].subplots(nrows=1, ncols=2)

d1_5= [4000,4000,4000]
b1_5 = ax1.barh(temps, d1_5,height= 1, linewidth=0.5,edgecolor='black',color=colour[1])
ax1.set_xscale('log')
ax1.set_xlim(1,4000)
ax1.margins(x=0,y=0)

d8_3= [60000000, 60000000, 0]
b8_3 = ax8.barh(temps, d8_3,height= 1, linewidth=0.5,edgecolor='black', color=colour[1])
ax8.set_xscale('log')
ax8.set_xlim(1,60000000)
ax8.margins(x=0,y=0)

[ax15, ax18] = subfigs[2].subplots(nrows=1, ncols=2)

d15_5= [0, 0, 5000000]
b15 = ax15.barh(temps, d15_5,height= 1, linewidth=0.5,edgecolor='black',color=colour[1])
ax15.set_xscale('log')
ax15.set_xlim(1,5000000)
ax15.margins(x=0,y=0)

ax18.remove()

labels=["1","2"]
lines=[b15,b14]

leg=fig.legend(lines, labels, loc = (0.7, 0.035), ncol=3,frameon=False)

# plt.savefig('output1.pdf', format='pdf')
plt.show()

I tried to make the subplots even by changing the contrained layout parameters and the subplot_adjust() method but nothing worked.

It would really helpful if someone can guide me what could be done here. Thanks a lot

I tried changing the parameters of the constrained layout method and the subplot_adjust method but nothing worked.

When I change the contrained_layout to tight_layout and then manually adjust the layout sizes, the figure does not render correctly.


Solution

  • Here is a solution with plt.tight_layout, the part which I have changed, I added a comment.

    fig = plt.figure(constrained_layout=True)
    
    temps = ['1', '2', '3']
    colour=['#ffffff','#29d639']
    
    subfigs = fig.subfigures(nrows=3, ncols=1)
    subfigs[1].suptitle('Open defects')
    
    
    [ax14, ax9] = subfigs[0].subplots(nrows=1, ncols=2)
    
    
    d14_4= [15000000,15000000,15000000]
    b14 = ax14.barh(temps, d14_4,height= 1, linewidth=0.5,edgecolor='black', color=colour[0])
    ax14.set_xscale('log')
    ax14.set_xlim(1,15000000)
    ax14.margins(x=0,y=0)
    
    
    d9_4= [1500000,1500000,1500000]
    b9_4 = ax9.barh(temps, d9_4,height= 1, linewidth=0.5,edgecolor='black', color=colour[0])
    ax9.set_xscale('log')
    ax9.set_xlim(1,1500000)
    ax9.margins(x=0,y=0)
    
    # Change to xytext=(2.2, -0.32), because after tight_layout the layout is changed
    ax14.annotate('', xy=(0, -0.32), xycoords='axes fraction', xytext=(2.2, -0.32), arrowprops=dict(arrowstyle="-", linestyle='-', linewidth=1.5, color='grey'))
    
    
    [ax1, ax8] = subfigs[1].subplots(nrows=1, ncols=2)
    
    d1_5= [4000,4000,4000]
    b1_5 = ax1.barh(temps, d1_5,height= 1, linewidth=0.5,edgecolor='black',color=colour[1])
    ax1.set_xscale('log')
    ax1.set_xlim(1,4000)
    ax1.margins(x=0,y=0)
    
    d8_3= [60000000, 60000000, 0]
    b8_3 = ax8.barh(temps, d8_3,height= 1, linewidth=0.5,edgecolor='black', color=colour[1])
    ax8.set_xscale('log')
    ax8.set_xlim(1,60000000)
    ax8.margins(x=0,y=0)
    
    [ax15, ax18] = subfigs[2].subplots(nrows=1, ncols=2)
    
    d15_5= [0, 0, 5000000]
    b15 = ax15.barh(temps, d15_5,height= 1, linewidth=0.5,edgecolor='black',color=colour[1])
    ax15.set_xscale('log')
    ax15.set_xlim(1,5000000)
    ax15.margins(x=0,y=0)
    
    ax18.remove()
    
    # Add tight_layout 
    plt.tight_layout(rect=[0, 0.2, 1, 0.9])
    
    labels=["1","2"]
    lines=[b15,b14]
    
    # Change location of the legend
    leg=plt.legend(lines, labels, loc = (1.5, 0.035), ncol=3,frameon=False)
    
    plt.show()
    

    Which gives you the result:

    result