outside legends examples example python legend scatter-plot

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



scatter plot python example (5)

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:

Los tamaños de los puntos en la leyenda son escalados pero no iguales. ¿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 ?


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 etiquetas)
  2. límite de ejes de grabación ( 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):

Espero que esto ayude a alguien más.


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 (potencialmente desencadenando un evento de cambio de tamaño) y no requiere el uso de ningún atributo oculto. El único inconveniente real es que tienes que construir la leyenda explícitamente a partir de listas de objetos y etiquetas, 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


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 que se establece 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()


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 ( incorrecto: 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 hack 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:

Lo que parece ser lo que querías.

Los cambios:

  • scatter convirtió en un plot , que cambia la escala del marcador (por lo tanto, 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 para que sea de 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 usar todo lo que ofrece la scatter .