matplotlib: ¿cómo anotar un punto en una flecha de dispersión colocada automáticamente?

Si hago un diagtwig de dispersión con matplotlib:

plt.scatter(randn(100),randn(100)) # set x, y lims plt.xlim([...]) plt.ylim([...]) 

Me gustaría anotar un punto dado (x, y) con una flecha apuntando hacia él y una etiqueta. Sé que esto se puede hacer con annotate , pero me gustaría que la flecha y su etiqueta se coloquen “de manera óptima” de tal manera que, si es posible (dadas las escalas / límites del eje actual), la flecha y la etiqueta no lo hagan. Se superponen con los otros puntos. por ejemplo, si desea etiquetar un punto atípico. ¿Hay alguna forma de hacer esto? no tiene que ser perfecto, sino simplemente una colocación inteligente de la flecha / etiqueta, dadas solo las coordenadas (x,y) del punto a etiquetar. Gracias.

Básicamente, no, no hay.

Los motores de diseño que manejan la colocación de tags de mapas similares a esto son sorprendentemente complejos y están fuera del scope de matplotlib. (Las intersecciones del cuadro delimitador son en realidad una forma bastante mala de decidir dónde colocar las tags. ¿Qué sentido tiene escribir una tonelada de código para algo que solo funcionará en un caso de 1000?)

Aparte de eso, debido a la cantidad de texto complejo que representa matplotlib (p. Ej., Látex), es imposible determinar la extensión del texto sin procesarlo por completo (lo que es bastante lento).

Sin embargo, en muchos casos, encontrará que usar una caja transparente detrás de la etiqueta colocada con anotar es una solución adecuada.

P.ej

 import numpy as np import matplotlib.pyplot as plt np.random.seed(1) x, y = np.random.random((2,500)) fig, ax = plt.subplots() ax.plot(x, y, 'bo') # The key option here is `bbox`. I'm just going a bit crazy with it. ax.annotate('Something', xy=(x[0], y[0]), xytext=(-20,20), textcoords='offset points', ha='center', va='bottom', bbox=dict(boxstyle='round,pad=0.2', fc='yellow', alpha=0.3), arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.5', color='red')) plt.show() 

introduzca la descripción de la imagen aquí

Use adjustText (revelación completa, lo escribí).

Vamos a etiquetar los primeros 10 puntos. El único parámetro que cambié fue reducir la fuerza de repulsión de los puntos, ya que hay muchos de ellos y queremos que el algoritmo tome un poco más de tiempo y coloque las anotaciones con más cuidado.

 import numpy as np import matplotlib.pyplot as plt from adjustText import adjust_text np.random.seed(1) x, y = np.random.random((2,500)) fig, ax = plt.subplots() ax.plot(x, y, 'bo') ts = [] for i in range(10): ts.append(plt.text(x[i], y[i], 'Something'+str(i))) adjust_text(ts, x=x, y=y, force_points=0.1, arrowprops=dict(arrowstyle='->', color='red')) plt.show() 

introduzca la descripción de la imagen aquí No es lo ideal, pero los puntos son realmente densos aquí y, a veces, no hay forma de colocar el texto cerca de su objective sin que se superpongan. Pero todo es automático y fácil de usar, y tampoco permite que las tags se superpongan entre sí.

PS Utiliza intersecciones de cuadro delimitador, pero más bien con éxito diría