Как создавать вложенные легенды в Matplotlib - PullRequest
3 голосов
/ 03 августа 2020

Мне нужно построить в Matplotlib количество, которое является суммой различных вкладов.

Я хотел бы выделить этот факт в легенде графика, перечислив различные вклады как подэлементы основной записи легенды.

Набросок результата, который я хотел бы получить, можно найти на рисунке ниже. Обратите внимание, что мне необязательно добиваться именно той легенды, которая изображена, а просто чего-то подобного.

Ответы [ 2 ]

1 голос
/ 08 августа 2020

Вы можете попробовать создать две отдельные легенды для своей фигуры. Конечно, это уловка, а не прямая особенность объекта легенды, поскольку кажется, что в matplotlib нет реализации того, что вам нужно. Но играя с числами в bbox и размером шрифта, вы можете довольно хорошо настроить его.

import matplotlib.pyplot as plt 
import numpy as np

x = np.arange(0.0, 1, 0.01)
x1 = np.sin(2*np.pi*x)
x2 = np.sin(2*np.pi*x+1)
x3 = np.sin(2*np.pi*x+2)


fig, ax = plt.subplots()
f1, = ax.plot(x1, 'r', lw=4)
f2, = ax.plot(x2, 'k', lw=2)
f3, = ax.plot(x3, 'b', lw=2)

legend1 = plt.legend([f1], ["Main legend"], fontsize=12, loc=3, bbox_to_anchor=(0,0.1,0,0), frameon=False)
legend2 = plt.legend((f2, f3), ('sublegend 1', 'sublegend 2'), fontsize=9,
                 loc=3, bbox_to_anchor=(0.05,0,0,0), frameon=False)
plt.gca().add_artist(legend1)
plt.show()

enter image description here

EDIT:

Well, if we insert 2 legends, why not just inserting a completely new figure as inset inside the bigger figure, dedicated for a legend, inside which you can draw and write whatever you like? Admittedly it’s a hard work, you have to design each and every line inside including the precise location coordinates. But that’s the way I could think of for doing what you wanted:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(0.0, 1, 0.01)
x1 = np.sin(2*np.pi*x)
x2 = np.sin(2*np.pi*x+1)
x3 = np.sin(2*np.pi*x+2)


fig, ax = plt.subplots()
f1, = ax.plot(x1, 'r', lw=4)
f2, = ax.plot(x2, 'k', lw=2)
f3, = ax.plot(x3, 'b', lw=2)

## set all lines for inner figure
yline1 = np.array([-0.15, -0.15]) 
line1 = np.array([2, 10])
yline2 = np.array([3, 0])
line2 = np.array([4, 4])
yline3 = np.array([1.5, 1.5])
line3 = np.array([4, 6])
yline4 = np.array([1.5, 1.5])
line4 = np.array([7, 10])
yline5 = np.array([3, 3])
line5 = np.array([4, 6])
yline6 = np.array([3, 3])
line6 = np.array([7, 10])

## inset figure
axin1 = ax.inset_axes([2.5, -1, 30, 0.5], transform=ax.transData)  # 

## plot all lines
axin1.plot(line1, yline1, linewidth=4, c='r')
axin1.plot(line2, yline2, 'k', lw=1)
axin1.plot(line3, yline3, 'k', lw=1)
axin1.plot(line4, yline4, 'b', lw=3)
axin1.plot(line5, yline5, 'k', lw=1)
axin1.plot(line6, yline6, 'k', lw=3)

## text
axin1.text(12, 0, 'MAIN', fontsize=12)
axin1.text(12, 1.7, 'Subtext 1', fontsize=10)
axin1.text(12, 3.2, 'Subtext 2', fontsize=10)

## adjust
axin1.set_ylim([4, -1])
axin1.set_xlim([0, 27])
axin1.set_xticklabels('')
axin1.set_yticklabels('')

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

0 голосов
/ 06 августа 2020

Я поискал нестандартный пример в легенде и не нашел никаких указаний на снижение уровня. Вы можете просто выстроить объекты в легенде. Я создал иерархию представленных изображений в виде цветов и маркеров. Официальная ссылка доработана. Благодаря этому отпадает необходимость в аннотировании только легенды особым образом.

import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerLineCollection, HandlerTuple

fig, ax1 = plt.subplots(1, 1, constrained_layout=True)

params = {'legend.fontsize': 16,
          'legend.handlelength': 3}

plt.rcParams.update(params)

x = np.linspace(0, np.pi, 25)
xx = np.linspace(0, 2*np.pi, 25)
xxx = np.linspace(0, 3*np.pi, 25)

p1, = ax1.plot(x, np.sin(x), lw=5, c='r')
p2, = ax1.plot(x, np.sin(xx), 'm-d', c='g')
p3, = ax1.plot(x, np.sin(xxx), 'm-s', c='b')

# Assign two of the handles to the same legend entry by putting them in a tuple
# and using a generic handler map (which would be used for any additional
# tuples of handles like (p1, p2)).
l = ax1.legend([p1, (p1, p2), (p1, p3)], ['Legend entry', 'Contribution 1', 'Contribution 2'], scatterpoints=1,
               numpoints=1, markerscale=1.3, handler_map={tuple: HandlerTuple(ndivide=None, pad=1.0)})

plt.show()

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

...