2012-01-31 4 views
24

jeśli zrobię wykres punktowy z matplotlib:matplotlib: jak opisać punkt na scatter automatycznie umieszczonej strzałki?

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

chciałbym opisywać dany punkt (x, y) ze strzałką skierowaną do niego i etykiety. Wiem, że można to zrobić za pomocą annotate, ale chciałbym, aby strzałka i jej etykieta były umieszczone "optymalnie" w taki sposób, aby jeśli to możliwe (biorąc pod uwagę obecne osie/ograniczenia), strzałka i etykieta nie pokrywają się z innymi punktami. np. jeśli chcesz oznaczyć punkt odstający. czy jest jakiś sposób na zrobienie tego? nie musi to być doskonałe, ale tylko inteligentne umieszczenie strzałki/etykiety, z podaniem jedynie współrzędnych punktu, który ma być oznaczony. dzięki.

+6

Na marginesie, "scatter" nie jest przeznaczony do tego, co robisz. Użyj go, gdy chcesz wykreślić 3 lub 4-wymiarowe dane, zmieniając kolor i/lub rozmiar znaczników. Nie używaj go, gdy potrzebujesz punktów. Nie ma nic niewłaściwego w używaniu go do punktów, ale zwróci kolekcję, która jest bardziej skomplikowana niż obiekt Line2D, który "zwraca". –

Odpowiedz

33

Zasadniczo nie, nie ma.

Silniki układu, które obsługują umieszczanie podobnych etykiet na mapie, są zaskakująco skomplikowane i wykraczają poza zakres matplotlib. (Skrzyżowania skrzynek z ograniczeniami w rzeczywistości są raczej kiepskim sposobem decydowania o tym, gdzie umieścić etykiety.) Jaki sens ma napisanie tony kodu dla czegoś, co działa tylko w jednym przypadku na 1000?)

Inne niż z powodu ilość złożonego renderowania tekstu wykonywanego przez matplotlib (np. latex), niemożliwe jest określenie zakresu tekstu bez pełnego renderowania go (co jest raczej powolne).

Jednak w wielu przypadkach okaże się, że użycie przezroczystego pudełka za etykietą umieszczoną z adnotacją jest odpowiednim rozwiązaniem.

E.g.

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

enter image description here

+2

Podoba mi się twój przykład. Można również umieścić adnotację poza ramką, użyj "ułamka osi" na przykład dla "textcoordów". Automatyczny charakter umieszczania można zawinąć w małą metodę, która tworzy adnotację. Jeśli punkt jest najbliższy środkowi dolnemu, umieść go poniżej osi X; jeśli jest bliżej prawej krawędzi, umieść ją tam, może nawet automatycznie obracając ... Mimo to umieszczenie będzie wymagać odrobiny finezji, ponieważ może ukryć etykiety zaznaczenia lub zostać przycięte przez krawędź figury itp. ... – Yann

+0

@Joe: Jak decydujecie, jakie wartości podać "xytext" (w tym przypadku '(-20,20)')? – user248237dfsf

+1

@ user248273 - To jest przesunięcie w punktach. Jest to arbitralne, ale nie zależy od twoich zakresów danych. Zauważ "textcoords" kwarg = "punkty odsunięcia". Przekazywanie różnych wartości dla 'textcoords' kontroluje sposób interpretowania liczb w' xytext'. –

2

Zastosowanie adjustText (pełne ujawnienie, napisałem go).

Oznaczmy etykietą pierwsze 10 punktów. Jedynym parametrem, który zmieniłem, było obniżenie siły odpychania z punktów, ponieważ jest ich tak dużo i chcemy, aby algorytm zajmował nieco więcej czasu i dokładniej umieszczał adnotacje.

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

enter image description here To nie jest idealne, ale punkty są tu naprawdę gęsta i czasami nie ma sposobu, aby umieścić tekst blisko swojego celu bez nakładania żadnego z nich. Ale to wszystko jest automatyczne i łatwe w użyciu, a także nie pozwala na nakładanie się etykiet.

PS Posługuje się prostymi przecięciami, ale raczej powiem!