Najznačajnije ggplot vizuelizacije

Neke karakteristike koje želimo da naš grafik ispunjava su da:

Sada ćemo navesti neke vizuelizacije, sortirane na osnovu njihove primarne svrhe. Pre svega, postoji nekoliko vrsta ciljeva za koje želimo da nacrtamo grafik. Dakle, pre nego što napravimo grafik, moramo prvo utvrditi koje odnose želimo da prikažemo vizuelizacijom - verovatno će upasti pod jednu (ili ponekad više) od ovih kategorija.

1. Korelacija

Sledeći grafici pomažu da se ispita koliko su korelisane dve promenljive.

Scatterplot

Najčešće korišćen grafik za analizu podataka je nesumnjivo scatterplot. Kad god želimo da razumemo prirodu veze između dve promenljive, uvek je prvi izbor scatterplot.

On se može nacrtati pomoću geom_point(). Dodatno, geom_smooth() podrazumevano crta liniju fitovanja (baziranu na loess regresiji), a može se podesiti da crta pravu linearne regresije postavljanjem method = lm.

options(scipen=999)
theme_set(theme_bw())  
data("midwest", package = "ggplot2")

# Scatterplot
gg = ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) + 
  geom_smooth(method="loess", se=F) + 
  xlim(c(0, 0.1)) + 
  ylim(c(0, 500000)) + 
  labs(subtitle="Area Vs Population", 
       y="Population", 
       x="Area", 
       title="Scatterplot", 
       caption = "Source: midwest")

plot(gg)

Scatterplot sa zaokruživanjem

Kada predstavljamo rezultate, ponekad želimo da istaknemo na grafiku neku grupu tačaka ili deo grafika kako bismo skrenuli pažnju na te specijalne slučajeve. Ovo se može zgodno uraditi uz pomoć funkcije geom_encircle() iz gglat paketa, koja zaokružuje grupe tačaka koje nas interesuju.

Unutar geom_encircle(), postavimo data na novu bazu podataka koja sadrži samo tačke (redove) koje želimo da zaokružimo. Štaviše, možemo podesiti krivu tako da prođe baš izvan tačaka. Boja i debljina krive se takođe mogu modifikovati. To ćemo videti na primeru ispod:

options(scipen = 999)
library(ggalt)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:plyr':
## 
##     arrange, count, desc, failwith, id, mutate, rename, summarise,
##     summarize
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
midwest_select = filter(midwest, poptotal > 350000, poptotal <= 500000, area > 0.01, area < 0.1)

# grafik
ggplot(midwest, aes(x=area, y=poptotal)) + 
  geom_point(aes(col=state, size=popdensity)) +   
  geom_smooth(method="loess", se=F) + 
  xlim(c(0, 0.1)) + 
  ylim(c(0, 500000)) +   
  geom_encircle(aes(x=area, y=poptotal), 
                data=midwest_select, 
                color="red", 
                size=2, 
                expand=0.08) +   # zaokruzujemo 
  labs(subtitle="Area Vs Population", 
       y="Population", 
       x="Area", 
       title="Scatterplot + Encircle", 
       caption="Source: midwest")

Jitter plot

Posmatrajmo sada druge podatke - ovog puta, koristićemo mpg bazu da predstavimo kilometražu koja se pređe u gradu (cty) u odnosu na kilometražu koja se pređe na autoputu (hwy) za istu količinu goriva.

data(mpg, package="ggplot2") 
theme_set(theme_bw())  

g = ggplot(mpg, aes(cty, hwy))

# Scatterplot
g + geom_point() + 
  geom_smooth(method="lm", se=F) +
  labs(subtitle="mpg: city vs highway mileage", 
       y="hwy", 
       x="cty", 
       title="Scatterplot with overlapping points", 
       caption="Source: midwest")

Ono što imamo je scatterplot gradske i kilometraže na autoputu u mpg bazi. Ovaj scatterplot izgleda uredno i pruža jasnu ideju o tome kako su kilometraža koja se pređe u gradu (cty) i kilometraža koja se pređe na autoputu (hwy) povezane. Međutim, ovaj grafik nevinog izgleda nešto krije.

dim(mpg)
## [1] 234  11

Originalna baza ima 234 tačke, ali izgleda da grafik prikazuje manje tačaka. Šta se desilo? Ovo se dešava zato što postoji mnogo preklapajućih tačaka koje se prikazuju kao jedna tačka. Činjenica da su cty i hwy celi brojevi u izvornoj bazi učinili su još lakšim da se ovaj detalj prikrije. Zato treba da budemo izuzetno oprezni kada pravimo scatterplot sa celim brojevima.

Kako da rešimo ovo? Postoji nekoliko opcija. Možemo napraviti jitter plot sa jitter_geom(). Kako mu ime sugeriše, preklapajuće tačke su nasumično razbacane oko svoje prave pozicije, a koliko odstupaju kontroliše argument width.

theme_set(theme_bw()) 
g = ggplot(mpg, aes(cty, hwy))
g + geom_jitter(width = 0.5, size=1) +
  labs(subtitle="mpg: city vs highway mileage", 
       y="hwy", 
       x="cty", 
       title="Jittered Points")

Sada se vidi više tacaka. Što je veće width, tačke se više pomeraju od svoje početne pozicije (ne treba preterati!).

Counts chart

Druga opcija da se prevaziđe problem preklapanja tačaka je da koristimo nešto što se zove counts chart. Gde god se preklapa više tačaka, krugovi se povećavaju.

theme_set(theme_bw()) 
g = ggplot(mpg, aes(cty, hwy))
g + geom_count(col="tomato3", show.legend=F) +
  labs(subtitle="mpg: city vs highway mileage", 
       y="hwy", 
       x="cty", 
       title="Counts Plot")

Bubble plot

Dok nam scatterplot omogućava da poredimo odnos između dve neprekidne promenljive, bubble chart služi da bolje razumemo odnos unutar grupa na osnovu:

  1. kategoričke promenljive (promenom boje) i

  2. druge neprekidne promenljive (promenom veličine tacaka)

Jednostavnije rečeno, bubble chart je pogodniji ako imamo 4-dimenzione podatke gde imamo dve numeričke (x i y), jednu kategoričku (color) i još jednu numeričku promenljivu (size).

mpg_select = filter(mpg, manufacturer %in% c("audi", "ford", "honda", "hyundai"))

theme_set(theme_bw()) 
g = ggplot(mpg_select, aes(displ, cty)) + 
  labs(subtitle="mpg: Displacement vs City Mileage",
       title="Bubble chart")

g + geom_jitter(aes(col=manufacturer, size=hwy)) + 
  geom_smooth(aes(col=manufacturer), method="lm", se=F)

Vidimo da bubble chart jasno razlikuje raspon displ i to kako varira nagib linije fitovanja među proizvođačima, pa nam omogućuje bolje vizuelno poređenje između grupa.

Animirani bubble chart

Animirani bubble chart se može implementirati pomoću gganimate paketa. Isti je kao bubble chart, ali još dodatno prikazuje kako se vrednosti menjaju u petoj dimenziji (najčešće vremenu).

Ključna stvar je da postavimo aes(frame) na željenu kolonu koju želimo da animiramo. Ostatak procedure povezan sa konstrukcijom grafika je isti. Kada je grafik konstruisan, možemo ga animirati koriscenjem gganimate() postavljanjem odabranog intervala.

library(ggplot2)
library(gganimate)
library(gapminder)
theme_set(theme_bw()) 

g = ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, frame = year)) +
  geom_point() +
  geom_smooth(aes(group = year), 
              method = "lm", 
              show.legend = FALSE) +
  facet_wrap(~continent, scales = "free") +
  scale_x_log10()  # konvertujemo u logaritamsku skalu

gganimate(g, interval=0.2)

Marginalni histogram / Boxplot

Ako želimo da prikažemo odnos, ali i raspodele na istom grafiku, koristimo marginalni histogram. Histogrami promenljivih x i y prikazuju se na marginama scatterplot-a.

Ovo se može primeniti korišćenjem funkcije ggMarginal() iz ggExtra paketa. Osim histograma, mozemo izabrati da nacrtamo i marginalni boxplot ili grafik gustine tako sto ćemo podesiti odgovarajuce opcije.

library(ggExtra)
data(mpg, package="ggplot2")

theme_set(theme_bw()) 
mpg_select = filter(mpg, hwy >= 35, cty > 27)
g = ggplot(mpg, aes(cty, hwy)) + 
  geom_count() + 
  geom_smooth(method="lm", se=F)

ggMarginal(g, type = "histogram", fill="transparent")

g1 = ggplot(mpg, aes(cty, hwy)) + 
  geom_count() + 
  geom_smooth(method="lm", se=F)

ggMarginal(g1, type = "boxplot", fill="transparent")

g2 = ggplot(mpg, aes(cty, hwy)) + 
  geom_count() + 
  geom_smooth(method="lm", se=F)

ggMarginal(g2, type = "density", fill="transparent")

Korelogram

Korelogram nam omogućava da vidimo korelaciju više neprekidnih slučajnih velicina koje se nalaze u jednoj bazi. Ovo je moguće uraditi uz pomoć funkcija iz ggcorrplot paketa.

# Korelaciona matrica
library(ggcorrplot)
data(mtcars)
corr = round(cor(mtcars), 1)

# Korelogram
ggcorrplot(corr, hc.order = TRUE, 
           type = "lower", 
           lab = TRUE, 
           lab_size = 3, 
           method="circle", 
           colors = c("tomato2", "white", "springgreen3"), 
           title="Correlogram of mtcars", 
           ggtheme=theme_bw)

2. Odstupanja

Upoređujemo odstupanja za određene podatke (ili kategorije) u odnosu na fiksnu referentnu vrednost.

Diverging bars

Diverging bars je grafik koji može da sadrži i pozitivne i negativne vrednosti. Ovo se može implementirati pomoću podešavanja u geom_bar(). Međutim, upotreba geom_bar() može biti prilično zbunjujuća, zato što se može koristiti za pravljenje bar grafika, kao i histograma.

Podrazumevano, geom_bar() ima stat podešen na count. To znači da, kad obezbedimo samo jednu neprekidnu promenljivu x (bez promenljive y), on pokušava da napravi histogram od podataka.

Da bi se napravio bar chart, a ne histogram, potrebno je uraditi dve stvari.

  1. Podesiti stat = identity.

  2. Navesti x i y unutar aes() gde je x znakovna ili faktor, a y numerička promenljiva.

Da bismo bili sigurni da dobijamo “diverging bars”, treba da osiguramo da kategorička promenljiva ima 2 kategorije takve da je jedna ispod, a jedna iznad određenog praga numeričke promenljive. U sledećem primeru, standardizujemo mpg iz mtcars baze zbog lepšeg prikaza. Vozila sa mpg_z većim od nule su označena zelenom bojom, a ona mpg sa manjim od nule crvenom bojom.

theme_set(theme_bw())  

data("mtcars") 
m = mutate(mtcars, car_name = rownames(mtcars))  # kreiramo novu kolonu sa imenima vozila
m = mutate(m, mpg_z = round((mpg - mean(mpg))/sd(mpg), 2))  # racunamo standardizovani mpg
m = mutate(m, mpg_type = ifelse(mpg_z < 0, "below", "above"))  # ispod / iznad praga
m = arrange(m, mpg_z)  # sortiramo
m$car_name = factor(m$car_name, levels = m$car_name)  # pretvaramo u faktor 

# Diverging Barcharts
ggplot(m, aes(x=car_name, y=mpg_z, label=mpg_z)) + 
  geom_bar(stat='identity', aes(fill=mpg_type), width=0.5)  +
  scale_fill_manual(name="Mileage", 
                    labels = c("Above Average", "Below Average"), 
                    values = c("above"="#00ba38", "below"="#f8766d")) + 
  labs(subtitle="Normalised mileage from 'mtcars'", 
       title= "Diverging Bars") + 
  coord_flip()

Diverging lollipop chart

Lollipop chart daje iste informacije kao Bar chart i Diverging chart, samo što izgleda modernije. Umesto geom_bar(), koristimo geom_point() i geom_segment() da bismo dobili vizuelizacije. Nacrtajmo lollipop koristeći iste podatke koje smo pripremili u prethodnom primeru za diverging bars.

theme_set(theme_bw())

ggplot(m, aes(x=car_name, y=mpg_z, label=mpg_z)) + 
  geom_point(stat='identity', fill="black", size=6)  +
  geom_segment(aes(y = 0, 
                   x = car_name, 
                   yend = mpg_z, 
                   xend = car_name), 
               color = "black") +
  geom_text(color="white", size=2) +
  labs(title="Diverging Lollipop Chart", 
       subtitle="Normalized mileage from 'mtcars': Lollipop") + 
  ylim(-2.5, 2.5) +
  coord_flip()

Diverging Dot Plot

Dot plot prikazuje slične informacije. Principi su isti kao za Diverging bars. U sledećem primeru koriste se isti podaci kao i za diverging bars:

theme_set(theme_bw())

ggplot(m, aes(x=car_name, y=mpg_z, label=mpg_z)) + 
  geom_point(stat='identity', aes(col=mpg_type), size=6)  +
  scale_color_manual(name="Mileage", 
                     labels = c("Above Average", "Below Average"), 
                     values = c("above"="#00ba38", "below"="#f8766d")) + 
  geom_text(color="white", size=2) +
  labs(title="Diverging Dot Plot", 
       subtitle="Normalized mileage from 'mtcars': Dotplot") + 
  ylim(-2.5, 2.5) +
  coord_flip()

Area Chart

Area chart se obično koristi da prikaže kako se neka mera (kao što je %) ponaša u odnosu na osnovnu liniju. Ovo implementira geom_area().

data("economics", package = "ggplot2")
library(lubridate)

# Racunamo % 
economics = mutate(economics, returns_perc = c(0, diff(psavert)/psavert[-length(psavert)]))

# podeoci i oznake na osama
brks = economics$date[seq(1,length(economics$date),12)]
lbls = year(economics$date[seq(1,length(economics$date),12)])

ggplot(economics[1:100, ], aes(date, returns_perc)) + 
  geom_area() + 
  scale_x_date(breaks=brks, labels=lbls) + 
  theme(axis.text.x = element_text(angle=90)) + 
  labs(title="Area Chart", 
       subtitle = "Perc Returns for Personal Savings", 
       y="% Returns for Personal savings", 
       caption="Source: economics")

3. Rangiranje

Koriste se za poređenje položaja ili performansi više stavki jednih u odnosu na druge.

Ordered Bar Chart

Ordered Bar Chart je bar chart sortiran po promenljivoj na y osi. Samo sortiranje baze po promenljivoj koja nas zanima nije dovoljno za ovaj grafik: promenljiva x ose mora biti konvertovana u faktor.

Hajde da prikažemo proesečnu gradsku kilometražu za svakog proizvođača iz mpg baze. Prvo, grupišemo i sortiramo podatke pre nego što nacrtamo grafik. Nakon toga, promenljiva x se konvertuje u faktor:

# Priprema podataka: grupisemo mean city mileage po proizvodjacima
library(dplyr)
cty_m = group_by(mpg, manufacturer)  # grupisemo
cty_mpg = summarise(cty_m, mileage = mean(cty))
cty_mpg = rename(cty_mpg, make = manufacturer) # menjamo imena kolonama
cty_mpg = arrange(cty_mpg, mileage)  # sortiramo
cty_mpg$make = factor(cty_mpg$make, levels = cty_mpg$make) 
head(cty_mpg, 4)
## # A tibble: 4 x 2
##   make       mileage
##   <fct>        <dbl>
## 1 lincoln       11.3
## 2 land rover    11.5
## 3 dodge         13.1
## 4 mercury       13.2

Promenljiva x je sada factor, nacrtajmo:

theme_set(theme_bw())

ggplot(cty_mpg, aes(x=make, y=mileage)) + 
  geom_bar(stat="identity", width=0.5, fill="tomato3") + 
  labs(title="Ordered Bar Chart", 
       subtitle="Make Vs Avg. Mileage", 
       caption="source: mpg") + 
  theme(axis.text.x = element_text(angle=65, vjust=0.6))

Lollipop Chart

Lollipop chart prikazuje iste informacije kao i bar chart-ovi. Smanjivanjem debelih barova, ulepšava se grafik i više naglašava vrednost. Ovi grafici izgledaju minimalistički i moderno.

theme_set(theme_bw())

ggplot(cty_mpg, aes(x=make, y=mileage)) + 
  geom_point(size=3) + 
  geom_segment(aes(x=make, 
                   xend=make, 
                   y=0, 
                   yend=mileage)) + 
  labs(title="Lollipop Chart", 
       subtitle="Make Vs Avg. Mileage", 
       caption="source: mpg") + 
  theme(axis.text.x = element_text(angle=65, vjust=0.6))

Dot Plot

Dot Plot-ovi su veoma slični Lollipop-ovima, samo su bez linija. Oni vise naglašavaju redosled i udaljenost stavki jednih u odnosu na druge.

library(scales)
theme_set(theme_classic())

ggplot(cty_mpg, aes(x=make, y=mileage)) + 
  geom_point(col="tomato2", size=3) +   #crtamo tacke
  geom_segment(aes(x=make, 
                   xend=make, 
                   y=min(mileage), 
                   yend=max(mileage)), 
               linetype="dashed", 
               size=0.1) +  
  labs(title="Dot Plot", 
       subtitle="Make Vs Avg. Mileage", 
       caption="source: mpg") +  
  coord_flip()

Slope Chart

Grafici nagiba su odličan način za poređenje tačaka u ondosu na vreme. Sledeći kod služi kao smernica kako se moze pristupiti ovome:

library(scales)
theme_set(theme_classic())

# priprema podataka
df = read.csv("https://raw.githubusercontent.com/selva86/datasets/master/gdppercap.csv")
df = rename(df, "1952"=X1952, "1957"=X1957)
left_label = paste(df$continent, round(df$`1952`),sep=", ")
right_label = paste(df$continent, round(df$`1957`),sep=", ")
df = mutate(df, class = ifelse((`1957`-`1952`) < 0, "red", "green"))

p = ggplot(df) + geom_segment(aes(x=1, xend=2, y=`1952`, yend=`1957`, col=class), size=0.75, show.legend=F) + 
                  geom_vline(xintercept=1, linetype="dashed", size=0.1) + #granice
                  geom_vline(xintercept=2, linetype="dashed", size=0.1) +
                  scale_color_manual(labels = c("Up", "Down"), 
                                     values = c("green"="#00ba38", "red"="#f8766d")) +  # boja linija
                  labs(x="", y="Mean GdpPerCap") +  # nazivi osa
                  xlim(0.5, 2.5) + ylim(0,(1.1*(max(df$`1952`, df$`1957`))))  # granice za ose

# dodajemo tekst
p = p + geom_text(label=left_label, y=df$`1952`, x=rep(1, NROW(df)), hjust=1.1, size=3.5)
p = p + geom_text(label=right_label, y=df$`1957`, x=rep(2, NROW(df)), hjust=-0.1, size=3.5)
p = p + geom_text(label="Time 1", x=1, y=1.1*(max(df$`1952`, df$`1957`)), hjust=1.2, size=5)  # naslov
p = p + geom_text(label="Time 2", x=2, y=1.1*(max(df$`1952`, df$`1957`)), hjust=-0.1, size=5)  # naslov

#minimizujemo temu
p + theme(panel.background = element_blank(), 
           panel.grid = element_blank(),
           axis.ticks = element_blank(),
           axis.text.x = element_blank(),
           panel.border = element_blank(),
           plot.margin = unit(c(1,2,1,2), "cm"))