--- title: "Wprowadzenie do ggplot2" output: pdf_document: default html_document: default --- `ggplot2` jest jednym z najczęściej wykorzystywanych pakietów do tworzenia grafiki w R. Pozwala tworzyć w łatwy sposób dość skomplikowane wykresy, posiada również dość rozsądne ustawienia standardowe, dzieki którym możemy uzyskać estetycznie wyglądające ilustracje małym nakładem pracy. Nie jest on dołączy do podstawowej dystrybucji R i należy zainstalować go za pomocą polecenia `install.packages`. ```{r} # install.packages('ggplot2') # instalacja ggplot2 library(ggplot2) ``` Praca z `ggplot2` różni się od pracy z grafiką w standardowym R. Podstawowe różnice to: 1. Preferowana jest praca z ramkami danych, raczej nie zajdziemy za daleko pracując na pojedynczych wektorach. 2. Przestaje obowiązywać metafora płótna. Rysując w standardowym R mogliśmy podchodzić do tego jak do nakładania kolejnych linii, punktów, segmentów itp. za pomocą funkcji niskiego poziomu. W przypadku `ggplot2` raczej definiujemy wykres, nie przejmujemy się najczęściej niskopoziomowymi szczegółami. 3. Opiera się na teoretycznej koncepcji *grammar of graphics* i stara się ją implementować. 4. Wykresy tworzymy dodając (`+` - `ggplot2` przeciąża operator dodawania) do siebie obiekty reprezentujące "warstwy" albo "mapowania" w specyficznej "gramatyce grafiki" implementowanej przez `ggplot2`. 5. Inaczej zapisujemy grafikę - używamy funkcji `ggsave`. **Co będziemy omawiać:** 1. Zarys możliwości pakietu. 2. Tworzenie wykresów za pomocą `qplot` oraz gramatyki grafiki (mapowanie). 3. Różne rodzaje wykresów dla jednej lub dwóch zmiennych nominalnych i liczbowych. 4. Kilkupanelowe wykresy. **Co nie będzie omawiane, ale jest ważną czescią pakietu `ggplot2`:** 1. Funkcje niskiego poziomu do rysowania kształtów. 2. Osie, skale. 3. Podpisy, napisy, przypisy (elementy tekstowe). 4. Drobnoziarnistwa kontrola. Innymi słowy - w `ggplot2` możemy zmieniać detale tworzonych przez nas wykresów, ale nie będziemy omawiać każdej służącej do tego funkcji i jej argumentu. Informacje można łatwo znaleźć w Internecie, dokumentacja rozmaitych funkcji bogata jest też w przykłady. # `qplot` - funkcja do szybkiego tworzenia wykresów (*quickplot*) `qplot` jest *wrapperem* (można myśleć: skrótem) na inne funkcje `ggplot2`, który pozwala nam bardzo szybko i bez potrzeby żmudnego definiowania mapowan tworzyć proste wykresy. Z założenia funkcja `qplot` nie ma generować bardzo złożonych wykresów, lecz estetycznie wyglądające bardzo podstawowe typy wykresów. Przypomnijmy sobie jak wyglądały dane ze zbioru `beaver1` - będziemy na nich pracować. ```{r} head(beaver1) ``` W przypadku, gdy w funkcji `qplot` podamy tylko jedną zmienną (`x`), `ggplot2` wyprodukuje domyślnie histogram. Zmienne przekazujemy **bez cudzysłowiów** i są nimi **nazwy kolumn** w naszej ramce danych. Podajemy również argument `data`, w którym przekazujemy zmienną, w której znajduje się ramka danych, której chcemy używać. ```{r} qplot(x = temp, data = beaver1) ``` Gdy podamy dwie zmienne - `x` oraz `y` - domyślnym rodzajem wykresu będzie wykres punktowy (*scatterplot*). ```{r} qplot(x = time, y = temp, data = beaver1[beaver1$day == 346,]) ``` Przekazując kolumnę z wektorem typu `factor` jako argument `color` możemy ustalić inne kolory ze względu na wartość tej kolumny. W poniższym przypadku punkty dotyczące obserwacji w obu dniach oznaczone są innymi kolorami. Warto zauważyć, że `ggplot2` automatycznie stworzył legendę (czego nie robią funkcje wbudowane w R!). ```{r} beaver1$dzien <- as.factor(beaver1$day) qplot(x = time, y = temp, color = dzien, data = beaver1) ``` Za pomocą argumentu `geom` możemy zmienić typ wykresu. Listę wszystkich możliwości z łatwością znajdą Państwo w internecie. Pokażemy sobie jednak kilka z nich. Ustawiając argument `geom` na `"line"` możemy stworzyć wykres liniowy. ```{r} qplot(x = time, y = temp, geom = 'line', data = beaver1[beaver1$day == 346,]) ``` `"step"` stworzy wykres schodkowy. ```{r} qplot(x = time, y = temp, geom = 'step', data = beaver1[beaver1$day == 346,]) ``` Jeśli do funkcji `plot` przekażemy dwie zmienne, z których jedna będzie kategorialna (`factor`), to `ggplot2` stworzy jeden ze znanych Państwu typów wykresów - na przykład wykres budełkowy. Jak jednak widzimy w przypadku wykresu pudełkowego argument `color` nie zadziałał tak, jak byśmy chcieli - pokolorował tylko linie na wykresie. ```{r} qplot(x=dzien, y = temp, color = dzien, geom = 'boxplot', data = beaver1) ``` Jeżeli chcielibyśmy pokolorować również wypełnienie, musielibyśmy użyć argumentu `fill`. ```{r} qplot(x=dzien, y = temp, fill = dzien, geom = 'violin', data = beaver1) ``` # Tworzenie wykresów w języku grafiki `ggplot2` `qplot` przydaje się przede wszystkim wtedy, kiedy chcemy stworzyć wykres *szybko* lub *nie jest skomplikowany*. Nie pozwala jednak wykorzystać wszystkich możliwości, jakie ma ten pakiet. Spróbujmy więc stworzyć wykres za pomocą "gramatyki grafiki" jaki implementuje `ggplot2`. ## Jedna zmienna (lub dwie zmienne i jedna dyskretna) Spróbujmy na początku stworzyć obiekt reprezentujący nasz wykres. Robimy to za pomocą funkcji `ggplot`. ```{r} ggplot(data = beaver1) ``` Otrzymaliśmy puste płótno. Dlaczego? Oczywiście nie ustaliliśmy co ma być na osi rzędnych, co na odciętych. Stwórzmy więc obiekt, w którym przekażemy nasze mapowanie. Odpowiada za to argument `mapping` (będziemy go dalej pomijać i przekazywać wartości pozycyjnie), w którym przekazujemy obiekt stworzomy za pomocą funkcji `aes`. ```{r} ggplot(data = beaver1, mapping = aes(x = temp)) ``` Mamy już wykres, mamy już mapowanie - nawet dostaliśmy odpowiednie oznaczenia na osi. Nie mamy jednak na nim żadnego kształtu! Spróbujmy dodać do naszego wykresu jakiś kształt. Robimy to za pomocą funkcji `geom_rodzaj_kształtu`, którą "dodajemy" do już istniejącego obiektu. ```{r} ggplot(data = beaver1, aes(x = temp)) + geom_histogram() ``` Stworzyliśmy histogram! Jak Państwo widzą wygląda chyba troszkę ładniej niż standardowy histogram w R. No dobrze, ale czy możemy dodać jakiś inny kształt? Spróbujmy dodać małe kreseczki reprezentujące poszczególne obserwacje za pomocą `geom_rug`. ```{r} ggplot(data = beaver1, aes(x = temp)) + geom_histogram() + geom_rug() ``` Wygląda to całkiem nieźle! Mogą się Państwo zastanawiać skąd `ggplot2` wie, co i gdzie powinien narysować wówczas, gdy "dodajemy" kolejne kształty? `ggplot2` uwzględnia dorysowując do wykresu kolejne kształte mapowanie (`aes`) odziedziczone z głównego wykresu (= obiektu stworzonego przez funckję `ggplot`). Dlatego właśnie możemy po prostu dodawać kolejne kształty. Nie musimy godzić się na mapowanie "odziedziczone" z głównego wykresu. Dla każdego kształtu (*geoma*) możemy mapowanie zmienić lub uzupełnić o dodatkowe elementy. W tym celu używamy funkcji `aes`. W poniższym przykładzie użyliśmy wbudowanego w `ggplot2` słowa kluczowego `..density..` za pomocą którego zmieniliśmy wartości na osi y. ```{r} ggplot(data = beaver1, aes(x = temp)) + geom_histogram(aes(y = ..density..)) + geom_rug() ``` Chociaż kolejne geomy dziedziczą mapowanie po pierwszym obiekcie, to możemy je oczywiście nadpisać. W tym przypadku podobnie jak w poprzednich dodaliśmy do naszego histogramu kreseczki za pomocą `geom_rug`, tym razem jednak dodaliśmy dodatkowe mapowanie - zmienna `dzien` ma być mapowana na kolor kreseczek. ```{r} ggplot(data = beaver1, aes(x = temp)) + geom_histogram(aes(y = ..density..)) + geom_rug(aes(color = dzien)) ``` Bez trudu możemy dodać jądrowy estymator gęstości dodając po prostu kolejny *geom* - tym razem `geom_density`. Proszę zwrócić uwagę, że zmieniliśmy jeszcze mapowanie kreseczek w `geom_rug`. `time` jest zmienną ciągłą (w miarę), więc `ggplot2` automatycznie dobrał ciągłą skalę kolorów i stworzył odpowiednią legendę. ```{r} ggplot(data = beaver1, aes(x = temp)) + geom_histogram(aes(y = ..density..)) + geom_rug(aes(color = time)) + geom_density() ``` `ggplot2` udostępnia oczywiście również rodzaje geomów bardziej wymyślne niż zwykłe histogramy. Na poniższym wykresie mają Państwo inny geom - `geom_dotplot`, który zamiast prostokątów rysuje kropki. ```{r} ggplot(data = beaver1, aes(x = temp)) + geom_dotplot() + geom_rug() ``` Zwróćmy teraz uwagę na to, że w naszym zbiorze danych mamy dane z okresów aktywności i braku aktywności. Chcielibyśmy je rozdzielić, żeby lepiej zobrazować różnicę między nimi. Użyjemy do tego funkcji `facet_grid`, która w sposób automatyczny dzieli nasz wykres na panele według zadanego czynnika. ```{r} beaver1['aktywnosc'] <- as.factor(beaver1$'activ') ggplot(data = beaver1, aes(x = temp, fill = aktywnosc)) + geom_dotplot() + geom_rug() + facet_grid(.~aktywnosc) # możemy wybrać dwie zmienne, składnia to `zmienna1 ~ zmienna2` ``` Możemy próbować uatrakcyjnić nasz wykres. Proszę zauważyć, że każdy *geom* ma tutaj inne mapowanie - wysokosc słupków kropek reprezentuje liczbe zliczeń w danym "koszyku" hisgoramu, kolor kropek - aktywność bobra, kreseczki - dokładna wartość każdej obserwacji. Z kolei kolor kreseczek reprezentuje dzień, w którym ich dokonywaliśmy. Każda z tych rzeczy była osobną "warstwą", którą definiowaliśmy w gramatyce grafiki `ggplot2`. ```{r} ggplot(data = beaver1, aes(x = temp)) + geom_dotplot(aes(fill = aktywnosc, color = aktywnosc), binpositions="all", stackgroups = TRUE) + geom_rug(aes(color=dzien, fill = dzien)) ``` Jeżeli chodzi o inne *geomy* w stylu histogramów to mamy jeszcze `geom_freqpoly`. ```{r} ggplot(data = beaver1, aes(x = temp, fill = dzien, color = dzien)) + geom_freqpoly() ``` Oraz `geom_area` (ten jest szczególnie efektowny). ```{r} ggplot(data = beaver1, aes(x = temp, fill = dzien, color = dzien)) + geom_area(stat='bin') ``` Jeżeli chodzi o wykresy słupkowe, to podobnie jak w standardowym R sami musimy policzyć wartości mające odpowiadać wysokości słupków. Jeśli mają to być zliczenia postąpimy więc tak: ```{r} ggplot(data = beaver1, aes(x = dzien, fill = aktywnosc)) + geom_bar() ``` Domyślnym zachowaniem jest ustawianie słupków na sobie, ale możemy je zmienić tak, by ustawiane były obok siebie. ```{r} ggplot(data = beaver1, aes(x = dzien, fill = aktywnosc)) + geom_bar(position= 'dodge') ``` Czasami jednak nie chcemy reprezentować na wykresie słupkowym liczby zliczeń czegoś, ale np. średnią, odchylenie standardowe, medianę. Wówczas chyba najprościej jest stworzyć sobie nową ramkę danych, w której będą obliczone te wartości i przekazać jako argument `stat` funkcji `geom_bar` wartość `"identity"`. ```{r} df <- data.frame(sd = tapply(beaver1$temp, beaver1$dzien, sd), dzien = levels(beaver1$dzien)) df ggplot(data = df, aes(y = sd, x = dzien, fill = dzien)) + geom_bar(stat = 'identity') ``` Biblioteka `ggplot2` daje możliwość łączenia ze sobą kilka rodzajów wykresów. Na poniższym rysunku połączyliśmy wykres punktowy (z *jitterm* dla większej przejrzystosci) z wykresem skrzypcowym. Dodaliśmy również podpisy pod osiami oraz tytuł wykresu, zmieniliśmy też domyślne kolory (żeby mniej się zlewały). ```{r} ggplot(data = beaver1) + geom_violin(aes(x = dzien, y = temp, fill = dzien), draw_quantiles = c(0.25, 0.5, 0.75)) + geom_jitter(aes(x = dzien, y = temp, color = aktywnosc)) + scale_fill_brewer(palette='Set1') + ggtitle('Wykres skrzypcowy') + labs(x='Dzień pomiarów', y='Temperatura') ``` ## Dwie zmienne ciągłe. W przypadku sytuacji, w której mamy dwie zmienne ciągłe postępujemy dokładnie tak samo. Do ilustracji posłużymy się zbiorem `iris`, który dotyczy kwiatków. ```{r} head(iris) ``` Znów, samo wywołanie funkcji `ggplot` nic nie daje. ```{r} ggplot(data = iris) ``` Nawet ustawienie mapowania nic nie daje, chociaż ustala nam osie i siatkę. ```{r} ggplot(data = iris) + aes(x = Petal.Length, y = Petal.Width) ``` Pierwszy *geom* jaki omowimy to `geom_point` odpowiadający znanemu już nam wykresowy punktowemu. ```{r} ggplot(data = iris) + geom_point(aes(x = Sepal.Width, y = Sepal.Length)) ``` Kstzałt punktów jest jedną z możliwościa mapowania zmiennej dyskretnej. ```{r} ggplot(data = iris) + geom_point(aes(x = Sepal.Width, y = Sepal.Length, shape = Species)) ``` Podobnie jak poprzednio możemy dodać dodatkowe mapowanie, które trzecią zmienną - gatunek - mapuje jako kolor. Dodatkowo zmieniliśmy wygląd całego wykresu dodając jeden z kilku wbudowanych motywów (`theme_minimal`) ```{r} ggplot(data = iris) + geom_point(aes(x = Sepal.Width, y = Sepal.Length, col = Species)) + theme_minimal() ``` Możemy oczywiście umieścić nasze punkty na trzech osobnych panelach w znany nam już sposób. ```{r} ggplot(data = iris) + geom_point(aes(x = Sepal.Width, y = Sepal.Length, col = Species)) + facet_grid(. ~ Species) + theme_bw() ``` Panele możemy umieścić w innej orientacji zmieniając kolejnośc zmiennych w funkcji `facet_grid`. Warto zwrócić uwagę, że podpisy pod kategoriami są prawidłowo w pozycji poziomej. Pokazuję również inny motyw (`theme_dark`) ```{r} ggplot(data = iris) + geom_point(aes(x = Sepal.Width, y = Sepal.Length, col = Species)) + facet_grid(Species~.) + theme_dark() ``` Bardzo przydatne może być dla Państwa dodawanie linii regresji do wykresu punktowego. Pokazuje więc jak zrobić to za pomocą `stat_smooth`. Dopasowuje ona model liniowy do danych. Zacienione pole do przedział ufności (domyślnie 95%) dla linii regresji. ```{r} ggplot(data = iris) + facet_grid(.~Species) + stat_smooth(aes(x = Sepal.Width, y = Sepal.Length, col = Species), method = "lm", fullrange= TRUE)+ geom_point(aes(x = Sepal.Width, y = Sepal.Length, col = Species)) ``` Należy jednak pamiętać, że domyślnie dopasowywane jest nie model liniowy, ale ten: https://en.wikipedia.org/wiki/Local_regression Linia jest więc pogięta, podobnie przedziały ufności. ```{r} ggplot(data = iris, aes(x = Sepal.Width, y = Sepal.Length, col = Species)) + geom_point() + geom_smooth() + facet_grid(.~Species) ``` Oczywiście nic nie stoi na przeszkodzie dodawać znane już nam geomy takie jak `geom_rug`. ```{r} ggplot(data = iris, aes(x = Sepal.Width, y = Sepal.Length, col = Species)) + geom_point() + geom_smooth() + geom_rug() + facet_grid(.~Species) ``` # Zakończenie - bardziej skomplikowany przykład. W kolejnym przykładzie tworzymy bardziej skomplikowany wykres dotyczący rozmaitych parametrów diamentów. Dane znajdują się w zbiorze `diamonds`, który wbudowany jest w R. ```{r} head(diamonds) ``` ```{r} ggplot(data = diamonds, aes(y=carat, x = cut, size = price, color = cut)) + geom_jitter() ``` ```{r} ggplot(data = diamonds, aes(x=carat, y = price)) + # podstawowe mapowanie geom_point(aes(color = color)) + # mapowanie dla punktów: kolor diamentów -> kolor punktów facet_grid(clarity~cut) + # dzielimy na panele ze względu na przejrzystość i szlif stat_smooth(method = 'lm') + # dopasowujemy do danych linię regresji ggtitle('Diamenty przyjacielem studenta') + theme_minimal() ``` ## Uwagi: 1. Proszę dobierać rodzaj wykresu do celu - inny będzie w specjalistycznym czasopiśmie, inny na prezentacji, inny na plakacie, inny w artykule prasowym, inny dla siebie do pracy. 2. Nie wszystkie mapowania są sobie równe - kwestie poznawcze. 3. Nie należy przesadzać, należy dobrze zaplanować co i dlaczego chcemy pokazać. 4. Ale nie warto bać się eksperymentowania. # Przydatne linki: 1. Plotly + `ggplot2` = wygryw: https://plot.ly/ggplot2/ 2. Dokumentacja https://ggplot2.tidyverse.org/ 3. Dodatki: https://exts.ggplot2.tidyverse.org/ 5. Galeria grafik wg koncepcji Tufte'a - niektóre z wykorzystaniem `ggplot2`: http://motioninsocial.com/tufte/