lhk
lhk

Reputation: 30006

seaborn barplot: vary color with x and hue

My dataset contains information on the short-term and long-term effects of a decision support model. I would like to plot this in a barplot, with 4 bars:

here is some sample code:

df = pd.DataFrame(columns=["model", "time", "value"])
df["model"] = ["on"]*2 + ["off"]*2
df["time"] = ["short", "long"] * 2
df["value"] = [1, 10, 2, 4]

sns.barplot(data=df, x="model", hue="time", y="value")
plt.show()

it looks like this:

barplot

There are many other related figures and they have established color conventions. Model on/off is encoded in the hue of the color. Longterm vs shortterm is encoded in the saturation of the color. So let's assume that I have given variables with color values. How can I assign each individual bar in the barplot an individual color?

The docs for seaborn.barplot only show color, which specifies one color for all elements and palette which only gives different hue values different colors.

Upvotes: 1

Views: 4640

Answers (2)

lhk
lhk

Reputation: 30006

The existing answer shows a nice way on how to arrange barplots with pyplot.

Unfortunately, my code relies heavily on other seaborn functionality, such as error bars, etc. So I would prefer to be able to keep the seaborn barplot functionality and just specify my own colors.

It is possible to iterate over the bars in a seaborn barplot as matplotlib patches. That allows setting a color, hatch, etc: Is it possible to add hatches to each individual bar in seaborn.barplot?

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

df = pd.DataFrame(columns=["model", "time", "value"])
df["model"] = ["on"]*2 + ["off"]*2
df["time"] = ["short", "long"] * 2
df["value"] = [1, 10, 2, 4]

fig, ax = plt.subplots()
bar = sns.barplot(data=df, x="model", hue="time", y="value", edgecolor="white")


colors = ["red", "green", "blue", "black"]
# Loop over the bars
for i,thisbar in enumerate(bar.patches):
    # Set a different hatch for each bar
    thisbar.set_color(colors[i])
    thisbar.set_edgecolor("white")

However, if you do this, it will not update the legend. You can use the following code to create a custom legend. It is complicated, because I need multiple color patches for every legend entry. This is apparently quite complicated to do: Python Matplotlib Multi-color Legend Entry

# add custom legend
ax.get_legend().remove()
legend_pos = np.array([1, 10])
patch_size = np.array([0.05, 0.3])
patch_offset = np.array([0.06, 0])

r2 = mpatches.Rectangle(legend_pos, *patch_size, fill=True, color='red')
r3 = mpatches.Rectangle(legend_pos + patch_offset, *patch_size, fill=True, color='blue')
ax.add_patch(r2)
ax.add_patch(r3)
ax.annotate('Foo', legend_pos + 3* patch_offset - [0, 0.1], fontsize='x-large')

plt.show()

custom barplot

Upvotes: 2

Diziet Asahi
Diziet Asahi

Reputation: 40667

Seaborn gives you convenience to do simple plots, but if you are trying to depart from the options that it gives you, it is usually simpler to use straight matplotlib functions instead:

plt.bar(x='model',height='value',data=df.loc[df.time=='short'], width=-0.4, align='edge', color=['C0','C1'])
plt.bar(x='model',height='value',data=df.loc[df.time=='long'], width=0.4, align='edge', color=['C2','C3'])

enter image description here

Upvotes: 2

Related Questions