Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overlapping bars in pandas plot are not perfectly centered over each other when they have different width

I am wondering if there is a way to center bars that overlap in a bar plot when using the plotting provided by pandas.

When using twiny, we get bars corresponding to the different plots to overlap, which is really practical. When they have the same width they will be centered perfectly over each other.

However when one changes the width of one of the sets, they won't center anymore, which makes the plot look messy as can be seen here:

Not aligned bars when changing width of the overlay bars

The first black bar of cat 1 and cat 2 is perfectly centered on the corresponding blue bars. However the next black bar is not centered on the orange bar, same with the third black bar which is not centered over the green bar.

As can be seen here they are perfectly centered when they have the same width. So how can they be centered in the same way when they don't have the same width?: enter image description here

This is the code that creates the current uncentered plot:

from io import StringIO
import pandas as pd
import matplotlib.pyplot as plt

txt = '''Category    COLUMN1         COLUMN2     COLUMN3    
A          0.5               3          Cat1   
B          0.3               5          Cat1 
C          0.7               4          Cat1
A          0.4               3          Cat2
B          0.8               5          Cat2
C          0.3               4          Cat2
'''

df = pd.read_table(StringIO(txt), sep="\s+")

order = ['Cat1', 'Cat2']

fig, ax = plt.subplots()
ax2 = ax.twiny()

col1 = pd.pivot_table(df,index='COLUMN3',columns='Category',values='COLUMN1').reindex(order)


col1.plot(kind='bar', ax=ax, edgecolor='Black', lw=1, color='black')

col2 = pd.pivot_table(df,index='COLUMN3',columns='Category',values='COLUMN2').reindex(order)
col2.plot(kind='bar', ax=ax2, legend=False, alpha=0.7)
ax2.xaxis.set_visible(False)

plt.show() 

EDIT

Here is a picture of what is desired, please excuse the "bad graphic editing".:

enter image description here

like image 529
BoroBorooooooooooooooooooooooo Avatar asked Dec 17 '25 18:12

BoroBorooooooooooooooooooooooo


1 Answers

First you need to have the bar positions and widths on the same scale. So you would probably want to create a twinx axes instead of twiny. (You can still use ax.set_ylim(ax2.get_ylim()) to get the y scale equal as well.)

Then, in case there are as many bars in the primary axes as in the twin axes, we have a one-to-one correspondance and can calculate the black bars' positions depending on the colored bars' positions.

for bbar, cbar in zip(ax.patches, ax2.patches):
    cpos = cbar.get_x()+cbar.get_width()/2.
    bpos = cpos-bbar.get_width()/2.
    bbar.set_x(bpos)

enter image description here

The complete code, producing the above plot:

from io import StringIO
import pandas as pd
import matplotlib.pyplot as plt

txt = u'''Category    COLUMN1         COLUMN2     COLUMN3    
A          0.5               3          Cat1   
B          0.3               5          Cat1 
C          0.7               4          Cat1
A          0.4               3          Cat2
B          0.8               5          Cat2
C          0.3               4          Cat2
'''

df = pd.read_table(StringIO(txt), sep="\s+")

order = ['Cat1', 'Cat2']

fig, ax = plt.subplots()
ax2 = ax.twinx()

col1 = pd.pivot_table(df,index='COLUMN3',columns='Category',values='COLUMN1').reindex(order)
col1.plot(kind='bar', ax=ax, edgecolor='Black', lw=1, color='black', width=0.2)
col2 = pd.pivot_table(df,index='COLUMN3',columns='Category',values='COLUMN2').reindex(order)
col2.plot(kind='bar', ax=ax2, legend=False, alpha=0.7)
ax2.xaxis.set_visible(False)
ax.set_ylim(ax2.get_ylim())

for bbar, cbar in zip(ax.patches, ax2.patches):
    cpos = cbar.get_x()+cbar.get_width()/2.
    bpos = cpos-bbar.get_width()/2.
    bbar.set_x(bpos)

plt.show() 
like image 118
ImportanceOfBeingErnest Avatar answered Dec 19 '25 06:12

ImportanceOfBeingErnest