Troy
Troy

Reputation: 548

Annotate seaborn distplot with log scale throws an error

%matplotlib inline
import seaborn as sns
titanic = sns.load_dataset('titanic')
ax1 = sns.distplot(titanic['fare'], kde=False, bins=15,)

# ax1.set_yscale('log')

for p in ax1.patches:
    ax1.annotate(
        s=f"{p.get_height():1.0f}",
        xy=(p.get_x() + p.get_width() / 2., p.get_height()), 
        xycoords='data',
        ha='center', 
        va='center', 
        fontsize=11, 
        color='black',
        xytext=(0,7), 
        textcoords='offset points',
    )

The above code plots the histogram for the Fare of the titanic dataset, where each bar is annotated with its value using ax1.annotate. The trouble comes when I want to set the y scale to logc -- Uncomment the set_yscale line and run it; it throws an error saying:

ValueError: Image size of 378x84035 pixels is too large. It must be less than 2^16 in each direction.

Perhaps the xycoords parameter should be changed, but I'm not quite sure what to change it too.

I'm on Python 3.7.2, seaborn is version 0.9.0. Matplotlib version 3.0.2, and I'm using the Inline backend.

Upvotes: 6

Views: 3399

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339590

Some annotations are placed at y=0. This will cause the position to be undefined on a logarithmic scale.
In a jupyter notebook with the inline backend the default option for showing figures is to run them through the bbox_inches="tight" option of savefig. This "tight" algrorithm is then unable to locate the labels and will extent the figure size to include them anyways.

The solution I would propose here is to set the clip_on option of the annotations to True. This will prevent annotations which are outside the axes to be visible. It thereby solves the problem of zero-placed annotations on the log scale.

import matplotlib.pyplot as plt
import seaborn as sns
titanic = sns.load_dataset('titanic')
ax1 = sns.distplot(titanic['fare'], kde=False, bins=15,)

ax1.set_yscale('log')

for p in ax1.patches:
    ax1.annotate(
        s=f"{p.get_height():1.0f}",
        xy=(p.get_x() + p.get_width() / 2., p.get_height()), 
        xycoords='data',
        ha='center', 
        va='center', 
        fontsize=11, 
        color='black',
        xytext=(0,7), 
        textcoords='offset points',
        clip_on=True,                   # <---  important
    )

plt.savefig("outfig.png", bbox_inches="tight")
plt.show()

Upvotes: 6

Related Questions