Único elemento de leyenda con dos líneas.

Me gustaría generar una leyenda de matplotlib personalizada que, para cada entrada tenga dos líneas para cada etiqueta, como se muestra en este ejemplo:

introduzca la descripción de la imagen aquí

De algunas investigaciones, parece posible simplemente proporcionar dos handles' to the método fig.legend (manejadores, tags) `(vea esta publicación como ejemplo). Sin embargo, como se muestra en el siguiente código de ejemplo, esto simplemente superpone las líneas una encima de la otra.

 import matplotlib.lines as mlines import matplotlib.pyplot as plt blue_line = mlines.Line2D([], [], color='r') green_line = mlines.Line2D([], [], linestyle='--', color='k') fig, ax = plt.subplots(figsize=(5, 5)) handles = [(blue_line,green_line)] labels = ['test'] fig.legend(handles=handles, labels=labels, fontsize=20) 

Entonces, creo que debo transformar uno de los objetos de Line2D o generar un nuevo objeto de Patch que contenga dos líneas. Sin embargo, no puedo averiguar cómo hacer esto. ¿Existe una forma sencilla de combinar dos parches, o me he perdido un truco al combinar las asas?

Contexto

En caso de que esto ayude a otros, el contexto es que estoy usando la técnica que se describe aquí , que es un eje gemelo que está coloreado para mostrar dos gráficos diferentes simultáneamente. Sin embargo, las dos líneas tienen la misma etiqueta, de ahí que quisiera combinarlas.

En la guía de leyendas de matplotlib hay un capítulo sobre los controladores de leyenda personalizados . Podría adaptarlo a sus necesidades, por ejemplo, así:

 import matplotlib.pyplot as plt import numpy as np from matplotlib.legend_handler import HandlerBase class AnyObjectHandler(HandlerBase): def create_artists(self, legend, orig_handle, x0, y0, width, height, fontsize, trans): l1 = plt.Line2D([x0,y0+width], [0.7*height,0.7*height], linestyle='--', color='k') l2 = plt.Line2D([x0,y0+width], [0.3*height,0.3*height], color='r') return [l1, l2] x = np.linspace(0, 3) fig, axL = plt.subplots(figsize=(4,3)) axR = axL.twinx() axL.plot(x, np.sin(x), color='k', linestyle='--') axR.plot(x, 100*np.cos(x), color='r') axL.set_ylabel('sin(x)', color='k') axR.set_ylabel('100 cos(x)', color='r') axR.tick_params('y', colors='r') plt.legend([object], ['label'], handler_map={object: AnyObjectHandler()}) plt.show() 

introduzca la descripción de la imagen aquí

Para tener múltiples entradas de este tipo, uno puede suministrar una tupla de parámetros que el Handler utiliza para dibujar la leyenda.

 import matplotlib.pyplot as plt import numpy as np from matplotlib.legend_handler import HandlerBase class AnyObjectHandler(HandlerBase): def create_artists(self, legend, orig_handle, x0, y0, width, height, fontsize, trans): l1 = plt.Line2D([x0,y0+width], [0.7*height,0.7*height], linestyle=orig_handle[1], color='k') l2 = plt.Line2D([x0,y0+width], [0.3*height,0.3*height], color=orig_handle[0]) return [l1, l2] x = np.linspace(0, 3) fig, axL = plt.subplots(figsize=(4,3)) axR = axL.twinx() axL.plot(x, np.sin(x), color='k', linestyle='--') axR.plot(x, 100*np.cos(x), color='r') axL.plot(x, .3*np.sin(x), color='k', linestyle=':') axR.plot(x, 20*np.cos(x), color='limegreen') axL.set_ylabel('sin(x)', color='k') axR.set_ylabel('100 cos(x)', color='r') axR.tick_params('y', colors='r') plt.legend([("r","--"), ("limegreen",":")], ['label', "label2"], handler_map={tuple: AnyObjectHandler()}) plt.show() 

introduzca la descripción de la imagen aquí

Aquí hay una solución inadecuada a la que he llegado después de jugar. Lo publico en caso de que ayude a otros, pero preferiría hacerlo correctamente. Utiliza dos columnas y la etiqueta de la primera como ‘/’ o una podría usar igualmente “y”.

 import matplotlib.pyplot as plt x = np.linspace(0, 3) fig, axL = plt.subplots() axR = axL.twinx() axL.plot(x, np.sin(x), color='k', label='/') axR.plot(x, 100*np.cos(x), color='r', label='label') axL.set_ylabel('sin(x)', color='k') axR.set_ylabel('100 cos(x)', color='r') axR.tick_params('y', colors='r') handlesL, labelsL = axL.get_legend_handles_labels() handlesR, labelsR = axR.get_legend_handles_labels() handles = handlesL + handlesR labels = labelsL + labelsR axR.legend(handles, labels, loc='lower center', ncol=2, fontsize=16, handletextpad=0.4, columnspacing=0.4) 

introduzca la descripción de la imagen aquí