Inteligentne umieszczanie etykiet punktowych w R.

102

1) Czy jest jakaś biblioteka / funkcja R, która implementowałaby INTELIGENTNE umieszczanie etykiet na wykresie R? Wypróbowałem kilka, ale wszystkie są problematyczne - wiele etykiet nakłada się albo na siebie, albo na inne punkty (lub inne obiekty na wykresie, ale widzę, że jest to znacznie trudniejsze w obsłudze).

2) Jeśli nie, to czy jest jakikolwiek sposób, jak WYGODNIE pomóc algorytmowi w umieszczeniu etykiety dla poszczególnych problematycznych punktów? Pożądane najbardziej wygodne i wydajne rozwiązanie.

Możesz zagrać i przetestować inne możliwości na moim odtwarzalnym przykładzie i sprawdzić, czy jesteś w stanie osiągnąć lepsze wyniki niż ja:

# data
x = c(0.8846, 1.1554, 0.9317, 0.9703, 0.9053, 0.9454, 1.0146, 0.9012, 
0.9055, 1.3307)
y = c(0.9828, 1.0329, 0.931, 1.3794, 0.9273, 0.9605, 1.0259, 0.9542, 
0.9717, 0.9357)
ShortSci = c("MotAlb", "PruMod", "EriRub", "LusMeg", "PhoOch", "PhoPho", 
"SaxRub", "TurMer", "TurPil", "TurPhi")

# basic plot
plot(x, y, asp=1)
abline(h = 1, col = "green")
abline(v = 1, col = "green")

W przypadku etykietowania wypróbowałem następnie te możliwości, nikt nie jest naprawdę dobry:

1) ten jest okropny:

text(x, y, labels = ShortSci, cex= 0.7, offset = 10)

2) ten jest dobry, jeśli nie chcesz umieszczać etykiet dla wszystkich punktów, ale tylko dla wartości odstających, ale nadal etykiety są często umieszczane nieprawidłowo:

identify(x, y, labels = ShortSci, cex = 0.7)

3) ten wyglądał obiecująco, ale jest problem z etykietami, które są zbyt blisko punktów; Musiałem wypełnić je spacjami, ale to niewiele pomaga:

require(maptools)
pointLabel(x, y, labels = paste("  ", ShortSci, "  ", sep=""), cex=0.7)

4)

require(plotrix)
thigmophobe.labels(x, y, labels = ShortSci, cex=0.7, offset=0.5)

5)

require(calibrate)
textxy(x, y, labs=ShortSci, cx=0.7)

Z góry dziękuję!

EDYCJA: todo: spróbuj labcurve {Hmisc} .

TMS
źródło
2
Niestety, odpowiedzi na pytania w języku R wydają się być równo podzielone między StackOverflow i CrossValidated. W tym przypadku pytanie jest duplikatem pytania sprzed 4 dni .
Ed Staub
3
Wpadłem na podobny problem i napisałem podstawowy pakiet, który wykorzystuje symulację pola siłowego do dostosowania położenia obiektu. Chociaż możliwe jest wiele ulepszeń, w tym integracja z ggplot itp., Wydaje się, że zadanie zostało wykonane. Poniżej przedstawiono funkcjonalność. Jeśli ktoś install.packages("FField") library(FField) FFieldPtRepDemo()
wpadnie
Czy mogę prosić o wypróbowanie ggrepel ?
Kamil Slowikowski
drogi @Joran, proszę wstaw swój komentarz "6) W przypadku wykresów ggplot2 dostępna jest nowa opcja o nazwie ggrepel, którą wielu ludzi lubi." w komentarzu lub odpowiedzi. Tutaj zamieściłem tylko listę opcji, które wypróbowałem, ale nie są satysfakcjonujące . Jeśli jest to coś, co działa dobrze, to powinno być w odpowiedzi.
TMS

Odpowiedzi:

49

Po pierwsze, oto wyniki mojego rozwiązania tego problemu:

wprowadź opis obrazu tutaj

Zrobiłem to ręcznie w podglądzie (bardzo podstawowa przeglądarka plików PDF / obrazów w systemie OS X) w ciągu zaledwie kilku minut. ( Edycja: Przepływ pracy był dokładnie taki, jakiego można się spodziewać: zapisałem wykres jako plik PDF z R, otworzyłem go w podglądzie i utworzyłem pola tekstowe z żądanymi etykietami (9 pkt Helvetica), a następnie po prostu przeciągałem je myszą, aż wyglądały dobrze. Następnie wyeksportowałem do PNG w celu przesłania do SO.)

Teraz, zanim ulegniesz silnej potrzebie odrzucenia, zagłosuj na to w zapomnienie i zostawisz złośliwe komentarze o tym, jak chodzi o zautomatyzowanie tego procesu, wysłuchaj mnie!

Szukanie rozwiązań algorytmicznych jest w porządku i (IMHO) naprawdę interesujące. Jednak według mnie sytuacje związane z oznaczaniem punktów można podzielić z grubsza na trzy kategorie:

  1. Masz małą liczbę punktów, ale żadne nie są tak blisko siebie . W takim przypadku jedno z rozwiązań wymienionych w pytaniu prawdopodobnie będzie działać przy dość minimalnych modyfikacjach.
  2. Masz niewielką liczbę punktów, z których niektóre są zbyt blisko upakowane, aby typowe rozwiązania algorytmiczne dawały dobre wyniki . W tym przypadku, ponieważ tylko masz małą liczbę punktów, znakowanie je ręcznie (albo za pomocą edytora obrazu lub dostrajasz wywołanie text), nie jest to dużo wysiłku.
  3. Masz dość dużą liczbę punktów . W tym przypadku naprawdę nie powinieneś ich etykietować, ponieważ trudno jest wizualnie przetworzyć dużą liczbę etykiet.

: wchodzenie na mydelniczkę:

Ponieważ ludzie tacy jak my uwielbiają automatyzację, myślę, że często wpadamy w pułapkę myślenia, że ​​prawie każdy aspekt tworzenia dobrej grafiki statystycznej powinien być zautomatyzowany. Z całym szacunkiem (pokorą!) Się nie zgadzam.

Nie ma doskonale ogólnego środowiska do tworzenia statystyk, które automagicznie tworzy obraz, który masz w głowie. Rzeczy takie jak R, ggplot2, lattice itp. Wykonują większość pracy; ale ta dodatkowa drobna zmiana, dodanie linii tutaj, dostosowanie tam marginesu, prawdopodobnie lepiej pasuje do innego narzędzia.

: zejście z mydelniczki:

Chciałbym również zauważyć, że myślę, że wszyscy moglibyśmy wymyślić wykresy rozrzutu z mniej niż 10-15 punktami, które będą prawie niemożliwe do wyczyszczenia, nawet ręcznie, i prawdopodobnie zepsują każde automatyczne rozwiązanie, które ktoś wymyśli.

Na koniec chcę powtórzyć, że wiem, że to nie jest odpowiedź, której szukasz. I nie mówię, że próby algorytmiczne są bezużyteczne lub głupie. Głosowałem za tym pytaniem i z radością zagłosuję za ciekawymi rozwiązaniami algorytmicznymi!

Powodem, dla którego opublikowałem tę odpowiedź, jest to, że myślę, że to pytanie powinno być kanonicznym pytaniem o oznaczenie punktu w R dla przyszłych duplikatów i myślę, że rozwiązania obejmujące ręczne etykietowanie zasługują na miejsce przy stole, to wszystko.

joran
źródło
10
Innym sposobem ręcznym jest zapisanie wykresu jako SVG i edycja za pomocą Inkscape, a następnie utworzenie z tego pliku PDF.
Spacedman
Cześć joran, dziękuję za odpowiedź. OK, akceptuję to rozwiązanie, chociaż myślę, że komputer powinien to zrobić najlepiej, a następnie poprosić o ręczną interwencję. Tutaj szukam najbardziej wygodnego i szybkiego rozwiązania. Czy mógłbyś opisać krok po kroku, jak stworzyłeś fabułę? Co wygenerowałeś w R, eksportujesz, przenosisz etykiety w podglądzie itp.?
TMS
1
@TomasT. Rozumiem. W takim razie „oszukiwałem”. Wygenerowałem jeden plik PDF z etykietami, używając jednej z powyższych metod, a drugą bez, i użyłem tej z etykietami jako przewodnika.
joran
1
+1 To świetna odpowiedź. Pewne wyjaśnienie, dlaczego pojawia się w meta-CV : zobacz tam komentarze.
whuber
1
Ręczne przenoszenie niewielkiego zestawu etykiet wydaje się rozsądne, ale równie dobrze możesz najpierw utworzyć je automatycznie , a następnie przenieść. W ten sposób oszczędzasz sobie dużo pracy, a także zmniejszasz prawdopodobieństwo błędnego oznakowania ...
nic 101
42

ggrepelwygląda obiecująco po zastosowaniu do ggplot2wykresów rozrzutu.

# data
x = c(0.8846, 1.1554, 0.9317, 0.9703, 0.9053, 0.9454, 1.0146, 0.9012, 
0.9055, 1.3307)
y = c(0.9828, 1.0329, 0.931, 1.3794, 0.9273, 0.9605, 1.0259, 0.9542, 
0.9717, 0.9357)
ShortSci = c("MotAlb", "PruMod", "EriRub", "LusMeg", "PhoOch", "PhoPho", 
"SaxRub", "TurMer", "TurPil", "TurPhi")


df <- data.frame(x = x, y = y, z = ShortSci)
library(ggplot2)
library(ggrepel)

ggplot(data = df, aes(x = x, y = y)) + theme_bw() + 

    geom_text_repel(aes(label = z), 
       box.padding = unit(0.45, "lines")) +

    geom_point(colour = "green", size = 3)

wprowadź opis obrazu tutaj

Sandy Muspratt
źródło
10

Czy wypróbowałeś pakiet directlabels ?

A tak przy okazji, argumenty pos i offset mogą przyjmować wektory, aby umożliwić umieszczenie ich we właściwych pozycjach, gdy istnieje rozsądna liczba punktów w zaledwie kilku przebiegach wykresu.

Jan
źródło
Czy pakiet directlabels może być używany z normalnym plot()drukowaniem? Nie udało mi się spróbować, więc ... Dzięki! PS: @SpacedMan & Ben, wyczyściłem swoje komentarze dotyczące aktualizacji R, ponieważ nie są one aż tak interesujące - możesz zrobić to samo.
TMS
6

Znalazłem rozwiązanie! Niestety nie jest to ostateczne i idealne rozwiązanie, ale teraz działa najlepiej dla mnie. Jest w połowie algorytmiczny, w połowie ręczny, więc oszczędza czas w porównaniu do czysto ręcznego rozwiązania naszkicowanego przez jorana.

Przeoczyłem bardzo ważną część ?identifypomocy!

Algorytm używany do umieszczania etykiet jest taki sam, jak używany przez tekst, jeśli określono tam pozycję, z tą różnicą, że położenie wskaźnika względem zidentyfikowanego punktu określa pozycję w identyfikacji.

Jeśli więc korzystasz z identify()rozwiązania, które napisałem w moim pytaniu, możesz wpłynąć na pozycję etykiety, nie klikając bezpośrednio w tym punkcie, ale klikając obok tego punktu w odpowiednim kierunku !!! Działa po prostu świetnie!

Wadą jest to, że są tylko 4 pozycje (górna, lewa, dolna, prawa), ale bardziej doceniłbym pozostałe 4 (lewy górny, prawy górny, lewy dolny, prawy dolny) ... użyj tego do oznaczenia punktów, w których mi to nie przeszkadza, i pozostałych punktów, które oznaczam bezpośrednio w mojej prezentacji Powerpoint, zgodnie z propozycją jorana :-)

PS: Nie wypróbowałem jeszcze rozwiązania directlabels lattice / ggplot, nadal wolę korzystać z podstawowej biblioteki wykresów.

TMS
źródło
4

Proponuję przyjrzeć się wordcloudpaczce. Wiem, że ten pakiet koncentruje się nie dokładnie na punktach, ale na samych etykietach, a także wydaje się, że styl jest raczej ustalony. Mimo to wyniki, które uzyskałem, były oszałamiające. Należy również zauważyć, że wersja pakietu, o której mowa, została wydana mniej więcej w czasie, gdy zadałeś pytanie, więc nadal jest bardzo nowa.

http://blog.fellstat.com/?cat=11

maj
źródło
3

Napisałem funkcję języka R wywołaną addTextLabels()w pakiecie basicPlotteR. Pakiet można zainstalować bezpośrednio w bibliotece języka R przy użyciu następującego kodu:

install.packages("devtools")
library("devtools")
install_github("JosephCrispell/basicPlotteR")

W podanym przykładzie użyłem następującego kodu, aby wygenerować przykładową figurę, do której link znajduje się poniżej.

# Load the basicPlotteR library
library(basicPlotteR)

# Create vectors storing the X and Y coordinates
x = c(0.8846, 1.1554, 0.9317, 0.9703, 0.9053, 0.9454, 1.0146, 0.9012, 
      0.9055, 1.3307)
y = c(0.9828, 1.0329, 0.931, 1.3794, 0.9273, 0.9605, 1.0259, 0.9542, 
      0.9717, 0.9357)

# Store the labels to be plotted in a vector
ShortSci = c("MotAlb", "PruMod", "EriRub", "LusMeg", "PhoOch", "PhoPho", 
             "SaxRub", "TurMer", "TurPil", "TurPhi")

# Plot the X and Y coordinates without labels
plot(x, y, asp=1)
abline(h = 1, col = "green")
abline(v = 1, col = "green")

# Add non-overlapping text labels
addTextLabels(x, y, ShortSci, cex=0.9, col.background=rgb(0,0,0, 0.75), 
              col.label="white")

Działa poprzez automatyczne wybieranie alternatywnej lokalizacji z drobnej siatki punktów. Najbliższe punkty siatki są odwiedzane jako pierwsze i wybierane, jeśli nie pokrywają się z żadnymi wykreślonymi punktami lub etykietami. Jeśli jesteś zainteresowany, spójrz na kod źródłowy .

Przykładowy rysunek

Joseph Crispell
źródło
2

Brak odpowiedzi, ale zbyt długi czas na komentarz. Bardzo prostym podejściem, które może działać na prostych przypadkach, gdzieś pomiędzy post-processingiem jorana a bardziej wyrafinowanymi algorytmami, które zostały zaprezentowane, jest stworzeniein-place jorana prostych transformacji w ramce danych.

Ilustruję to, ggplot2ponieważ jestem bardziej zaznajomiony z tą składnią niż bazowe wykresy R.

df <- data.frame(x = x, y = y, z = ShortSci)
library("ggplot2")
ggplot(data = df, aes(x = x, y = y, label = z)) + theme_bw() + 
    geom_point(shape = 1, colour = "green", size = 5) + 
    geom_text(data = within(df, c(y <- y+.01, x <- x-.01)), hjust = 0, vjust = 0)

Jak widać, w tym przypadku wynik nie jest idealny, ale do niektórych celów może być wystarczający. I jest to dość łatwe, zazwyczaj wystarczy coś takiegowithin(df, y <- y+.01)

wprowadź opis obrazu tutaj

Patrick T.
źródło
2
Zamiast modyfikować sposób dfużytkowania within, często robię to dostosowując estetykę: geom_text(aes(x = x - .01, y = y + .01), hjust = 0, vjust = 0)wydaje się czystszy.
Gregor Thomas