I have the a dataframe where in the column Products there are many different items, let's show only a few:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
data = np.array([[1, 1, 570], [2, 1, 650], [1, 2, 27], [2, 2, 64], [1, 3, 125], [2, 3, 216],
[1, 'item_1', 343], [2, 'item_1', 340], [1, 'item_2', 343], [2, 'item_2', 345]])
df = pd.DataFrame(data=data, columns=["Flag", "Products", "Value"])
I'm using Seaborn to get the following lineplot:
sns.set_theme()
sns.set_style("ticks")
sns.set_context("paper")
fig1, ax1 = plt.subplots()
sns.lineplot(data=df, x="Flag", y="Value",
hue="Products", style="Products", ax=ax1)
plt.legend(bbox_to_anchor=(1.02, 1),borderaxespad=0)
fig1.tight_layout()
In this way all the lines have a style "chosen" by Seaborn, but I need to set a specific color and a line style (not dashed) for the products named 'item_1' and 'item_2'. Up to now I have found the following solution:
palette = {c:'red' if c=='item_1' else 'blue' for c in df.Products.unique()}
sns.lineplot(data=df, x="Flag", y="Value",
hue="Products", style="Products", palette=palette, ax=ax1)
So, I can set the red color for the item_1 only, but all the other lines are blue, while I'd like to:
Is it possible to do that?
palette=
and dashes=
can be passed a dictionary mapping levels of the column used to different colors/styles.
You can generate these dictionaries by hand or programmatically (depending on how many levels you have).
for instance, the color palette:
#color palette
cmap = sns.color_palette("bright")
palette = {key:value for key,value in zip(data[hue_col].unique(), cmap)}
palette['item_1'] = 'red'
palette['item_2'] = 'red'
output:
{'1': (0.00784313725490196, 0.24313725490196078, 1.0), '2': (1.0, 0.48627450980392156, 0.0), '3': (0.10196078431372549, 0.788235294117647, 0.2196078431372549), 'item_1': 'red, 'item_2': 'red'}
we give each level a different color from the "bright" palette, and we can fix some values by hand if needed (although do keep in mind that there is a color very similar to red in the bright palette already, so there might be some possible confusion).
The same can be done for the dash style:
#style palette
dash_list = sns._core.unique_dashes(data[style_col].unique().size+1)
style = {key:value for key,value in zip(data[style_col].unique(), dash_list[1:])}
style['item_1'] = '' # empty string means solid
style['item_2'] = ''
output:
{'1': (4, 1.5), '2': (1, 1), '3': (3, 1.25, 1.5, 1.25), 'item_1': '', 'item_2': ''}
Here I use one of seaborn's private functions (use at your own risk, could change at any time), to generate a list of dash styles, and then manually set the particular levels I want to have a solid line. I request one too many items in dash_list
because the first element is always a solid line, and I want to reserve solid lines for item_1
and item_2
.
full code:
data = df
x_col = 'Flag'
y_col = "Value"
hue_col = "Products"
style_col = "Products"
#color palette
cmap = sns.color_palette("bright")
palette = {key:value for key,value in zip(data[hue_col].unique(), cmap)}
palette['item_1'] = 'red'
palette['item_2'] = 'red'
#style palette
dash_list = sns._core.unique_dashes(data[style_col].unique().size+1)
style = {key:value for key,value in zip(data[style_col].unique(), dash_list[1:])}
style['item_1'] = '' # empty string means solid
style['item_2'] = ''
sns.set_theme()
sns.set_style("ticks")
sns.set_context("paper")
fig1, ax1 = plt.subplots()
sns.lineplot(data=df, x=x_col, y=y_col,
hue=hue_col, palette=palette,
style=style_col, dashes=style,
ax=ax1)
plt.legend(bbox_to_anchor=(1.02, 1),borderaxespad=0)
fig1.tight_layout()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With