Как применить linestyle к определенной линии в Seaborn lineplot?

#colors #seaborn

#Цвет #seaborn

Вопрос:

У меня есть фрейм данных, где в столбце Products есть много разных элементов, давайте покажем только некоторые:

 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"])
 

Я использую Seaborn для получения следующего 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()
 

Таким образом, все строки имеют стиль, «выбранный» Seaborn, но мне нужно установить определенный цвет и стиль линии (не пунктирный) для продуктов с именами ‘item_1’ и ‘item_2’.
До сих пор я нашел следующее решение:

 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)
 

Итак, я могу установить красный цвет только для item_1, но все остальные строки синие, хотя я хотел бы:

  • установите красный цвет и не пунктирные линии как для items_1, так и для items_2
  • установите другую цветовую палитру (например, яркую) для всех остальных линий

Возможно ли это сделать?

Ответ №1:

palette= и dashes= может быть передан словарь, отображающий уровни столбца, используемые для разных цветов / стилей.

Вы можете сгенерировать эти словари вручную или программно (в зависимости от того, сколько уровней у вас есть).

например, цветовая палитра:

 #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'
 

вывод:

 {'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'}
 

мы присваиваем каждому уровню цвет, отличный от цвета палитры «bright», и при необходимости мы можем исправить некоторые значения вручную (хотя имейте в виду, что в палитре bright уже есть цвет, очень похожий на красный, поэтому может возникнуть некоторая путаница).

То же самое можно сделать для стиля штриха:

 #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'] = ''
 

вывод:

 {'1': (4, 1.5),
'2': (1, 1),
'3': (3, 1.25, 1.5, 1.25),
'item_1': '',
'item_2': ''}
 

Здесь я использую одну из частных функций seaborn (используйте на свой страх и риск, может измениться в любое время), чтобы сгенерировать список стилей штрихов, а затем вручную установить конкретные уровни, которые я хочу, чтобы иметь сплошную линию. Я запрашиваю слишком много элементов в dash_list , потому что первый элемент всегда представляет собой сплошную линию, и я хочу зарезервировать сплошные линии для item_1 и item_2 .

полный код:

 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()
 

введите описание изображения здесь

Комментарии:

1. Большое спасибо @Diziet Asahi, действительно, это работает в примере dataframe! Но в моем полном фрейме данных, который содержит еще много элементов, я получаю сообщение об ошибке: в словаре палитры отсутствуют ключи: {‘9′, ’20’, …}. Итак, как я могу справиться с этой ошибкой? Учтите, что мне нужно анализировать фреймы данных, в которых количество элементов различно. Кроме того, есть ли способ установить linestyle для всех элементов с именем ‘item_x’ вместо ввода каждого из них?

Ответ №2:

Большое спасибо @Diziet Asahi, действительно, это работает в примере dataframe! Но в моем полном фрейме данных, который содержит еще много элементов, я получаю сообщение об ошибке:

В словаре палитры отсутствуют ключи: {‘9′, ’20’, …}

Я предполагаю, что это связано с тем, что цветовая палитра по умолчанию в seaborn — это качественная палитра, состоящая только из десяти различных оттенков. Чтобы справиться с этой ошибкой, я установил следующую цветовую палитру:

 cmap = sns.color_palette("hls", 75)
 

Это работает также при выборе цветового пространства «husl».