Matplotlib повторяется, чтобы объединить маркеры и надписи легенды - PullRequest
0 голосов
/ 28 мая 2019

Если у меня есть следующая подпрограмма построения графика, которая строит график рассеяния и соответствующую линейную регрессию и комбинирует маркеры легенды:

import pandas as pd
from scipy.stats import linregress
import numpy as np
import matplotlib.pyplot as plt

#data and Regression
x = np.arange(0,5,1)
y = np.arange(0,10,2)
df = pd.DataFrame(data = {'x':x,'y':y})
s, intcpt, r, p, serr = linregress(df.x, df.y)
xHat = np.linspace(0,5,100)


# do the plotting
fig,ax = plt.subplots()
df.plot(x='x',y='y',ax=ax,label='series1',ls=' ',marker='x',c='blue')
ls_handle, = ax.plot(xHat, s*xHat + intcpt, linestyle='-', marker=None, c='blue')
handle2merge = [ls_handle]
handles, labels = ax.get_legend_handles_labels()
handle_combined = zip(handles, handle2merge)
ax.legend(handle_combined, labels)

, который возвращает место, где маркеры и маркеры линии объединяются в виде:

enter image description here

Теперь я хочу построить другой набор данных аналогичным образом:

#get current axis handles and labels
handle_start, label_start = ax.get_legend_handles_labels()

#second dataset and regression
x1 = np.arange(0,5,1)
y1 = np.arange(0,2.5,0.5)
df1 = pd.DataFrame(data = {'x':x1,'y':y1})
s1, intcpt1, r1, p1, serr1 = linregress(df1.x, df1.y)
xHat1 = np.linspace(0,5,100)

#plot second data set on same figure
marker_handle2, = ax.plot(df1.x, df1.y, marker = 'x', zorder=10,c='k', linestyle=' ')
line_handle2, = ax.plot(xHat, s1*xHat1 + intcpt1, linestyle='--', marker=None, c='k') 
new_line_handles = [line_handle2]
new_marker_handles= [marker_handle2]

ax.legend(handle_start + zip(new_marker_handles,new_line_handles), label_start + ['series2'])

Возвращает график, на котором маркеры для series1 дескриптор легенды содержит только маркер.

enter image description here

Почему len(handle_start)=1, когда я создал ручку с handle_combined = zip(handles, handle2merge)?

Ответы [ 2 ]

1 голос
/ 28 мая 2019

Я немного обшарил код.То, что вы делаете, передает list из tuples в ax.legend, что, по-видимому, рисует каждого Артиста в каждом tuple как одну запись в легенде.Я на самом деле не сталкивался с таким поведением раньше;это может быть ошибка или непреднамеренное использование ax.legend.

Тем не менее, я думаю, что в этом случае, поскольку вы знаете, как должны выглядеть ваши строки заранее, вместо того, чтобы идти окольным путем с zipи прочее, вы можете просто передать пользовательский Line2D на legend напрямую:

import numpy as np

from scipy.stats import linregress

from matplotlib import pyplot as plt
from matplotlib import lines

x1 = np.arange(0, 5, 1)
y1 = np.arange(0, 10, 2)
x2 = np.arange(0, 5, 1)
y2 = np.arange(0, 2.5, 0.5)

m1, c1, r1, p1, serr1 = linregress(x1, y1)
m2, c2, r2, p2, serr2 = linregress(x2, y2)

x_pred = np.linspace(0,5,100)

fig, ax = plt.subplots()

first_line, = ax.plot(x_pred, x_pred * m1 + c1, ls='-', c='blue')
first_scatter = ax.scatter(x1, y1, marker='x', c='blue')

second_line, = ax.plot(x_pred, x_pred * m2 + c2, ls='--', c='black')
second_scatter = ax.scatter(x2, y2, marker='x', c='black')

ax.legend([lines.Line2D([0], [0], marker='x', ls='-', c='blue'),
           lines.Line2D([0], [0], marker='x', ls='--', c='black')],
          ['series_1', 'series_2'])

enter image description here

Я немного очистил ваш код,но не стесняйтесь брать только последнюю строку и необходимый импорт.

0 голосов
/ 28 мая 2019

В последней строке просто используйте уже созданный объединенный дескриптор handle_combined вместо handle_start.

ax.legend(handle_combined + list(zip(new_marker_handles,new_line_handles)), 
          label_start + ['series2'])

Длина равна 1, но если вы посмотрите на содержимое списка, это кортеж, состоящий из двух объектов. Если вы напечатаете handle_combined, вы получите список из двух Line2D объектов, один из которых является маркером, а другой - строкой.

print (handle_combined)
# [(<matplotlib.lines.Line2D object at xxxxxxxxx>, <matplotlib.lines.Line2D object at xxxxxxxxx>)]

Однако, если вы печатаете handle_start, он возвращает только один Line2D объект

print (handle_start)
# [<matplotlib.lines.Line2D object at xxxxxxxxx>]

enter image description here

...