1

I saw variations of this question asked several times, but I don't think any of the variations I saw fixes it (other than "use matplotlib for combo-plots", but I'd appreciate help understanding why should I do that).

df1 = pd.DataFrame({'height': {0: 161, 1: 173, 2: 168, 3: 185, 4: 163},
                    'year': {0: 2015, 1: 2016, 2: 2017, 3: 2018, 4: 2019}})
df2 = pd.DataFrame({'year': {0: 2015, 1: 2015, 2: 2016, 3: 2016, 4: 2017,
                             5: 2017, 6: 2018, 7: 2018, 8: 2019, 9: 2019},
                    'weight': {0: 64, 1: 81, 2: 82, 3: 83, 4: 66,
                               5: 71, 6: 84, 7: 91, 8: 99, 9: 94},
                    'sex': {0: 'M', 1: 'F', 2: 'M', 3: 'F', 4: 'M',
                            5: 'F', 6: 'M', 7: 'F', 8: 'M', 9: 'F'}})
ax = sns.barplot(x='year', y='weight', hue='sex', data=df2)
ax2 = ax.twinx()
sns.lineplot(x='year', y='height', data=df1, ax=ax2)

I expected this to be a textbook example of a comboplot, but the result is: broken combo chart

Why is that? Shouldn't the X axes simply converge and make a nice plot? Of course, each plot renders fine individually. working chart1 working chart2

Yaniv Aknin
  • 4,103
  • 3
  • 23
  • 29
  • 1
    Does this answer your question? [Seaborn lineplot and barplot don't align in the X axis](https://stackoverflow.com/questions/64402358/seaborn-lineplot-and-barplot-dont-align-in-the-x-axis) – dm2 May 04 '21 at 22:06
  • 1
    Yes, I must've missed (or didn't understand) it, until @tdy really showed the solution. Many thanks to both! – Yaniv Aknin May 04 '21 at 22:37

1 Answers1

2

If you plot them separately and check their xlim, you can see seaborn shifts the bar plot's x values down to 0 (the years are displayed separately via xticklabels):

ax = sns.barplot(x='year', y='weight', hue='sex', data=df2)
print(ax.get_xlim())
print(ax.get_xticklabels())

# (-0.5, 4.5)
# [Text(0, 0, '2015'), Text(1, 0, '2016'), Text(2, 0, '2017'), Text(3, 0, '2018'), Text(4, 0, '2019')]

The line plot does not shift the x values and plots the years in the 2000s range:

ax = sns.lineplot(x='year', y='height', data=df1)
print(ax.get_xlim())

# (2014.8, 2019.2)

One workaround is to use reset_index() on the line plot's data and use x='index' to manually shift its x values to 0 to align with the bar plot:

g = sns.lineplot(x='index', y='height', data=df1.reset_index(), ax=ax2)

after reset_index() on line plot data

tdy
  • 36,675
  • 19
  • 86
  • 83