Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

On changing the bar width of a countplot, the relative position of the bars get shifted from the x-ticks. How can we fix this?

I am trying to display a count plot using seaborn, but the width of the bars is very high and the plot doesn't look nice. To counter it I change the width of the plot using the following code snippet:

sns.set()
fig,ax = plt.subplots(figsize=(10,4))
sns.countplot(x=imdb_data["label"],ax=ax)
for patch in ax.patches:
    height = p.get_height()
    width = patch.get_width
    p.set_height(height*0.8)
    patch.set_width(width*0.4)
    x      = p.get_x()
    ax.text(x = x+new_width/2.,y= new_height+4,s = height,ha="center")

ax.legend(labels=("Negative","Positive"),loc='lower right')
plt.show()

But upon doing so the x-tick labels get shifted and the plot looks something like as shown in the attached image.

enter image description here

How should I change the width that, the x-tick location also, change automatically as per the new width of the bar ? . Also the legend is not being displayed properly. I used the below snippet to add the legend:

plt.legend(labels=['Positive','Negative'],loc='lower right')

Please help me out.

like image 976
Sarvagya Dubey Avatar asked Sep 06 '25 02:09

Sarvagya Dubey


1 Answers

To keep the bar centered, you also need to change the x position with half the difference of the old and new width. Changing the height doesn't seem to be a good idea, as then the labels on the y-axis get mismatched. If the main reason to change the height is to make space for the text, it would be easier to change the y limits, e.g. via ax.margins(). Aligning the text vertically with 'bottom' allows leaving out the arbitrary offset for the y position.

The labels for the legend can be set via looping through the patches and setting the labels one by one. As the x-axis already has different positions for each bar, it might be better to leave out the legend and change the x tick labels?

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

sns.set()
imdb_data = pd.DataFrame({"label": np.random.randint(0, 2, 7500)})

fig, ax = plt.subplots(figsize=(10, 4))
sns.countplot(x=imdb_data["label"], ax=ax)
for patch, label in zip(ax.patches, ["Negative", "Positive"]):
    height = patch.get_height()
    width = patch.get_width()
    new_width = width * 0.4
    patch.set_width(new_width)
    patch.set_label(label)
    x = patch.get_x()
    patch.set_x(x + (width - new_width) / 2)
    ax.text(x=x + width/2, y=height, s=height, ha='center', va='bottom')
ax.legend(loc='lower right')
ax.margins(y=0.1)
plt.tight_layout()
plt.show()

resulting plot

PS: To change the x tick labels, so they can be used instead of the legend, add

ax.set_xticklabels(['negative', 'positive'])

and leave out the ax.legend() and patch.set_label(label) lines.

like image 148
JohanC Avatar answered Sep 07 '25 21:09

JohanC