ggplot2 buduje wykresy z warstw
ggplot2 to najczęściej używany pakiet R do wykresów, a ludzie przysięgają na niego z powodu jednego pomysłu projektowego: gramatyki grafiki (grammar of graphics). Bazowy R daje jedną funkcję na wykres (plot(), hist(), barplot()), każdą z własnymi argumentami. ggplot2 daje zamiast tego mały zestaw łączonych elementów (zbiór danych, mapowanie kolumn na właściwości wizualne i warstwy rysujące), które przez + składają się w dowolny wykres. Naucz się gramatyki raz, a każdy nowy typ wykresu to to samo zdanie z jednym zmienionym słowem. W dodatku wartości domyślne (szary panel, rozsądne osie, automatyczne legendy) wyglądają przyzwoicie bez żadnej stylizacji.
To pakiet dodatkowy, więc wymaga jednorazowej instalacji i wczytania w każdej sesji:
install.packages("ggplot2") # once, on your machine
library(ggplot2) # at the top of every script that uses it
Jedno oczekiwanie warto znać od początku: ggplot2 chce ramki danych (a nie luźnych wektorów), najlepiej w uporządkowanym długim formacie: jeden wiersz na obserwację, jedna kolumna na zmienną. Jeśli dane są szerokie, przekształcenie ich do formatu długiego zwykle rozwiązuje coś, co wyglądało na problem z wykresem.
Model myślowy: dane, mapowanie, geometria
Każdy ggplot odpowiada na trzy pytania, po jednym na część:
- Dane: która ramka danych?
ggplot(mtcars, ...) - Mapowanie: które kolumny sterują którymi właściwościami wizualnymi?
aes(x = wt, y = hp) - Geometria: jak narysować obserwacje?
+ geom_point()
Oto anatomia, element po elemencie:
ggplot(mtcars, aes(x = wt, y = hp)) +
geom_point()
ggplot(mtcars, ...)zaczyna wykres, którego domyślne dane tomtcars. Sam w sobie rysuje pusty szary panel, bo nie ma jeszcze warstw.aes(x = wt, y = hp)to mapowanie estetyk: pozycja pozioma pochodzi z kolumnywt, pionowa zhp. Zwróć uwagę, że piszesz gołe nazwy kolumn, a niemtcars$wt, bo mapowanie jest obliczane wewnątrz podanych danych.+ geom_point()dodaje warstwę, która rysuje każdy wiersz jako punkt.+to naprawdę dodawanie warstw: wykres to obiekt, który budujesz, i możesz dalej dodawać kolejny geom, etykiety czy motyw, każdy przez kolejny+.
Po uruchomieniu dostaniesz ten sam wykres punktowy masy względem mocy co wersja w bazowym R z przewodnika po wykresie punktowym, na szarym panelu z białą siatką. Zamień geom_point() na geom_line(), a to samo mapowanie stanie się wykresem liniowym. Tak właśnie działa gramatyka.
Geomy, których naprawdę będziesz używać
Kilkanaście geomów pokrywa prawie całą codzienną pracę:
geom_point() # scatter plot: two numeric variables
geom_line() # line chart: trends over an ordered variable
geom_col() # bar chart from pre-computed heights (needs y)
geom_bar() # bar chart that counts raw rows for you (no y)
geom_histogram() # distribution of one numeric variable (set binwidth)
geom_boxplot() # distributions compared across groups
geom_smooth() # fitted trend line; method = "lm" for straight
Warstwy się nakładają i tu ggplot2 zaczyna wygrywać z bazowym R pod względem ekspresji: geom_point() + geom_smooth(method = "lm") to wykres punktowy z linią regresji i pasmem ufności w jednej czytelnej linii. Ponieważ wykresy to obiekty, możesz też zapisać bazę i rozgałęziać się od niej:
p <- ggplot(mtcars, aes(x = wt, y = hp)) + geom_point()
p + geom_smooth(method = "lm") # one variant
p + geom_smooth() # another (loess curve)
Mapowanie a ustawianie: klasyczne zamieszanie z aes()
Jeden błąd, który popełnia każdy początkujący w ggplot2, zwykle w pierwszym tygodniu: wstawienie dosłownego koloru do wnętrza aes(). Zasada jest krótka: wewnątrz aes() oznacza "zmienia się z danymi"; na zewnątrz oznacza "stałe ustawienie", a oba miejsca robią zupełnie różne rzeczy:
# MAPPING: color varies by a column - each cylinder count gets its own
# color, and a legend appears automatically
ggplot(mtcars, aes(x = wt, y = hp, color = factor(cyl))) +
geom_point(size = 2)
# SETTING: every point is literally steelblue - no legend, no mapping
ggplot(mtcars, aes(x = wt, y = hp)) +
geom_point(color = "steelblue", size = 2)
A sama pułapka:
# WRONG: "blue" inside aes() is treated as data, not as a color
ggplot(mtcars, aes(x = wt, y = hp, color = "blue")) +
geom_point()
To rysuje punkty w kolorze łososiowym z legendą, której jedyny wpis nazywa się "blue". Dlaczego: aes() mapuje kolumny na właściwości, więc color = "blue" tworzy sztuczną jednokategoryjną kolumnę, w której wszędzie jest słowo "blue", a ggplot przypisuje tej kategorii swój pierwszy domyślny kolor, który akurat jest czerwonawym łososiowym. Jeśli chodziło o "zrób punkty niebieskie", kolor trafia poza mapowanie: geom_point(color = "blue"). Ta sama zasada dotyczy size, shape, fill i alpha. (Zwróć też uwagę na factor(cyl) w przykładzie z mapowaniem: cyl jest liczbowe, a owinięcie go w factor() prosi o osobne kolory dla grup zamiast ciągłego gradientu.)
Etykiety, panele i motywy
labs() nazywa wszystko w jednym miejscu, a wykres nie jest gotowy, dopóki nie ma prawdziwych etykiet:
ggplot(mtcars, aes(x = wt, y = hp, color = factor(cyl))) +
geom_point(size = 2) +
labs(title = "Horsepower vs. weight",
x = "Weight (1000 lbs)",
y = "Horsepower",
color = "Cylinders")
Panele (facets) to sztandarowa funkcja ggplot2: dzielą jeden wykres na małe wielokrotności, po jednym panelu na grupę, ze wspólnymi osiami, żeby panele dało się uczciwie porównywać:
ggplot(mtcars, aes(x = wt, y = hp)) +
geom_point() +
facet_wrap(~ cyl)
Pojawiają się trzy panele, po jednym dla samochodów 4-, 6- i 8-cylindrowych, ułożone w siatkę z wartością grupy podpisaną nad każdym panelem. W bazowym R to żonglowanie par(mfrow) z ręcznie ustawianymi wspólnymi zakresami; tutaj to jedna linia. Panele nagradzają uporządkowane dane, dlatego ggplot2 tak naturalnie łączy się z potokiem dplyr: przefiltruj i podsumuj, a potem przekaż wynik prosto do ggplot().
Motywy zmieniają styl wszystkiego, co nie jest danymi, jednym wywołaniem: + theme_minimal() (czyste białe tło), + theme_bw(), + theme_classic() (tylko osie, bez siatki) albo domyślny szary. Wybierz jeden na projekt i trzymaj się go: raport, w którym każdy wykres ma ten sam motyw, czyta się jak jeden dokument, a nie jak album z wycinkami.
Zapisywanie przez ggsave() i kiedy bazowy R wciąż wystarcza
ggsave() zapisuje ostatni wykres na dysk i rozpoznaje format po rozszerzeniu pliku:
ggsave("hp-vs-weight.png", width = 8, height = 5) # last plot shown
ggsave("hp-vs-weight.pdf", plot = p, width = 8, height = 5) # a saved object
width i height są domyślnie w calach; ustawiaj je jawnie, bo rozmiar czcionki skaluje się z wymiarami wyjścia, a wartości domyślne rzadko pasują do miejsca docelowego wykresu.
A kiedy w ogóle warto: na pięciosekundowe spojrzenie na jedną zmienną w trakcie analizy plot() i pokrewne funkcje bazowego R wymagają mniej pisania i żadnych zależności. Nie ma nagrody za używanie ggplot2 wszędzie. ggplot2 pokazuje swoją wartość, gdy tylko wykres obejmuje grupy, legendy, panele albo odbiorców: gramatyka skaluje się do złożoności, z którą wykresy bazowe radzą sobie tylko z wysiłkiem.
Co warto zapamiętać
- ggplot2 składa każdy wykres z danych + mapowania
aes()+ warstw geom połączonych przez+; naucz się gramatyki raz i używaj jej dla każdego typu wykresu. aes()mapuje kolumny na właściwości wizualne: gołe nazwy kolumn, obliczane w twojej ramce danych.- Wewnątrz
aes()= zmienia się z danymi (i dostaje legendę); na zewnątrz = stałe ustawienie.aes(color = "blue")to klasyczny błąd. labs()do tytułów,facet_wrap(~ group)do małych wielokrotności,theme_minimal()i pokrewne do stylu,ggsave()z jawnymi wymiarami do eksportu.- Szybki podgląd dla siebie: bazowy R. Grupy, panele albo odbiorcy: ggplot2.
To zamyka rozdział o wykresach. Naturalnym następnym krokiem są statystyki opisowe, czyli liczby opisujące kształty, które pokazują te wykresy.
Najczęściej zadawane pytania
Czym jest ggplot2 w R?
ggplot2 to najpopularniejszy pakiet R do tworzenia wykresów. Zamiast jednej funkcji na każdy typ wykresu składasz każdy wykres z tych samych trzech elementów: ramki danych, mapowania aes() kolumn na właściwości wizualne i jednej lub więcej warstw geom_*(), połączonych przez +. Jest częścią tidyverse.
Jak zainstalować ggplot2?
Uruchom raz install.packages("ggplot2"), a potem library(ggplot2) na początku każdego skryptu, który z niego korzysta. Instalacja umieszcza pakiet na komputerze; library() wczytuje go do bieżącej sesji.
Co robi aes() w ggplot2?
aes() mapuje kolumny danych na właściwości wizualne: aes(x = wt, y = hp, color = factor(cyl)) mówi, że pozycja pozioma pochodzi z wt, pionowa z hp, a kolor punktu z liczby cylindrów. Wszystko wewnątrz aes() zmienia się razem z danymi; wszystko poza nim to stałe ustawienie.
Dlaczego punkty w ggplot nie mają koloru, o który proszę?
Kolor trafił do wnętrza aes(): aes(color = "blue") nie ustawia koloru, tylko mapuje zmyśloną kategorię o nazwie "blue", więc ggplot wybiera własny domyślny kolor i dodaje legendę. Żeby ustawić dosłowny kolor, umieść go poza mapowaniem: geom_point(color = "blue").