Установка фиксированного размера точек в легенде
Я делаю несколько точечных диаграмм и хочу установить размер точек в легенде на фиксированное, равное значение.
Прямо сейчас у меня есть это:
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()
Который производит это:
Размеры точек в легенде масштабируются, но не одинаковы. Как я могу зафиксировать размеры точек в легенде на одинаковое значение, не влияя на размеры в графике scatter
?
5 ответов:
Я заглянул в исходный код
matplotlib
. Плохая новость заключается в том, что, похоже, не существует простого способа установить равные размеры точек в легенде. Это особенно трудно с точечными диаграммами (неправильно: смотрите обновление ниже ). По существу, существуют две альтернативы:Ни то, ни другое не доставляет особого удовольствия, хотя #1 кажется проще. Сюжеты
- измените код
Добавьте преобразование в объектыmaplotlib
PathCollection
, представляющие точки На изображении. Преобразование (масштабирование) должно принимать исходный размер в счет.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()
Это дает:
Похоже, именно этого ты и добивался.Изменения:
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
.
Аналогично ответу, предполагая, что вы хотите, чтобы все маркеры имели одинаковый размер:
lgnd = plt.legend(loc="lower left", scatterpoints=1, fontsize=10) for handle in lgnd.legendHandles: handle.set_sizes([6.0])
С MatPlotlib 2.0.0
Я не имел большого успеха, используя решение @DrV, хотя, возможно, мой случай использования уникален. Из-за плотности точек я использую наименьший размер маркера, т. е.
plt.plot(x, y, '.', ms=1, ...)
, и хочу, чтобы символы легенды были больше.Я последовал рекомендации, которую нашел на форумах matplotlib :
- построить график данных (без меток)
- предел осей записи (
xlimits = plt.xlim()
)- построение поддельных данных вдали от реальных данных с соответствующими условным обозначениям цветами символов и размеры
- восстановление границ осей(
plt.xlim(xlimits)
)- создать легенду
Вот как это получилось (для этого точки На самом деле менее важны, чем линии):
Надеюсь, что это помогает кто-то другой.
Вы можете создать объект 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()
Здесь просто еще одна альтернатива. Это имеет то преимущество, что он не будет использовать никаких "частных" методов и работает даже с другими объектами, кроме рассеивателей, присутствующих в легенде. Ключ состоит в том, чтобы сопоставить разброс
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 colors = ["limegreen", "crimson", "indigo"] markers = ["o", "s", r"$\clubsuit$"] labels = ["ABC", "DEF", "XYZ"] plt.plot(np.linspace(0,1,8), np.random.rand(8), 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 update(handle, orig): handle.update_from(orig) handle.set_sizes([64]) plt.legend(handler_map={PathCollection : HandlerPathCollection(update_func=update)}) plt.show()