Establecer un tamaño fijo para los puntos en la leyenda

Estoy haciendo algunos gráficos de dispersión y quiero establecer el tamaño de los puntos en la leyenda a un valor fijo e igual.

Ahora mismo tengo esto:

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() 

que produce esto:

introduzca la descripción de la imagen aquí

Los tamaños de los puntos en la leyenda se escalan pero no son los mismos. ¿Cómo puedo fijar los tamaños de los puntos en la leyenda a un valor igual sin afectar los tamaños en el scatter ?

matplotlib un vistazo al código fuente de matplotlib . La mala noticia es que no parece haber una forma sencilla de establecer puntos de igual tamaño en la leyenda. Es especialmente difícil con los gráficos de dispersión ( mal: consulte la actualización a continuación ). Hay esencialmente dos alternativas:

  1. Cambia el código de maplotlib
  2. Agregue una transformación en los objetos PathCollection que representan los puntos en la imagen. La transformación (escala) debe tener en cuenta el tamaño original.

Ninguno de estos es muy divertido, aunque el # 1 parece ser más fácil. Los scatter son especialmente desafiantes a este respecto.

Sin embargo, tengo un truco que probablemente hace lo que quieres:

 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() 

Esto da:

introduzca la descripción de la imagen aquí

Lo que parece ser lo que querías.

Los cambios:

  • scatter transformó en una plot , que cambia la escala del marcador (de ahí el sqrt ) y hace que sea imposible usar el cambio de tamaño del marcador (si se pretendía)
  • el tamaño del marcador se cambió manualmente a 6 puntos para ambos marcadores en la leyenda

Como puede ver, esto utiliza propiedades de subrayado ocultas ( _legmarker ) y es feo. Puede descomponerse en cualquier actualización en matplotlib .

Actualizar

Haa, lo encontré. Un mejor truco:

 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() 

Ahora los _sizes (otra propiedad de subrayado) hacen el truco. No es necesario tocar la fuente, a pesar de que es un truco. Pero ahora puedes utilizar todo lo que ofrece la scatter .

introduzca la descripción de la imagen aquí

De forma similar a la respuesta, asumiendo que desea que todos los marcadores tengan el mismo tamaño:

 lgnd = plt.legend(loc="lower left", scatterpoints=1, fontsize=10) for handle in lgnd.legendHandles: handle.set_sizes([6.0]) 

Con MatPlotlib 2.0.0

No tuve mucho éxito con la solución de @DrV, aunque quizás mi caso de uso sea único. Debido a la densidad de puntos, estoy usando el tamaño de marcador más pequeño, es decir, plt.plot(x, y, '.', ms=1, ...) , y quiero que los símbolos de leyenda sean más grandes.

Seguí la recomendación que encontré en los foros de matplotlib :

  1. trazar los datos (sin tags)
  2. límite de ejes de registro ( xlimits = plt.xlim() )
  3. traza datos falsos lejos de datos reales con colores y tamaños de símbolos adecuados a la leyenda
  4. restaurar los límites de los ejes ( plt.xlim(xlimits) )
  5. crear leyenda

Así es como resultó (para esto los puntos son en realidad menos importantes que las líneas): introduzca la descripción de la imagen aquí

Espero que esto ayude a alguien más.

Solo otra alternativa aquí. Esto tiene la ventaja de que no usaría ningún método “privado” y funciona incluso con otros objetos que no estén dispersos en la leyenda. La clave es asignar el PathCollection dispersión a un HandlerPathCollection con una función de actualización configurada en él.

 def update(handle, orig): handle.update_from(orig) handle.set_sizes([64]) plt.legend(handler_map={PathCollection : HandlerPathCollection(update_func=update)}) 

Ejemplo de código completo:

 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() 

introduzca la descripción de la imagen aquí

Puedes hacer un objeto Line2D que se parezca a tus marcadores elegidos, excepto con un tamaño de marcador diferente de tu elección, y usarlo para construir la leyenda. Esto es bueno porque no requiere colocar un objeto en sus ejes (lo que podría desencadenar un evento de cambio de tamaño) y no requiere el uso de ningún atributo oculto. El único inconveniente real es que tiene que construir la leyenda explícitamente a partir de listas de objetos y tags, pero esta es una función de matplotlib bien documentada, por lo que se siente bastante seguro de usar.

 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() 

Enlace a la figura resultante