Seaborn Barplot - Displaying Values
Works with single ax or with matrix of ax (subplots)
from matplotlib import pyplot as plt
import numpy as np
def show_values_on_bars(axs):
def _show_on_single_plot(ax):
for p in ax.patches:
_x = p.get_x() + p.get_width() / 2
_y = p.get_y() + p.get_height()
value = '{:.2f}'.format(p.get_height())
ax.text(_x, _y, value, ha="center")
if isinstance(axs, np.ndarray):
for idx, ax in np.ndenumerate(axs):
_show_on_single_plot(ax)
else:
_show_on_single_plot(axs)
fig, ax = plt.subplots(1, 2)
show_values_on_bars(ax)
Let's stick to the solution from the linked question (Changing color scale in seaborn bar plot). You want to use argsort to determine the order of the colors to use for colorizing the bars. In the linked question argsort is applied to a Series object, which works fine, while here you have a DataFrame. So you need to select one column of that DataFrame to apply argsort on.
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
df = sns.load_dataset("tips")
groupedvalues=df.groupby('day').sum().reset_index()
pal = sns.color_palette("Greens_d", len(groupedvalues))
rank = groupedvalues["total_bill"].argsort().argsort()
g=sns.barplot(x='day',y='tip',data=groupedvalues, palette=np.array(pal[::-1])[rank])
for index, row in groupedvalues.iterrows():
g.text(row.name,row.tip, round(row.total_bill,2), color='black', ha="center")
plt.show()
The second attempt works fine as well, the only issue is that the rank as returned by
rank()
starts at 1
instead of zero. So one has to subtract 1 from the array. Also for indexing we need integer values, so we need to cast it to int
.
rank = groupedvalues['total_bill'].rank(ascending=True).values
rank = (rank-1).astype(np.int)
New in matplotlib 3.4.0
There is now a built-in Axes.bar_label
to automatically label bar containers:
-
For single-group bar plots, pass the single bar container:
ax = sns.barplot(x='day', y='tip', data=groupedvalues) ax.bar_label(ax.containers[0])
-
For multi-group bar plots (with
hue
), iterate the multiple bar containers:ax = sns.barplot(x='day', y='tip', hue='sex', data=df) for container in ax.containers: ax.bar_label(container)
More details:
-
How to label count plots (
sns.countplot
andsns.catplot
) -
How to label percentage counts (
fmt
param) -
How to label with commas as thousands separators (
labels
param) - How to label thresholded bar plots
- How to label horizontal bar plots
Color-ranked version
Is there a way to scale the colors of the bars, with the lowest value of
total_bill
having the lightest color (in this case Friday) and the highest value oftotal_bill
having the darkest?
-
Find the rank of each
total_bill
value:-
Either use
Series.sort_values
:ranks = groupedvalues.total_bill.sort_values().index # Int64Index([1, 0, 3, 2], dtype='int64')
-
Or condense Ernest's
Series.rank
version by chainingSeries.sub
:ranks = groupedvalues.total_bill.rank().sub(1).astype(int).array # [1, 0, 3, 2]
-
-
Then reindex the color palette using
ranks
:palette = sns.color_palette('Blues_d', len(ranks)) ax = sns.barplot(x='day', y='tip', palette=np.array(palette)[ranks], data=groupedvalues)
Just in case if anyone is interested in labeling horizontal barplot graph, I modified Sharon's answer as below:
def show_values_on_bars(axs, h_v="v", space=0.4):
def _show_on_single_plot(ax):
if h_v == "v":
for p in ax.patches:
_x = p.get_x() + p.get_width() / 2
_y = p.get_y() + p.get_height()
value = int(p.get_height())
ax.text(_x, _y, value, ha="center")
elif h_v == "h":
for p in ax.patches:
_x = p.get_x() + p.get_width() + float(space)
_y = p.get_y() + p.get_height()
value = int(p.get_width())
ax.text(_x, _y, value, ha="left")
if isinstance(axs, np.ndarray):
for idx, ax in np.ndenumerate(axs):
_show_on_single_plot(ax)
else:
_show_on_single_plot(axs)
Two parameters explained:
h_v
- Whether the barplot is horizontal or vertical. "h"
represents the horizontal barplot, "v"
represents the vertical barplot.
space
- The space between value text and the top edge of the bar. Only works for horizontal mode.
Example:
show_values_on_bars(sns_t, "h", 0.3)