Skip to content
Advertisement

How to assign different palettes to hue levels in a Seaborn barplot?

I’m using Seaborn to create bar plots in Python. I have a 2 (attention: divided vs focused) X 3 (solutions: 1,2,3) design. For divided, I want the bar colors to be darkred, darkgreen, darkblue. For focused, I want the bar colors to be red, green, blue.

This code doesn’t work because the palette only applies to the hue parameter:

import seaborn as sns
import matplotlib.pyplot as plt

data = sns.load_dataset('attention')

palette = ["darkred", "red", "darkgreen", "green", "darkblue", "blue"]
palette = ["darkred", "darkgreen", "darkblue", "red", "green", "blue"]

sns.set(style='ticks', context='notebook', font_scale=1.2)
fig, ax = plt.subplots(figsize=(7, 5), dpi=96)
my_plot = sns.barplot(x="solutions", y="score", hue="attention",
                      palette=palette, capsize=.1, ax=ax, data=data)

plt.show()

enter image description here

This code almost works:

import seaborn as sns
import matplotlib.pyplot as plt

data = sns.load_dataset('attention')

sns.set(style='ticks', context='notebook', font_scale=1.5)
fig, ax = plt.subplots(figsize=(7, 5), dpi=96)
sns.barplot(x="solutions", y="score",
            palette=['red', 'green', 'blue'], capsize=.1, ax=ax,
            data=data.query('attention=="focused"'))
sns.barplot(x="solutions", y="score",
            palette=['darkred', 'darkgreen', 'darkblue'], capsize=.1, ax=ax,
            data=data.query('attention=="divided"'))
sns.despine(top=True, right=True)
plt.tight_layout()
plt.show()

but has 2 problems:

  1. No Legend
  2. The bars are not separated.

enter image description here

There must (should?) be some way to create this figure with seaborn in an elegant way. The first approach is ideal: a single call to barplot with parameters specifying the graph. The second approach is ok: multiple calls to barplot. The lease desirable approach would be direct matplotlib bar() calls, but at this point I’d settle for any approach that led to the desired figure.

Note that, as far as I can tell, these similarly named questions do not appear to address the outcome being sought in this question:

  1. seaborn barplot: vary color with x and hue
  2. Set color-palette in Seaborn Grouped Barplot depending on values

Advertisement

Answer

You could create the bar plot using grey and black as hue-colors for the legend. And, afterwards, loop through the created bars and change their color.

import seaborn as sns
import matplotlib.pyplot as plt

data = sns.load_dataset('attention')

sns.set(style='ticks', context='notebook', font_scale=1.2)
fig, ax = plt.subplots(figsize=(7, 5), dpi=96)
sns.barplot(x="solutions", y="score", hue="attention", hue_order=['divided', 'focused'],
            palette={'divided': 'grey', 'focused': 'black'}, capsize=.1, ax=ax, data=data)
for bar_group, desaturate_value in zip(ax.containers, [0.5, 1]):
    for bar, color in zip(bar_group, ['red', 'green', 'blue']):
        bar.set_facecolor(sns.desaturate(color, desaturate_value))
plt.show()

sns.barplot with adapted colors

To get all the variations into the legend, the TupleHandler can be used, for example (using colors=['crimson', 'limegreen', 'dodgerblue']):

from matplotlib.legend_handler import HandlerTuple

ax.legend(handles=[tuple(bar_group) for bar_group in ax.containers],
          labels=[bar_group.get_label() for bar_group in ax.containers],
          title=ax.legend_.get_title().get_text(),
          handlelength=4, handler_map={tuple: HandlerTuple(ndivide=None, pad=0.1)})

using a TupleHandler for the legend

Here is another example:

data = sns.load_dataset('tips')

sns.set(style='ticks', context='notebook', font_scale=1.2)
fig, ax = plt.subplots(figsize=(12, 5))
sns.barplot(x="day", y="tip", hue="smoker",
            capsize=.1, ax=ax, data=data)
for bar_group, desaturate_value in zip(ax.containers, [0.5, 1]):
    for bar, color in zip(bar_group, plt.cm.Set2.colors):
        bar.set_facecolor(sns.desaturate(color, desaturate_value))

ax.legend(handles=[tuple(bar_group) for bar_group in ax.containers],
          labels=[bar_group.get_label() for bar_group in ax.containers],
          title=ax.legend_.get_title().get_text(),
          handlelength=4, handler_map={tuple: HandlerTuple(ndivide=None, pad=0.1)})
sns.despine()
plt.tight_layout()
plt.show()

tips dataset sns.barplot colors per day

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement