Adding a line to a barplot in Seaborn pads the graph, how do I get rid of the new margins?

Tags: , ,



I have a seaborn barplot with a line graphed on top of it that looks like this:

current output

As you can see, there are thick margins on the plot that appear after adding the line. The plot looks normal if the line is not added:

output without line added

plt.margins() does not work and basically destroys the entire graph. Not sure why. I have not tried additional rc-settings in sns.set() because I cannot find the page that lists all the options anymore, so if someone could link that as well so I could bookmark it that would be appreciated.

The barplot is created with:

sns.set(rc={'figure.figsize': (50, 10), 'axes.labelsize': 25, 'ytick.labelsize': 17, 'axes.labelpad': 15, 'legend.fontsize': 20})
fig, ax1 = plt.subplots()
g = sns.barplot(x='City/Town', y="Value", hue="Metric", data=df, ax=ax1)
g.set_title(title, fontsize=30)
fig.autofmt_xdate()

and the line is added with:

ax1.plot(range(len(X)), df.Y_pred[df['Metric'] == bar1]*scale, color='red')

where X and Y are numpy ndArrays and scale is just an int.

Answer

Here is an attempt to reproduce your plot, although I don’t have access to your data, nor the way X is determined.

What seems to happen, is that sns.barplot() explicitly sets the limits for the x-axis to have nice margins. ax1.plot() resets the margins, using matplotlib’s defaults (typically 0.05 or 5% of the width). So, a solution is to explicitly set very small margins: ax1.margins(x=0.01) (or even smaller, depending on your data).

The code below also uses seaborn’s lineplot to get more consistency in how the x-axis is treated (but lineplot also employs matplotlib’s margins, so it isn’t a solution for this padding). autofmt_xdate() could be replaced by explicitly rotating the labels, which would give some more control about the exact rotation (but this also doesn’t influence the padding problem).

import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np

np.random.seed(123)
df = pd.DataFrame({'City/Town': np.random.choice([*'ABCDEFGHIJKLMNOP'], 500),
                   'Metric': np.random.choice(['bar1', 'bar2', 'bar3'], 500),
                   'Value': np.random.randint(10, 20, 500)})
sns.set(rc={'figure.figsize': (12, 6), 'axes.labelsize': 25, 'ytick.labelsize': 17, 'axes.labelpad': 15,
            'legend.fontsize': 20})
fig, ax1 = plt.subplots()
sns.barplot(x='City/Town', y="Value", hue="Metric", data=df, ax=ax1)
ax1.set_title('title', fontsize=30)
df1 = df[df['Metric'] == 'bar1']
sns.lineplot(x=df1['City/Town'], y=df1["Value"]*1.2, ci=None, ax=ax1)
ax1.margins(x=0.01)
plt.setp(ax1.get_xticklabels(), rotation=30, ha='right')
plt.tight_layout()
plt.show()

seaborn barplot and lineplot



Source: stackoverflow