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:

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?:

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".:

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)

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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With