Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display count on top of seaborn barplot

I have a dataframe that looks like:

  User   A       B     C
   ABC   100    121   OPEN
   BCD   200    255   CLOSE
   BCD   500    134   OPEN
   DEF   600    125   CLOSE
   ABC   900    632   OPEN
   ABC   150    875   CLOSE
   DEF   690    146   OPEN

I am trying to display a countplot on column 'User'. The code is as follows:

fig, ax1 = plt.subplots(figsize=(20,10))
graph = sns.countplot(ax=ax1,x='User', data=df)
graph.set_xticklabels(graph.get_xticklabels(),rotation=90)
for p in graph.patches:
    height = p.get_height()
    graph.text(p.get_x()+p.get_width()/2., height + 0.1,
        'Hello',ha="center")

The output looks like:

However, I want to replace string 'Hello' with the value_counts of column 'User'. When I add the code to add label to graph :

for p in graph.patches:
    height = p.get_height()
    graph.text(p.get_x()+p.get_width()/2., height + 0.1,
        df['User'].value_counts(),ha="center")

I get the output as:

like image 481
Tjs01 Avatar asked Mar 11 '19 15:03

Tjs01


2 Answers

New in matplotlib 3.4.0

We can now automatically annotate bar plots with the built-in Axes.bar_label, so all we need to do is access/extract the seaborn plot's Axes.

Seaborn offers several ways to plot counts, each with slightly different count aggregation and Axes handling:

  • seaborn.countplot (most straightforward)

    This automatically aggregates counts and returns an Axes, so just directly label ax.containers[0]:

    ax = sns.countplot(x='User', data=df)
    ax.bar_label(ax.containers[0])
    
  • seaborn.catplot (kind='count')

    This plots a countplot onto a facet grid, so extract the Axes from the grid before labeling ax.containers[0]:

    g = sns.catplot(x='User', kind='count', data=df)
    for ax in g.axes.flat:
        ax.bar_label(ax.containers[0])
    
  • seaborn.barplot

    This returns an Axes but does not aggregate counts, so first compute Series.value_counts before labeling ax.containers[0]:

    counts = df['User'].value_counts().rename_axis('user').reset_index(name='count')
    
    ax = sns.barplot(x='user', y='count', data=counts)
    ax.bar_label(ax.containers[0])
    

If you are using hue:

  • hue plots will contain multiple bar containers, so ax.containers will need to be iterated:

    ax = sns.countplot(x='User', hue='C', data=df)
    for container in ax.containers:
        ax.bar_label(container)
    

seaborn bars with labels

like image 169
tdy Avatar answered Sep 19 '22 07:09

tdy


df['User'].value_counts() will return a Series containing counts of unique values of the column User.

Without analyzing in much detail your code, you could correct it by indexing the result of value_counts with a counter:

fig, ax1 = plt.subplots(figsize=(20,10))
graph = sns.countplot(ax=ax1,x='User', data=df)
graph.set_xticklabels(graph.get_xticklabels(),rotation=90)
i=0
for p in graph.patches:
    height = p.get_height()
    graph.text(p.get_x()+p.get_width()/2., height + 0.1,
        df['User'].value_counts()[i],ha="center")
    i += 1

With your sample data, it produces the following plot:

enter image description here

As suggested by @ImportanceOfBeingErnest, the following code produces the same output with simpler code, using the height variable itself instead of the value_counts indexed:

fig, ax1 = plt.subplots(figsize=(20,10))
graph = sns.countplot(ax=ax1,x='User', data=df)
graph.set_xticklabels(graph.get_xticklabels(),rotation=90)
for p in graph.patches:
    height = p.get_height()
    graph.text(p.get_x()+p.get_width()/2., height + 0.1,height ,ha="center")
like image 42
Daniel Labbe Avatar answered Sep 17 '22 07:09

Daniel Labbe