Установка фиксированного размера для точек в легенде
Я делаю некоторые диаграммы рассеяния, и я хочу установить размер точек в легенде на фиксированное равное значение.
Сейчас у меня есть это:
import matplotlib.pyplot as plt
import numpy as np
def rand_data():
return np.random.uniform(low=0., high=1., size=(100,))
# Generate data.
x1, y1 = [rand_data() for i in range(2)]
x2, y2 = [rand_data() for i in range(2)]
plt.figure()
plt.scatter(x1, y1, marker='o', label='first', s=20., c='b')
plt.scatter(x2, y2, marker='o', label='second', s=35., c='r')
# Plot legend.
plt.legend(loc="lower left", markerscale=2., scatterpoints=1, fontsize=10)
plt.show()
который производит это:
![enter image description here]()
Размеры точек в легенде масштабируются, но не одинаковы. Как я могу зафиксировать размеры точек в легенде равным значению, не влияя на размеры на графике scatter
?
Ответы
Ответ 1
Я просмотрел исходный код matplotlib
. Плохая новость заключается в том, что не существует простого способа установить равные размеры точек в легенде. Это особенно сложно с диаграммами рассеяния (неправильно: см. Обновление ниже). Существуют две альтернативы:
- Измените код
maplotlib
- Добавить преобразование в объекты
PathCollection
, представляющие точки на изображении. Преобразование (масштабирование) должно учитывать исходный размер.
Ни один из них не очень приятен, хотя # 1, кажется, проще. В этом отношении особенно важны графики scatter
.
Однако у меня есть хак, который, вероятно, вам нужен:
import matplotlib.pyplot as plt
import numpy as np
def rand_data():
return np.random.uniform(low=0., high=1., size=(100,))
# Generate data.
x1, y1 = [rand_data() for i in range(2)]
x2, y2 = [rand_data() for i in range(2)]
plt.figure()
plt.plot(x1, y1, 'o', label='first', markersize=np.sqrt(20.), c='b')
plt.plot(x2, y2, 'o', label='second', markersize=np.sqrt(35.), c='r')
# Plot legend.
lgnd = plt.legend(loc="lower left", numpoints=1, fontsize=10)
#change the marker size manually for both lines
lgnd.legendHandles[0]._legmarker.set_markersize(6)
lgnd.legendHandles[1]._legmarker.set_markersize(6)
plt.show()
Это дает:
![enter image description here]()
Кажется, что вы хотели.
Изменения:
-
scatter
изменен на plot
, который изменяет масштаб маркера (следовательно, sqrt
) и делает невозможным использование изменяемого размера маркера (если это было предназначено).
- размер маркера вручную изменился на 6 пунктов для обоих маркеров в легенде
Как вы можете видеть, это использует скрытые свойства подчеркивания (_legmarker
) и является ошибкой-уродливым. Он может сломаться при любом обновлении в matplotlib
.
Обновление
Хаа, я нашел его. Лучше взломать:
import matplotlib.pyplot as plt
import numpy as np
def rand_data():
return np.random.uniform(low=0., high=1., size=(100,))
# Generate data.
x1, y1 = [rand_data() for i in range(2)]
x2, y2 = [rand_data() for i in range(2)]
plt.figure()
plt.scatter(x1, y1, marker='o', label='first', s=20., c='b')
plt.scatter(x2, y2, marker='o', label='second', s=35., c='r')
# Plot legend.
lgnd = plt.legend(loc="lower left", scatterpoints=1, fontsize=10)
lgnd.legendHandles[0]._sizes = [30]
lgnd.legendHandles[1]._sizes = [30]
plt.show()
Теперь _sizes
(другое свойство подчеркивания) делает трюк. Не нужно прикасаться к источнику, хотя это довольно хак. Но теперь вы можете использовать все scatter
.
![enter image description here]()
Ответ 2
Аналогично ответу, если вы хотите, чтобы все маркеры имели одинаковый размер:
lgnd = plt.legend(loc="lower left", scatterpoints=1, fontsize=10)
for handle in lgnd.legendHandles:
handle.set_sizes([6.0])
С MatPlotlib 2.0.0
Ответ 3
У меня не было большого успеха при использовании решения @DrV, хотя, возможно, мой вариант использования уникален. Из-за плотности точек я использую наименьший размер маркера, т.е. plt.plot(x, y, '.', ms=1, ...)
, и хочу, чтобы символы легенды были больше.
Я следовал рекомендации, которую нашел на форумах matplotlib:
- построить данные (без меток)
- предел записи по осям (
xlimits = plt.xlim()
)
- отображать поддельные данные вдали от реальных данных, используя цвета и размеры символов, соответствующие легенде
- восстановить пределы осей (
plt.xlim(xlimits)
)
- создать легенду
Вот как это оказалось (для этого точки на самом деле менее важны, чем линии):
![enter image description here]()
Надеюсь, это поможет кому-то еще.
Ответ 4
Просто еще одна альтернатива здесь. Это имеет то преимущество, что он не будет использовать какие-либо "частные" методы и работает даже с другими объектами, отличными от рассеивателей, присутствующих в легенде. Ключ заключается в том, чтобы отобразить разброс PathCollection
на HandlerPathCollection
с установленной на него функцией обновления.
def update(handle, orig):
handle.update_from(orig)
handle.set_sizes([64])
plt.legend(handler_map={PathCollection : HandlerPathCollection(update_func=update)})
Полный пример кода:
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(42)
from matplotlib.collections import PathCollection
from matplotlib.legend_handler import HandlerPathCollection, HandlerLine2D
colors = ["limegreen", "crimson", "indigo"]
markers = ["o", "s", r"$\clubsuit$"]
labels = ["ABC", "DEF", "XYZ"]
plt.plot(np.linspace(0,1,8), np.random.rand(8), marker="o", markersize=22, label="A line")
for i,(c,m,l) in enumerate(zip(colors,markers,labels)):
plt.scatter(np.random.rand(8),np.random.rand(8),
c=c, marker=m, s=10+np.exp(i*2.9), label=l)
def updatescatter(handle, orig):
handle.update_from(orig)
handle.set_sizes([64])
def updateline(handle, orig):
handle.update_from(orig)
handle.set_markersize(8)
plt.legend(handler_map={PathCollection : HandlerPathCollection(update_func=updatescatter),
plt.Line2D : HandlerLine2D(update_func = updateline)})
plt.show()
![enter image description here]()
Ответ 5
Вы можете создать объект Line2D, который будет напоминать выбранные вами маркеры, за исключением другого размера маркера по вашему выбору, и использовать его для создания легенды. Это хорошо, потому что не требует размещения объекта на ваших осях (потенциально может вызвать событие изменения размера) и не требует использования каких-либо скрытых атрибутов. Единственным недостатком является то, что вы должны явно создавать легенду из списков объектов и меток, но это хорошо документированная функция matplotlib, поэтому ее использование довольно безопасно.
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import numpy as np
def rand_data():
return np.random.uniform(low=0., high=1., size=(100,))
# Generate data.
x1, y1 = [rand_data() for i in range(2)]
x2, y2 = [rand_data() for i in range(2)]
plt.figure()
plt.scatter(x1, y1, marker='o', label='first', s=20., c='b')
plt.scatter(x2, y2, marker='o', label='second', s=35., c='r')
# Create dummy Line2D objects for legend
h1 = Line2D([0], [0], marker='o', markersize=np.sqrt(20), color='b', linestyle='None')
h2 = Line2D([0], [0], marker='o', markersize=np.sqrt(20), color='r', linestyle='None')
# Set axes limits
plt.gca().set_xlim(-0.2, 1.2)
plt.gca().set_ylim(-0.2, 1.2)
# Plot legend.
plt.legend([h1, h2], ['first', 'second'], loc="lower left", markerscale=2,
scatterpoints=1, fontsize=10)
plt.show()
![resulting figure]()