Как построить линейный участок с точками в определенных точках с определенными цветами и типами линий для каждой линии, используя морскую волю? - PullRequest
1 голос
/ 21 октября 2019

У меня есть следующий фрейм данных

import pandas as pd



 data_tmp = pd.DataFrame({'x': [0,14,28,42,56, 0,14,28,42,56],
                         'y': [0, 0.003, 0.006, 0.008, 0.001, 0*2, 0.003*2, 0.006*2, 0.008*2, 0.001*2],
                         'cat': ['A','A','A','A','A','B','B','B','B','B'],
                         'color': ['#B5D8F0','#B5D8F0','#B5D8F0','#B5D8F0','#B5D8F0','#247AB2','#247AB2','#247AB2','#247AB2','#247AB2'],
                         'point': [14,14,14,14,14,28,28,28,28,28],
                         'linestyles':['-','-','-','-','-','--','--','--','--','--']})

Я хотел бы создать линейный участок с различными color и linestyles на cat. Но я хотел бы дать конкретные color и linestyles на cat, как они определены в dataframe. Наконец, я хотел бы отметить point s на каждой строке одним и тем же цветом.

Я только попробовал:

sns.lineplot(x="x", y="y", hue="cat", data=data_tmp)
sns.scatterplot(x="point",y="y",hue="cat", data=data_tmp[data_tmp.point==data_tmp.x])
plt.show()

Есть идеи?

Ответы [ 3 ]

3 голосов
/ 21 октября 2019

Может быть, вы хотите использовать matplotlib напрямую, как

import pandas as pd
import matplotlib.pyplot as plt


df = pd.DataFrame({'x': [0,14,28,42,56, 0,14,28,42,56],
                   'y': [0, 0.003, 0.006, 0.008, 0.001, 0*2, 0.003*2, 0.006*2, 0.008*2, 0.001*2],
                   'cat': ['A','A','A','A','A','B','B','B','B','B'],})


d = {"A" : {"color": '#B5D8F0', "markersize":  5, "linestyle": "-"},
     "B" : {"color": '#247AB2', "markersize": 10, "linestyle": "--"}}

for n, grp in df.groupby("cat"):
    plt.plot(grp.x, grp.y, marker="o", label=n, **d[n])

plt.legend()
plt.show()

enter image description here

1 голос
/ 21 октября 2019

Вот мое решение с помощью @ jdehesa

Я также поставил здесь легенду за пределами сюжета и немного отшлифовал до меток

def get_dash_pattern(style):
    _, dash = mpl.lines._get_dash_pattern(style)
    return dash if dash else (None, None)

palette = dict(zip(data_tmp.cat, data_tmp.color))
dashes = dict(zip(data_tmp.cat, data_tmp.linestyles))
dashes = {k: get_dash_pattern(v) for k, v in dashes.items()}

ax = sns.lineplot(x="x", y="y", hue="cat", data=data_tmp, palette=palette, style='cat',  dashes=dashes)
ax = sns.scatterplot(x="point", y="y", hue="cat", data=data_tmp[data_tmp.point == data_tmp.x], palette=palette,
                     legend=False)

ax.set_title('title')
ax.set_ylabel('y label')
ax.set_xlabel('x label')
ax.legend(loc=(1.04, 0))
plt.show()

enter image description here

1 голос
/ 21 октября 2019

Вот как я мог это сделать. Вам необходимо использовать столбец cat для управления различными параметрами графика (цвет, стиль, размер маркера), а затем создавать объекты отображения (здесь дикты), которые сообщают, какое значение параметра использовать для каждой категории. Цвет легкий. Стиль линий сложнее, потому что Seaborn предлагает только dashes в качестве настраиваемого параметра, который должен быть задан в расширенном формате Matplotlib (segment, gap). Функция matplotlib.lines._get_dash_pattern переводит строковое значение (например, --) в этот формат, хотя возвращаемое значение необходимо обрабатывать с осторожностью. К сожалению, для размера маркера lineplot не дает возможности изменить размер маркера в зависимости от категории (даже если вы можете изменить стиль маркера), поэтому вам нужно использовать scatterplot сверху. Последний бит - это легенда, вы, вероятно, захотите отключить ее для второго сюжета, чтобы не повторять ее, но проблема в том, что у первой легенды не будет маркеров. Если это вас беспокоит, вы все равно можете редактировать легенду вручную. В общем, это может выглядеть так:

import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns

# Converts a line style to a format acceptable by Seaborn
def get_dash_pattern(style):
    _, dash = mpl.lines._get_dash_pattern(style)
    return dash if dash else (None, None)

data_tmp = pd.DataFrame({
    'x': [0,14,28,42,56, 0,14,28,42,56],
    'y': [0, 0.003, 0.006, 0.008, 0.001, 0*2, 0.003*2, 0.006*2, 0.008*2, 0.001*2],
    'cat': ['A','A','A','A','A','B','B','B','B','B'],
    'color': ['#B5D8F0','#B5D8F0','#B5D8F0','#B5D8F0','#B5D8F0',
              '#247AB2','#247AB2','#247AB2','#247AB2','#247AB2'],
    'point': [14,14,14,14,14,28,28,28,28,28],
    'linestyles':['-','-','-','-','-','--','--','--','--','--']})
# Extract plot features as dicts
feats = (data_tmp[['cat', 'color', 'linestyles', 'point']]
         .set_index('cat').drop_duplicates().to_dict())
palette, dashes, sizes = feats['color'], feats['linestyles'], feats['point']
# Convert line styles to dashes
dashes = {k: get_dash_pattern(v) for k, v in dashes.items()}
# Lines
lines = sns.lineplot(x="x", y="y", hue="cat", style="cat", data=data_tmp,
                     palette=palette, dashes=dashes)
# Points
sns.scatterplot(x="x", y="y", hue="cat", size="cat", data=data_tmp,
                palette=palette, sizes=sizes, legend=False)
# Fix legend
for t, l in zip(lines.legend().get_texts(), lines.legend().get_lines()):
    l.set_marker('o')
    l.set_markersize(sizes.get(l.get_label(), 0) / t.get_fontsize())
plt.show()

Вывод:

Final plot

...