Uvod

Plotly je paket za izradu interaktivnih i dinamičkih vizualizacija podataka namijenjenih prikazivanju preko weba, koji se može koristiti u okviru raznih programskih jezika poput R-a, Phytona, MATLAB-a, Julie itd.

U ovoj lekciji pokazat ćemo vizualizaciju podataka putem paketa plotly namjenjenog radnom okruženju jezika R. Za korištenje plotlyja u sklopu nekih drugih programskih jezika vrijedi drugačija sintaksa.

U sklopu ove lekcije pokazat će se:

  • osnovna sintaksa za izradu interaktivnih grafikona (funkcije plot_ly() i varijacije funkcije add_trace()), od kojih će se pokazati nekoliko primjera točkastih, linijskih i stupčastih grafikona
  • kako urediti stilove grafikona funkijom layout()
  • kako podesiti informacije koje se privremeno pokažu prelaskom miša preko grafa
  • prikaz više grafikona na istoj slici, te kako ih međusobno povezati
  • kako grafikonu dodati filtar, padajući izbornik i/ili potvrdni okvir (engl. checkbox)
  • izrada i uređivanje animacija i akumuliranih animacija
  • izrada kartograma.

Praćenje ovoga teksta preporuča se korisnicima koji su već upoznati s jezikom R i manipulacijom podataka preko osnovnih funkcija iz kolekcije paketa tidyverse. U svim primjerima, osim u poglavlju Karte, korišteni su podatkovni okviri koji su ugrađeni u R te se mogu lako replicirati. Podaci potrebni za repliciranje kôda iz poglavlja o kartama nalaze se u datotekama populacija.csv i zupanije.csv.

U ovoj lekciji nastojalo se pojasniti svaku funkciju i argument koji se koristio, no za još bolje razumijevanje i širi spektar mogućnosti preporuča se paralelno korištenje dodatnih izvora poput službenoga priručnika za paket plotly i ostalih poveznica koje će se navoditi tijekom lekcije.

Za jasnije razumijevanje teksta, pojasnit ćemo kako se unutar ovog konteksta koriste neki pojmovi.

Graf neke funkcije f jest skup svih uređenih parova \((x, f(x))\) kada x prolazi domenom funkije f. Može biti u obliku raspršenih točaka, krivulje, ali i predočen skupom dužina, stupcima, pravokutnicima itd. Koristi se još i izraz trag.

Pod pojmom grafikona ili grafičkoga prikaza smatrat ćemo dijagram sa svim njegovim sastavnicama, poput grafa jedne ili više funkcija, koordinatnih osi, naslova i oznaka na osima, legende, itd.

Grafičke prikaze možemo podijeliti na tri vrste:

  • statičke — bilo koja vizualizacija u obliku slike koja se ne mijenja

  • interaktivne — grafika se mijenja s obzirom na akciju korisnika (prelaskom miša preko određenih dijelova grafa mogu iskočiti dodatne informacije o prikazu, dijelovi slike se mogu povećati ili smanjiti (engl. zoom in/out), preko legende se mogu izdvojiti određene varijable, a druge sakriti, osi koordinatnoga sustava se mogu pomjerati itd.))

  • dinamičke — grafika se mijenja automatski, na primjer animacije.

Početak rada

Za početak rada potrebno je osigurati da je instalirana zadnja inačica RStudija radi kompatibilnosti s paketom htmlwidgets (koji se učita preko plotly paketa), potom jednokratno instalirati paket plotly naredbom install.packages("plotly"), a na početku svake nove sesije ga i učitati naredbom library(plotly).

install.packages("plotly")
library(plotly)

Uobičajena sintaksa za vizualizaciju podataka putem paketa plotly izgleda otprilike ovako:

podatkovni_okvir %>% 
  plot_ly() %>% 
  add_trace() %>% 
  layout()

Riječima se to može opisati kao proces gdje se podatkovni okvir (ili matrica) koju se želi vizualizirati proslijedi funkciji za izradu grafikona plot_ly(). Vrsta grafa se može specificirati fukcijom add_trace() gdje umjesto trace može doći i markers, line, bars ,histogram ili drugo, ovisno o načinu na koji se žele prikazati podaci. Za dodatno uređivanje grafikona koristi se funkcija layout() u kojoj se mogu podesiti naziv, raspon i vrsta skale svake osi, naziv grafikona, i još puno toga. Funkcije se međusobno povezuju operatorom cjevovodi (engl. pipe operator) %>%.

U sljedećem kôdu dan je primjer jedne od mogućih vizualizacija podatkovnog okvira iris. Može se uočiti kako se za pozivanje varijabli podatkovnog okvira iris ne treba svaki put navoditi i ime podatkovnog okvira, nego se umjesto toga koristi znak ~. Drugim riječima, umjesto iris$Petal.Width dovoljno je staviti ~Petal.Width.

data(iris)
iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers() %>%
  layout(title = "Podatkovni okvir iris")

Slika 1: Primjer jednostavnog grafikona paketa plotly

Može se primijetiti da je grafikon interaktivan, a da se to svojstvo nije moralo eksplicitno ‘uključiti’.

Prelaskom miša preko pojedine točke ispisat će se njene x i y vrijednosti. To je tzv. hover info, a kasnije ćemo pokazati i kako ga urediti. U desnom gornjem izborniku nude se opcije u kojima se slika može pohraniti u obliku datoteke nastavka png, opcije za zumiranje, odnosno za povećanje i smanjenje cijele slike ili njenih odabranih dijelova, za pomicanje po koordinatnom sustavu, za selekciju putem pravokutnika (engl. box select) ili proizvoljnim oblikom (engl. lasso select). Uz pomoć tipke [Shift] moguće je napraviti više selekcija odjednom. Sve ovo može poslužiti tokom analize ili prezentacije rezultata.

Idemo sada detaljnije razraditi svaku od gore navedenih funkcija.

Funkcija plot_ly()

Funkciji plot_ly() preuzima podatkovni skup (podatkovni okvir, matrica, vektor) nad kojim se izvodi vizualizacija. Ona crta koordinatni sustav i mapira varijable na koordinatne osi. Sama ta funkcija je dovoljna da se proizvede grafikon i ako se ne specificira tip grafikona argumentom type ili dodavanjem traga funkcijom add_trace(), plot_ly() će sam zaključiti koji je najpogodniji oblik grafičkoga prikaza za dane varijable.

Prikaz dviju numeričkih varijabli
iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) 

Slika 2: Prikaz dviju numeričkih varijabli dan je raspršenim grafikonom

Prikaz numeričke i kategorijske varijable
iris %>% plot_ly(x = ~Species, y = ~Petal.Length) 

Slika 3: Prikaz faktorske i numeričke varijable dan je stupčastim grafikonom

Ako se cilja na baš određenu vrstu grafa, potrebno je postaviti argument type na jednu od sljedećih opcija: scatter, bar, box, heatmap, histogram, histogram2d, histogram2dcontour, contour, scatterternary, violin, funnel, waterfall, image, pie, sunburst, treemap, funnelarea, scatter3d, surface, isosurface, volume, mesh3d, cone, streamtube, scattergeo, choropleth, scattergl, splom, pointcloud, heatmapgl, parcoords, parcats, scattermapbox, choroplethmapbox, densitymapbox, sankey, indicator, table, carpet, scattercarpet, contourcarpet, ohlc, candlestick, scatterpolar, scatterpolargl, barpolar, area.

Plotly je dakle bogat grafičkim opcijama, a pojašnjenje za svaku od njih i izbor argumenata koje mogu poprimiti može se pronaći na poveznici o tragovima.

Vrijedno je još dodati to da ne postoji type = line(s), nego se za grafikon čije su vrijednosti povezane linijama, odnosno dužinama treba odabrati type = scatter, a onda argumentom mode postaviti jednu od opcija mode = c("lines", "lines+markers"), ovisno o tome žele li se prikazati samo linije ili točke i linije.

Raspršeni grafikon

Slijede tri različita prikaza raspršenoga grafikona nad istim skupom podataka generiranoga sljedećim kôdom:

set.seed(1)
x <- 1:100
y <- rnorm(100)
default
plot_ly(x=x, y=y)  # <==> plot_ly(x=x, y=y, type = "scatter", mode = "markers")

Slika 4: Točkasti raspršeni grafikon

mode = “lines+markers”
plot_ly(x=x, y=y, type = "scatter", mode = "lines+markers" )

Slika 5: Raspršeni grafikon s točkama i linijama

mode = “lines”
plot_ly(x=x, y=y, type = "scatter", mode = "lines" )

Slika 6: Linijski raspršeni grafikon

Preko funkcije plot_ly() mogu se postaviti i određene karakteristike samoga grafa, odnosno traga kao što su boja, debljina, simbol i prozirnost. Ako pretražimo dokumentaciju te funkcije upisom plot_ly u tražilicu kartice Help, izlistat će se neki argumenti kojima možemo urediti njen trag. Detaljniji popis nalazi se na ranije spomenutoj poveznici o tragovima. Navest ćemo primjer s bojom točaka.

Ako se boja točaka želi postaviti na neku određenu boju, jednaku za sve točke, proslijedit ćemo je argumentu color. Boja, zadana nazivom, heksadecimalnim oblikom ili cijelim brojem kojim se referira na boju iz standardne palete, mora se navesti unutar tzv. funkcije “As Is”, odnosno I() kako je plot_ly() ne bi zamijenio za varijablu.

Ovo su tri načina kojima se boja točaka može postavi na crveno.

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, color = I("red")) 

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, color = I(2))  # crvena je druga boja u standardnoj paleti
iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, color = I("#FF0000"))

Slika 7: Raspršeni grafikon s crvenim točkama

Bojom se također može dočarati utjecaj neke treće varijable. Unutar funkcije plot_ly() moguće je mapirati varijablu na atribut boje, naravno, uz pomoć operatora tilde ~. Time su sve točke s istom vrijednosti u toj varijable jednake boje.

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, color = ~Species)

Slika 8: Prikaz treće varijable bojom

Kada korisnik želi sam izbrati boje koje se dodjeljuju raznim vrijednostima varijable, koristi argument colors. Argument colors se dakle koristi nakon što je argumentu color dodijeljena varijabla. Boje u colors zadaju se standardnim načinom, na primjer u obliku vektora ili imenom palete te nije potrebno posredovanje funkcije I().

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, color = ~Species, colors = c("mediumturquoise", "hotpink", "dodgerblue"))

Slika 9: Prikaz treće varijable vlastitim izborom boja

S mapiranom trećom varijablom na graf vidi se kako duljina i širina latica cvjetova iris variraju ovisno o vrsti cvijeta.

U slučaju kada je varijabla koja se mapira na color numerička, legenda se pretvara u spektar boja s nijansama između zadanih boja (standardnih ili dviju ili više boja koje je korisnik zadao argumentom colors).

Standardna paleta boja
iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, 
                 color = ~Sepal.Length)

Slika 10: Prikaz numeričke varijable bojom, koristeći standardni spektar boja

Zadana paleta boja
iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, 
                 color = ~Sepal.Length, 
                 colors =c("yellow", "red"))

Slika 11: Postavljanje spektra boja na nijanse između žute i crvene

Iz ovakvog prikaza može se jasno vidjeti da su duljina i širina cvjetova iris visoko korelirane s duljinom čašičnoga listića (engl. sepal length).

Analogno se može odrediti širina točaka argumentima size i sizes, vrsta točaka argumentima symbol i symbols, vrsta linija argumentima linetype i linetypes, debljina obruba (tamo gdje je moguće) argumentima stroke i stokes itd.

Inače, symbol prima sve argumente kao i pch u grafici osnovnih paketa jezika R, te se mogu pronaći upisom pojma “points” u tražilicu Help kartice.

Za demonstraciju ćemo nanizati nekoliko argumenata kako bi se dobio dojam o njihovom učinku. Općenito, ovakve prikaze treba izbjegavati jer izgledaju pretrpano, no ovdje imamo opravdan razlog za takvo pretjerivanje.

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, 
                 symbol = 16, 
                 size = ~Sepal.Length, 
                 color = ~Species, 
                 stroke = I("red"), 
                 alpha = 0.5, 
                 showlegend = TRUE, 
                 linetype = ~Species)

Slika 12: Neke od mogućnosti za ‘uređivanje’ raspršenoga grafa

Funkcija add_trace()

Jednom kada imamo plotly objekt dobiven na primjer funkcijom plot_ly(), možemo mu dodavati nove grafove pomoću funkcija add_*(). Terminologija koja se koristi jest “dodavanje traga”. Njome se zapravo određuje vrsta grafa kojom se prikazuju (nove ili postojeće) varijable. add_trace() je općeniti oblik funkcije, a postoje specijalni slučajevi te funkcije ovisno o vrsti traga. Na primjer. add_trace(type = "bar") je ekvivalentno funkciji add_bars(), add_trace(type = "scatter") je ekvivalentno funkciji add_markers() itd. Slijedi tablica najčešće korištenih tragova, no u dokumentaciji funkcije add_trace() mogu se naći i mnoge ostale mogućnosti.

Nazivi funkcija ovisno o vrsti traga
Naziv funkcije Vrsta traga
add_marker() raspršeni graf
add_lines() linijski graf
add_histogram histogram
add_bars stupčasti dijagram
add_boxplot dijagram pravokutnika
add_choropleth kartogram
add_text proizvoljan tekst
add_image slika

Sve one nasljeđuju globalna svojstva zadana unutar funkcije plot_ly(), no svakom tragu se mogu zadati lokalne postavke koje imaju prednost nad globalnima, kao na primjer nove vrijednosti x i/ili y koordinata, vrstu, boju, oblik i veličinu traga. Drugim riječima, funkcije add_*() nasljeđuju sve globalne postavke iz funkcije plot_ly(), osim onih koje su joj lokalno zadane.

Sljedeća dva kôda predstavljaju različite načine dobivanja donjeg grafikona. Radi čitljivosti u lekciji će se preferirati drugi način, odnosno u plot_ly dijelu će se navoditi argumenti koji se tiču globalnih postavki, a tragovi će se raspodijeliti na add_*() funkcije i po potrebi im pridružiti vlastite lokalne postavke.

plot_ly(x = 1:10, y = 1:10, type = "scatter", mode = "markers", name = "prvi trag") %>% 
  add_markers(y = 3:12, name = "drugi trag")         

#----------- Drugi način -----------#

plot_ly(x = 1:10, y = 1:10) %>%
  add_markers(name = "prvi trag") %>%         
  add_markers(y = 3:12, name = "drugi trag")

Slika 13: Dva grafa, tj. traga na jednom grafikonu

Ovdje se koristio argument name kako bi se u legendi jasno prikazali nazivi grafova, odnosno tragova. Ako se želi izostaviti pojavljivanje traga u legendi, doda se atribut showlegend = FALSE.

Suradnja paketa dplyr i plotly

Prednost paketa plotly jest to što dozvoljava ispreplitanje, odnosno povezivanje svojih grafičkih funkcija s funkcijama izvan toga paketa. Posebno je korisno koristiti funkcije za manipulaciju podacima iz paketa dplyr (kao na primjer group_by(), filter(), select(), summarise()) s funkcijama paketa plotly.

U sljedećem primjeru želimo prikazati dijagram pravokutnika duljina latica i srednju vrijednost označenu crvenom točkom za svaku vrstu cvijeta iris iz podatkovnog okvira iris. Za to će nam trebati dva traga, jedan za dijagram pravokutnika, a drugi za srednje vrijednosti svakog od triju podskupova.

iris %>% plot_ly(x = ~Species, y = ~Petal.Length) %>%
  add_boxplot(name = "dijagram duljina latica") %>%
  group_by(Species) %>% summarise(Mean = mean(Petal.Length)) %>%
  add_markers(y = ~Mean, 
              color = I("red"), 
              name = "Srednja vrijednost duljine latica")

Slika 14: Kombinacija dijagrama pravokutnika i raspršenoga grafikona

Nakon crtanja dijagrama pravokutnika funkcijom add_boxplot(), podatke je bilo potrebno podijeliti na tri dijela i za svaki izračunati srednju vrijednost te onda njih preslikati na grafikon novim tragom add_markers(). U ovom slučaju, add_markers() je naslijedio x-koordinate iz funkcije plot_ly(), a y-koordinate su mu zadane lokalno. Sve su funkcije povezane operatorom cjevovodi %>%, a dodavanje tragova istom grafikonu nije ometeno činjenicom da je usred niza funkcija paketa plotly dodano par funkcija iz paketa dplyr.

Zahvaljujući tome što je grafikon interaktivan, slika se može uvećati jednim potezom miša držeći lijevi klik kako bi se, na primjer, vidjela razlika između medijana i srednje vrijednosti, a da bi se vidjele njihove točne vrijednosti, dovoljno je postaviti pokazivač miša iznad njih.

Dodavanje regresijskih krivulja

U vizualizaciji podataka često je poželjno istaknuti trend kretanja podataka dodavanjem krivulje. U tu svrhu mogu se iskoristiti funkcije jezika R poput lm() ili loess() za izradu modela, a potom iskoristiti dobiveni model funkcijom fitted().

Linearna regresija
m <- lm(iris$Petal.Length ~ iris$Petal.Width)

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers(showlegend=FALSE) %>%
  add_lines(y= ~fitted(m))

Slika 15: Pridruživanje modela linearne regresije raspršenom grafikonu

Polinomna regresija II. stupnja
m <- lm(Petal.Length ~ poly(Petal.Width, 2, raw = TRUE), iris) 
# ili   m <- lm(Petal.Length ~ Petal.Width + I(Petal.Width^2), iris)
iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers(showlegend=FALSE) %>%
  add_lines(y= fitted(m))

Slika 16: Pridruživanje modela polinomne regresije II. stupnja raspršenom grafikonu

Polinomna regresija III. reda
m <- lm(Petal.Length ~ poly(Petal.Width, 3, raw = TRUE), iris)
 #ili m <- lm(Petal.Length ~ Petal.Width + I(Petal.Width^2) + + I(Petal.Width^3), iris)
iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers(showlegend=FALSE) %>%
  add_lines(y= fitted(m))

Slika 17: Pridruživanje modela polinomne regresije III. stupnja raspršenom grafikonu

Segmentirana polinomna regresija
m <- loess(iris$Petal.Length ~ iris$Petal.Width)

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers(showlegend=FALSE) %>%
  add_lines(y= fitted(m))

Slika 18: Pridruživanje modela polinomne regresije po dijelovima raspršenom grafikonu

Funkcija layout()

Funkcija layout() služi za stilsko uređenje grafikona, to jest svega onoga što nije trag. Argumentom title može se postaviti naslov grafikona, xaxis i yaxis argumentima može se zadati lista atributa kojima se mogu odrediti naslovi osi, oznake na osima, postaviti boje pozadine grafikona u unutrašnjosti i na rubovima, margine, vrstu slova itd.

Prvo ćemo pokazati jednostavan primjer funkcije layout() u kojem ćemo postaviti samo naslov grafikona, nazive osi i raspon osi y.

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, color = ~Species) %>%
  
  layout(title = "Jednostavan primjer \nkorištenja funkcije layout()", 
         xaxis = list(title = "Širina latica"),
         yaxis = list(title = "Duljina latica", range = c(0, 8)))

Slika 19: Jednostavan primjer korištenja funkcije layout() za uređenje grafikona

A sada ćemo pokazati primjer s nešto razrađenijim postavkama.

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length, color = ~Species) %>%
  
layout(title = list(text="Odnos širine i duljine latica cvjetova iris", 
                    x = 0.09,                               # horizontalno pozicioniranje naslova
                    y=0.93),                                # vertikalno pozicioniranje naslova
         
      xaxis = list(title = list(text = "Širina latica", 
                                font = list(size = 16),
                                standoff = 10),            # vertikalno pozicioniranje naziva osi x
                   tickmode = "array",                     # način za ručno podešavanje oznaka na osima
                   tickvals = c(1, 2),                     # pozicije oznaka na osi x 
                   ticktext = c("1cm", "2cm"),             # nazivi oznaka na osi x
                   tickfont = list(size = 14),             # veličina oznaka na osi x
                   range = c(0, 2.75)),                    # raspon osi x
         
      yaxis = list(title = "Duljina latica", 
                      range = c(0,8),
                      linecolor = "blue"),                 # boja osi y
         
      paper_bgcolor = "darkturquoise",                           
      plot_bgcolor = "aliceblue",
         
      legend = list(title = list(text="Vrste irisa", 
                                 font = list(color = "yellow",
                                                size = 14)), 
                    bgcolor = "cadetblue", 
                    font = list(color = "white"),
                    x = 0.78,                              # horizontalno pozicioniranje legende
                    y = 0.05),                             # vertikalno pozicioniranje legende
         
      margin = list(t = 60, b = 60, r = 60))              # širine gornjeg, donjeg i desnog ruba u px

Slika 20: Primjer korištenja mnogih argumenata za uređivanje grafikona funkcije layout()

Duga lista atributa može se pronaći na već spomenutoj poveznici o tragovima pod rubrikom Layout. Za spretno korištenje atributa bitno je razumjeti što je kojem argumentu roditelj (engl. parent) i da se većina argumenata navodi unutar liste. Argument koji ima složenoga roditelja nalazi se unutar nekoliko listi. Na primjer, ako se želi podesiti boja naslova legende atributom color, iz donje slike se vidi da je roditelj tog atributa layout.legend.title.font, što znači da se color treba postaviti unutar liste za font, koja se nalazi unutar liste za title, koja se nalazi unutar liste za legend u funkciji layout(). Sive vertikalne linije s lijeve strane pomažu u snalaženju među atributima i ugnježdavanjem listi. Treba uzeti u obzir i da ima puno argumenata naziva color, i da treba odabrati onaj pravi.

Slika  21: Izvadak s web-stranice koja sadrži popis svih argumenata za funkciju layout(). Napomena: Slika se sastoji od tri dijela koja su naknadno spojena.

Slika 21: Izvadak s web-stranice koja sadrži popis svih argumenata za funkciju layout(). Napomena: Slika se sastoji od tri dijela koja su naknadno spojena.

U moru svih tih atributa, korisno je izdvojiti:

  • separators = ",." čime se prvi znak u navodnicima postavlja na decimalni znak, a drugi za razdvajanje tisućica. Standardna postavka je ". ", odnosno koristi decimalnu točku, a razmak za razdvajanje tisućica, što nije po standardima hrvatskoga pravopisa.

  • xaxis/yaxis = list(type = c("linear", "log", "date", "category", "multicategory")) — ovime se može odrediti vrsta skale na osima. Iako plotly sam procjenjuje koji je tip podataka preslikan na os, u nekim slučajevima kada se radi o datumima, prikaz će bolje raditi i ljepše izgledati ako se os postavi na datumski tip pomoću type = "date". Kod podataka koji imaju široki raspon, a gust skup točaka na jednom dijelu, koordinatnu os je moguće transformirati na logaritamsku skalu jednostavnim zapisom type = "log" kao što ćemo vidjeti kasnije u primjeru o prodaji nekretnina.

  • range je atribut unutar xaxis i yaxis za podešavanje raspona osi. Ponekad se deselektiranjem podataka iz legende raspon osi prilagodi novom, smanjenom prikazu podataka što nije uvijek poželjno, te je zgodno iskoristiti ovaj atribut kako bi se raspon osi fiksirao.

  • title = list(x, y, xref, yref) — s obzirom na to da grafike paketa plotly dolaze s interaktivnim izbornikom u gornjem, desnom kutu, on se vizualno često preklapa s nazivom grafikona. Ovim postavkama, naziv grafikona se može pomaknuti na bolju poziciju. x i y poprimaju vrijednosti između [0, 1] gdje 0 označava lijevi rub za x, a dno za y, a 1 označava desni rub za x, a vrh za y. xref i yref su podešeni tako da se taj raspon odnosi na unutrašnjost grafikona, odnosno dio između osi, dok postoji i opcija da se xref i yref postave na “paper” u kojem slučaju bi se x i y vrijednosti odnosile na cijeli okvir slike.

Još je ostalo napomenuti da plotly ne koristi boje iz sustava R, nego CSS-boje. Popis imenovanih boja može se pronaći, između ostalog, na poveznici s CSS-bojama, a vrijedi pogledati i poveznicu s paletama boja ugrađenima u plotly.

Vrste grafikona

Nakon osnova o trima glavnim funkcijama koje se koriste za grafički prikaz podataka putem paketa plotly, pokažimo neke od češće korištenih grafikona i njihove varijacije.

Raspršeni grafikoni

Popis i pojašnjenje argumenata za raspršeni grafikon nalazi se na poveznici o tragovima u poglavlju Scatter i podnaslovu marker.

Obični
x <- seq(1, 10, 0.5)
y <- seq(1, 10, 0.5)

plot_ly(x = x, y = y) %>%
  add_markers(name = "prvi trag") %>%         
  add_markers(y = y-3, name = "drugi trag") 

Slika 22: Primjer jednostavnoga raspršenog grafikona s dva traga

‘Ukrašeni’
plot_ly(x = x, y = y) %>%
  add_markers(name = "prvi trag",
              marker = list(size = 6,
                             color = 'aquamarine',
                             line = list(color = 'black',
                                         width = 0.8))) %>%         
  add_markers(y = y-3, name = "drugi trag",
              marker = list(size = 10,
                             color = rep(c('plum', "snow", "aqua"),length.out = length(x)),
                             line = list(color = 'red',
                                         width = 2))) %>%
  add_markers(y = y-5, name = "treći trag",
              marker = list(symbol = 1:19), size = 12)

Slika 23: Primjer raspršenoga grafikona gdje su točke tragova ukrašene na razne načine

Linijski grafikoni

Popis i pojašnjenje argumenata za linijski graf nalazi se na poveznici o tragovima u poglavlju Scatter i podnaslovu line. Ima nekoliko podnaslova line, ali treba uzeti onaj čiji je roditelj samo scatter (“Parent: data[type=scatter]”).

Razne vrste linija
x <- 1:100
y <- rnorm(100, mean =5)

plot_ly(x= x, y = y, type = "scatter", mode = "line", 
        line = list(dash = 'dot',       
                    width = 0.5, 
                    color = "red"),
        name = "prvi trag") %>%
  
  add_lines(y = mean(y), name = "drugi trag",
             line = list(dash = 'dash', color = "blue", width = 3),
             name = "drugi trag")

Slika 24: Primjer raspršenoga grafikona s dva traga čije su točke povezane isprekidanim linijama

Linije s nedostajućim vrijednostima
set.seed(1)
x <- 1:20
y <- sample(20)
y[c(sample(20,5))] <- NA

# y == c(4,  7,  1,  2, NA, 19, 11, 17, NA,  3, 18,  5,  9, NA,  6, 15, NA, 10, 20, NA)

plot_ly(x = x, y = y) %>% 
  add_lines(name ="prvi trag") %>%
  
  add_lines(y = y + 3, 
            name = "drugi trag",
            connectgaps = TRUE)

Slika 25: Primjer linijskih grafova s prekidima i bez prekida na mjestima nedostajućih vrijednosti

Linije za interpolaciju
set.seed(1)
x <- 1:10
y <- sample(10, replace = TRUE)

plot_ly(x = x, y = y) %>%
  add_lines(name = "Obična linija") %>%
  add_lines(y = y+3, line = list(shape = "spline"), name = "Spline") %>%
  add_lines(y = y+9, line = list(shape = "hv"), name = "hv") %>%
  add_lines(y = y+18, line = list(shape = "hvh"), name = "hvh")

Slika 26: Razne vrste linija za povezivanje zadanih točaka

#ostale opcije za shape su "vh", "vhv" (kombinacije horizontalnog i vertikalnog)
Ispunjena površina ispod linije
set.seed(1)
x <- 1:10
y <- sample(10, replace = TRUE)

plot_ly(x = x, y = y) %>%
  add_lines(fill = 'tonexty', 
            fillcolor="DarkGoldenRod", 
            line = list(shape = "spline",
                        color = 'red'),
            name = "prvi trag") %>%

    add_lines(y= y+c(1,2,3),
              fill = 'tonexty',
              fillcolor="darkcyan", 
              line = list(shape = "spline",
                        color = 'transparent'),
              name = "drugi trag")

Slika 27: Primjer grafikona s ispunjenom površinom ispod grafova

Stupčasti grafikoni

Popis i pojašnjenje argumenata za stupčaste grafikone nalazi se na poveznici o tragovima u poglavlju bar.

Obični
x <- c("2015.", "2016.", "2017.", "2018.", "2019.")
y <- sample(15, 5)
y2 <- sample(15, 5)

plot_ly(x = x, y = y) %>%
  add_bars()

Slika 28: Obični stupčasti grafikon

Uređeni
plot_ly(x = x, y = y) %>%
  add_bars(width = 0.5, 
           color = I("DarkTurquoise"),
           marker = list(line = list(color = "black",
                       width=2)),
           text = paste(y, "       "),
           textposition = "inside",
           textfont = list(size = 12, 
                           color = "blue"),
           textangle = -45,
           error_y = list(array = ~0.05*y,
                          color = "black"))

Slika 29: Primjer raznih načina uređivanja stupaca

Grupirani
plot_ly(x = x, y = y) %>%
  add_bars(color = I("DarkSlateGray")) %>%
  add_bars(y = y2, 
           color = I("DarkTurquoise")) %>%
  layout(barmode = 'group', bargroupgap = 0.1, bargap = 0.3)

Slika 30: Stupčasti grafikon s grupiranim stupcima

Naslagani
plot_ly(x = x, y = y) %>%
  add_bars(color = I("DarkSlateGray")) %>%
  add_bars(y = y2, 
           color = I("DarkTurquoise")) %>%
  layout(barmode = 'stack')

Slika 31: Stupčasti grafikon s naslaganim stupcima

Trodimenzionalni grafikon

Podatkovni skup volcano je 87 x 91 numerička matrica ugrađena u R koja sadrži topografske informacije o vulkanu Maunga Whau. Na donjem grafikonu je dan njen trodimenzionalni prikaz pomoću funkcije add_surface(), a s obzirom na to da je grafikon interaktivan, prikaz se može okretati i vidjeti iz svih uglova.

plot_ly(z = ~volcano) %>% 
  add_surface()

Slika 32: Trodimenzionalni prikaz matrice volcano. Prikaz se može okretati u svim smjerovima.

Hoverinfo

Engleski izraz za hover info odnosi se na informacije koje se prikažu kada se mišem prelazi preko grafa. Te informacije uvijek postoje, no ne nužno u željenom obliku. Atributom hoverinfo = "text" naznačuje se da će korisnik sam podesiti prikaz tih informacija, a potom se atributom text i definiraju, obično koristeći funkciju paste() kako bi se nanizali tekstualni zapisi i varijable u željenom redoslijedu.

iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>%
  add_markers(color = ~Species, hoverinfo = "text",
                 text = ~paste("Duljina latice:", Petal.Length, "\nŠirina latice:", Petal.Width))

Slika 33: Informacije koje se prikažu prelaskom miša preko grafa su uređene

Uočimo da je za argument text korišten složeni izraz koji u sebi sadrži dvije varijable podatkovnog okvira iris i funkciju paste(). U složenim izrazima koji mapiraju jednu ili više varijabli na plotly grafikon znak tilda ~ se uvijek stavlja ispred cijelog izraza.

Spajanje grafikona

Funkcijom subplot() nekoliko grafikona može se staviti u istu sliku. Argumentom nrows kontroliramo njihov poredak.

Osi grafikona iz iste slike mogu se dijeliti, tamo gdje to ima smisla, logičkim argumentom shareX, odnosno shareY. U tom slučaju će se uvećavanje dijela (engl. zoom) jednoga grafikona odraziti i uvećavanjem dijela drugoga grafikona kako bi osi ostale zajedničke.

Ako se osi ne dijele, standardna je postavka da se ne ispisuju imena osi, pa ih treba ručno dodati logičkim argumentom titleX i titleY.

Grafikoni sa zajedničkom osi y
g1 <- iris %>% plot_ly(x= ~Species, y = ~Petal.Length) %>% add_boxplot(color = ~Species)
g2 <- iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% add_markers(color = ~Species)

subplot(g1, g2, nrows = 1, shareY = TRUE, titleX = TRUE)

Slika 34: Oba grafikona dijele os y, čak i kada se uveća ili pomjeri slika na jednom od njih

Grafikoni s vlastitom osi y
g1 <- iris %>% plot_ly(x= ~Species, y = ~Petal.Length) %>% add_boxplot(color = ~Species)
g2 <- iris %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% add_markers(color = ~Species)

subplot(g1, g2, nrows = 1, titleY = TRUE, titleX = TRUE, margin = 0.08)

Slika 35: Slika s dvama neovisnim grafikonima

Grafikoni sa zajedničkom osi x
g1 <- iris %>% plot_ly(x = ~Species, y = ~Petal.Length) %>% add_boxplot(showlegend=FALSE)
g2 <- iris %>% plot_ly(x = ~Species, y = ~Petal.Width) %>% add_boxplot(showlegend=FALSE)

subplot(g1, g2, nrows = 2, shareX = TRUE, titleY = TRUE)

Slika 36: Oba grafikona dijele os x, čak i kada se uveća ili pomjeri slika na jednom od njih

Funkcija subplot() je prigodna i u situacijama kada faktorska varijabla ima mnogo razina pa bi prikaz bojama bio prešaren i nejasan. Umjesto toga, svaka razina faktora može se prikazati na zasebnom grafikonu. Skup grafikona može se u jednom potezu dobiti kombinacijom funkcija group_by() + do() gdje se unutar do() navodi “.” svaki put kada se poziva podatkovni okvir (na primjer data = .). do() je inače odlična funkcija za automatizaciju postupaka i izbjegavanja grešaka.

iris %>%
  group_by(Species) %>%
  do(plot = plot_ly(data = ., x = ~Petal.Length) %>%
       add_histogram(name = ~Species)) %>%
  subplot(nrows = 1, shareY = TRUE, titleX = TRUE)

Slika 37: Primjer korištenja funkcija subplot() za postavljanje nekoliko grafikona u jednu sliku

Ako se grafikoni preklapaju, mogu se razdvojiti argumentom margin, na primjer margin = 0.1.

Paket crosstalk

Na prethodnih nekoliko grafikona pokazano je kako različiti grafikoni na istoj slici mogu dijeliti osi pa se, na primjer, uvećavanjem jednoga grafikona uvećava i drugi grafikon. Bilo bi dobro kada bi se to svojstvo moglo proširiti i na selektiranje podataka.

Paket crosstalk omogućuje baš to, tj. omogućuje komunikaciju između različitih interaktivnih grafikona (opširnije html dodataka), u smislu da kada se odabere određeni podskup u jednom grafikonu, on će se automatski odabrati i u drugom. U engleskoj terminologiji koristi se izraz linked views kako bi se opisala ta veza.

Princip rada je svim povezanim grafikonima proslijediti isti podatkovni okvir koji je prethodno ‘obrađen’ funkcijom SharedData.

plotly_selected
library(crosstalk)

iris_shared <- SharedData$new(iris)

g1 <- iris_shared %>% plot_ly(x = ~Petal.Length) %>% add_histogram(color=~Species, showlegend = FALSE)
g2 <- iris_shared %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% add_markers(color = ~Species, showlegend = FALSE)

subplot(g1, g2, nrows = 1, titleY=TRUE, titleX = TRUE) %>% 
  highlight(on = "plotly_selected", off = 'plotly_deselect') %>%
  layout(title = "Odaberite proizvoljan podskup", 
         margin = list(t=100))

Slika 38: Povezivanje grafikona paketom crosstalk — odabirom podskupa na jednom grafikonu, odabire se isti podskup i na drugom

plotly_hover
iris_shared <- SharedData$new(iris)

g1 <- iris_shared %>% plot_ly(x = ~Petal.Length) %>% add_histogram(color=~Species, showlegend = FALSE)
g2 <- iris_shared %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% add_markers(color = ~Species, showlegend = FALSE)

subplot(g1, g2, nrows = 1, titleY=TRUE, titleX = TRUE) %>% 
  highlight(on = "plotly_hover", off = 'plotly_deselect') %>%
  layout(title = "Pređite mišem preko proizvoljnog elementa grafa", 
         margin = list(t=100))

Slika 38: Povezivanje grafikona paketom crosstalk — odabirom podskupa na jednom grafikonu, odabire se isti podskup i na drugom

plotly_click
iris_shared <- SharedData$new(iris)

g1 <- iris_shared %>% plot_ly(x = ~Petal.Length) %>% add_histogram(color=~Species)
g2 <- iris_shared %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% add_markers(color = ~Species, showlegend = FALSE)

subplot(g1, g2, nrows = 1, titleY=TRUE, titleX = TRUE) %>% 
  highlight(on = "plotly_click", off = 'plotly_doubleclick') %>%
  layout(title = "Kliknite na proizvoljan element grafa", 
         margin = list(t=100))

Slika 38: Povezivanje grafikona paketom crosstalk — odabirom podskupa na jednom grafikonu, odabire se isti podskup i na drugom

argumenti dynamic i persistant
iris_shared <- SharedData$new(iris)

g1 <- iris_shared %>% plot_ly(x = ~Petal.Length) %>% add_histogram(color=~Species, showlegend = FALSE)
g2 <- iris_shared %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% add_markers(color = ~Species, showlegend = FALSE)

subplot(g1, g2, nrows = 1, titleY=TRUE, titleX = TRUE) %>% 
  highlight(on = "plotly_select", 
            off = 'plotly_deselect',
            persistent = TRUE,
            dynamic =TRUE) %>%
  layout(title = "Odaberite proizvoljne podskupove i označite ih različitim bojama", 
         margin = list(t=100))

Slika 38: Povezivanje grafikona paketom crosstalk — odabirom podskupa na jednom grafikonu, odabire se isti podskup i na drugom

Ovakva komunikacija može se uspostaviti između bilo koja dva ili više html dodataka koji su kompatibilni s paketom crosstalk, pa je tako moguće kombinirati razne vrste grafikona, tablica, geografskih mapa itd. Osim funkcije subplot() može se koristiti i funkcija bscols() paketa crosstalk.

U gornjem kôdu može se uočiti dodatak funkcije highlight(). Njome se određuje kako će akcije korisnika utjecati na komunikaciju između grafikona. Tako postoji opcija on kojom se može odrediti što je okidač za selektiranje podataka. Argument on može poprimiti vrijednosti plotly_click, plotly_hover, plotly_selected. Dobro je navesti i argument off kao u gornjim primjerima da se naznači kako se vratiti na originalni prikaz, jer ponekad bez toga neće najbolje raditi.

Korisno je još spomenuti argument persistent kojim se mogu akumulirati odabrani podskupovi. Ovaj argument je standardno postavljen na FALSE jer se isti učinak može dobiti tipkom [Shift].

Argumentom dynamic možemo odrediti različite boje kojima će se istaknuti odabrani podskupovi, što je u kombinaciji s persistent = TRUE korisno za uspoređivanje različitih podskupova na istoj slici.

Ostali argumenti funkcije highlight() mogu se pronaći na poveznici o funkciji highlight.

Povezivanje vremenskih nizova

Kod vremenskih nizova poželjno je odabrati cijeli niz koji se odnosi na neku kategoriju, a ne samo jednu točku toga niza, zato se povezivanje ovakvih grafikona obično radi uz jedno dodatno svojstvo.

Za idući primjer koristit ćemo podatkovni okvir txhousing ugrađen u R.

data(txhousing)
str(txhousing)
tibble [8,602 x 9] (S3: tbl_df/tbl/data.frame)
 $ city     : chr [1:8602] "Abilene" "Abilene" "Abilene" "Abilene" ...
 $ year     : int [1:8602] 2000 2000 2000 2000 2000 2000 2000 2000 2000 2000 ...
 $ month    : int [1:8602] 1 2 3 4 5 6 7 8 9 10 ...
 $ sales    : num [1:8602] 72 98 130 98 141 156 152 131 104 101 ...
 $ volume   : num [1:8602] 5380000 6505000 9285000 9730000 10590000 ...
 $ median   : num [1:8602] 71400 58700 58100 68600 67300 66900 73500 75000 64500 59300 ...
 $ listings : num [1:8602] 701 746 784 785 794 780 742 765 771 764 ...
 $ inventory: num [1:8602] 6.3 6.6 6.8 6.9 6.8 6.6 6.2 6.4 6.5 6.6 ...
 $ date     : num [1:8602] 2000 2000 2000 2000 2000 ...

Radi se o podatkovnom okviru od 8.602 zapisa i devet varijabli o mjesečnoj prodaji nekretnina u teksaškim gradovima između 2000. i 2015. godine. Za naš primjer izdvojit ćemo samo pet nasumično odabranih gradova. Prije toga ćemo izraditi novu varijablu datum u kojoj ćemo spojiti godinu i mjesec (varijable year i month).

#izrada nove varijable datum
txhousing <- mutate(txhousing, datum = as.character(paste0(year,"-", month))) 
#odabir pet nasumičnih gradova
set.seed(1)
gradovi <- sample(txhousing$city, 5)
housing5 <- txhousing %>% filter(city %in% gradovi) 

Prikažimo na jednom grafikonu kretanje prodaje nekretnina (varijabla sales) za svaki od pet odabranih gradova, a na drugom ukupan volumen prodaje (varijabla volume) postignut u danom vremenskom periodu.

Argument key
housing5_shared <- txhousing %>% filter(city %in% gradovi) %>% 
  SharedData$new(key = ~city)

g1 <- housing5_shared %>% 
  plot_ly(x = ~datum, y = ~sales) %>%
  add_lines(color = ~city,
            showlegend = FALSE)


g2 <- housing5_shared %>% 
  plot_ly(x = ~city, y = ~volume) %>%
  add_bars(color = ~city, 
           showlegend = FALSE)
  
subplot(g1, g2)%>% 
    highlight(on = "plotly_click",
              off = 'plotly_doubleclick') %>% 
  layout(title = "Kliknite mišem za odabir proizvoljnog podskupa",
         margin = list(t=100))

Slika 39: Povezivanje linijskih grafova s drugim grafikonom uz pomoć argumenta key

Bez argumenta key (klikom)
housing5_shared <- txhousing %>% filter(city %in% gradovi) %>% 
  SharedData$new()

g1 <- housing5_shared %>% 
  plot_ly(x = ~datum, y = ~sales) %>%
  add_lines(color = ~city,
            showlegend = FALSE)


g2 <- housing5_shared %>% 
  plot_ly(x = ~city, y = ~volume) %>%
  add_bars(color = ~city, 
           showlegend = FALSE)
  
subplot(g1, g2)%>% 
    highlight(on = "plotly_click",
              off = "plotly_doubleclick") %>% 
  layout(title = "Kliknite mišem za odabir proizvoljnog podskupa",
         margin = list(t=100))

Slika 40: Bez argumenta key komunikacija između grafikona se ne može uspostaviti klikanjem

Bez argumenta key (povlačenjem miša)
housing5_shared <- txhousing %>% filter(city %in% gradovi) %>% 
  SharedData$new()

g1 <- housing5_shared %>% 
  plot_ly(x = ~datum, y = ~sales) %>%
  add_lines(color = ~city,
            showlegend = FALSE)


g2 <- housing5_shared %>% 
  plot_ly(x = ~city, y = ~volume) %>%
  add_bars(color = ~city, 
           showlegend = FALSE)
  
subplot(g1, g2)%>% 
    highlight(on = "plotly_select",
              off = "plotly_deselect") %>% 
  layout(title = "Povlačenjem miša odaberite proizvoljan podskup",
         margin = list(t=100))

Slika 41: Povlačenjem miša moguće je odabrati podskup na stupčastom grafikonu i prikazati ga na linijskom grafikonu, ali ne i obrnuto

Da bi selektiranje podataka radilo i da bi se moglo odraziti na oba grafikona istovremeno, potrebno je funkciji SharedData$new() dodati argument key kojim se definira ključ za povezivanje svih točaka koje pripadaju navedenoj kategoriji. U tom slučaju nisu mogući djelomični odabiri vremenskoga niza, nego cijeli niz.

Bez dodavanja ključa moguće je eventualno odabrati podskup na stupčastom dijagramu povlačenjem miša, ali ne i klikom miša.

Izbornik s filtrima

Osim selektiranjem izravno iz grafa ili iz legende, podaci se mogu filtrirati i preko izbornika, potvrdnog okvira (engl. chechbox) ili klizača (engl. slider). Odabirom preko ovakvih izbornika, podaci se filtriraju na svim grafikonima koji dijele iste SharedData-podatke, za razliku od legende, preko koje se selektiraju samo na onom grafikonu na koji se odnose.

Za aktiviranje ovih opcija koristi se funkcija bscols() paketa crosstalk, kojoj se dodaju filtri:

  • bscols(filter_select(id =, label=, sharedData = , group = ~)) — za izbornik
  • bscols(filter_checkbox(id =, label=, sharedData = , group = ~)) — za potvrdni okvir
  • bscols(filter_slider(id =, label=, sharedData = , column = ~)) — za klizač.

Izbornik
iris_shared <- SharedData$new(iris)

g1 <- iris_shared %>% plot_ly(x = ~Petal.Length) %>% 
  add_histogram(color=~Species, showlegend = FALSE) %>% 
  layout(xaxis = list(range = c(0,8)),
         yaxis = list(range = c(0,45)))

g2 <- iris_shared %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers(color = ~Species, showlegend = FALSE) %>% 
  layout(xaxis = list(range = c(0,3.5)),
         yaxis = list(range = c(0,7.5)))


bscols(filter_select(id = "Species", 
                     label= "Odaberi vrstu", 
                     sharedData = iris_shared, 
                     group = ~Species), 
       g1, 
       g2, 
       widths = c(2, 5, 5))
Potvrdni okvir
iris_shared <- SharedData$new(iris)

g1 <- iris_shared %>% plot_ly(x = ~Petal.Length) %>% 
  add_histogram(color=~Species, showlegend = FALSE) %>% 
  layout(xaxis = list(range = c(0,8)),
         yaxis = list(range = c(0,45)))

g2 <- iris_shared %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers(color = ~Species, showlegend = FALSE) %>% 
  layout(xaxis = list(range = c(0,3)),
         yaxis = list(range = c(0,7.5)))


bscols(filter_checkbox(id = "Species", 
                       label= "Vrsta cvijeta", 
                       sharedData = iris_shared, 
                       group = ~Species), 
       g1, 
       g2, 
       widths = c(2, 5, 5))
Klizač
iris_shared <- SharedData$new(iris)

g1 <- iris_shared %>% plot_ly(x = ~Petal.Length) %>% 
  add_histogram(color=~Species, showlegend = FALSE) %>% 
  layout(xaxis = list(range = c(0,8)),
         yaxis = list(range = c(0,45)))

g2 <- iris_shared %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers(color = ~Species, showlegend = FALSE) %>% 
  layout(xaxis = list(range = c(0,3)),
         yaxis = list(range = c(0,7.5)))


bscols(filter_slider(id = "Petal.Length", 
                     label= "Duljina latica", 
                     sharedData = iris_shared, 
                     column = ~Petal.Length), 
       g1, 
       g2, 
       widths = c(3, NA, NA))
Kombinacija
iris_shared <- SharedData$new(iris)

g1 <- iris_shared %>% plot_ly(x = ~Petal.Length) %>% 
  add_histogram(color=~Species, showlegend = FALSE) %>% 
  layout(xaxis = list(range = c(0,8)),
         yaxis = list(range = c(0,45)))

g2 <- iris_shared %>% plot_ly(x = ~Petal.Width, y = ~Petal.Length) %>% 
  add_markers(color = ~Species, showlegend = FALSE) %>% 
  layout(xaxis = list(range = c(0,3)),
         yaxis = list(range = c(0,7.5)))

bscols(widths = c(12, 12, 12), 
       filter_select(id = "Vrsta", label= "Odaberi vrstu", sharedData = iris_shared, group = ~Species),
       filter_slider(id = "PetalLength", label= "Duljina latica", sharedData = iris_shared, column = ~Petal.Length), 
       filter_checkbox(id = "Species", label= "Vrsta cvijeta", sharedData = iris_shared, group = ~Species)) %>% 
  bscols(., g1, g2, widths = c(3, NA, NA))

Argumentom widths se raspodijeli koliko stupaca od ukupno njih 12 odlazi na koji element prikaza. U gornjem primjeru iz prve kartice prva dva stupca rezervirana su za filtar, idućih pet za grafikon g1, a zadnjih pet za grafikon g2.

U zadnjoj kartici iskombinirane su dvije funkcije bscols() gdje su u prvoj sva tri filtra posložena u jedan stupac (jer su sve širine 12), a zatim se taj stupac proslijedio u prvi stupac druge funkcije bscols().

Animacije

Animacije su dinamički grafikoni gdje se promjene u grafičkom prikazu izvode automatski. Jedina potrebna intervencija korisnika je pritisak dugmeta Start kojim se pokreće animacija.

Animacije su izrađene kao i animirani filmovi, od slika koje se u kratkim vremenskim razmacima i točno određenom redoslijedu izmjenjuju na ekranu. Svaka pojedina sličica od koje je izrađena animacija zove se kadar (engl. frame).

U našem slučaju, svaki kadar će biti plotly grafikon u jednoj vremenskoj ili prostornoj točki. Koristeći glatke prijelaze (engl. smooth transition), kadrovi se spajaju u animaciju.

Kadar je najčešće vremenska varijabla, no općenito može biti neka faktorska varijabla.

Za potrebe demonstracije ponovno ćemo koristiti podatkovni okvir txhousing. Ovaj put uredit ćemo ga tako da podatke sortiramo kronološki i abecedno po gradovima. Varijabla datum koju smo ranije izradili poslužit će nam za određivanje kadara tako da će svi podaci koji se odnose na određeni mjesec određene godine pripadati istom kadru.

txhousing <- arrange(txhousing, year, month, city)

S obzirom na to da se radi o 46 gradova i nizu od skoro 16 godina, animacija bi dugo trajala pa ćemo je za sada skratiti na 15 gradova s najvećim obujmom prodaje i vremenskim intervalom od zadnjih pet godina. Novi podatkovni okvir nazvat ćemo tx_bubble.

# ako je potrebno: library(dplyr)
txhousing %>% filter(year==2015, month == 1) %>% top_n(15,sales) %>% select(city) %>% unlist() -> gradovi
tx_bubble <- txhousing %>% filter(city %in% gradovi, year > 2010)
animacija <- tx_bubble %>% plot_ly(x = ~listings, y = ~sales) %>%
  add_markers(frame = ~datum, 
              ids = ~city,
              size = ~volume, 
              color = ~city, 
              marker = list(sizemode = "diameter"))

animacija

Slika 46: Primjer mjehuričaste animacije (engl. bubble chart): Pritisnite dugme Play

Glavnu ulogu u animaciji ima argument frame. Njemu se pridružuje varijabla koja određuje kako su kadrovi podijeljeni, a u našem slučaju to je varijabla datum.

Idući korisni argument je ids, u našem slučaju ids = city. On pomaže da se u svakom kadru istoj točki pridruži vrijednost za isti grad u slučaju nedostajućih vrijednosti ili nekonzistentnog redoslijeda u podacima. Ako su podaci savršeno uređeni i nema nedostajućih vrijednosti, onda je ovaj argument suvišan.

U našem podatkovnom okviru postoje nedostajuće vrijednosti, i sljedeći primjer pokazuje slučaj pobrkanih balončića ako izostavimo argument ids. Pažnju obratite na početno razdoblje oko 2011-4, i na samom kraju animacije gdje nedostaju podaci za grad Corpus Christi.

tx_bubble %>% plot_ly(x = ~listings, y = ~sales) %>%
  add_markers(frame = ~datum, 
              #ids = ~city,
              size = ~volume, 
              color = ~city, 
              marker = list(sizemode = "diameter")) 

Slika 47: Mjehuričasta animacija s nedostajućim vrijednostima ne radi ‘glatko’ bez argumenta ids

Ovakve animacije, u engleskoj terminologiji poznate kao bubble charts, obično prikazuju tri do četiri varijable, no treba uvijek biti oprezan da se ne pretjera i da gledatelj može percipirati sve varijable.

U gornjem primjeru prikazano je čak pet varijabli. Osim vremenske varijable, prikazane su listings i sales (broj nekretnina na tržištu i broj prodanih nekretnina) na koordinatnim osima, volumen prodaje je prikazan promjerom krugova dok su gradovi reprezentirani bojom.

Uređivanje animacija

Animacije se mogu dodatno urediti funkcijama animation_opts(), animation_slider() i animation_button() tako da im se podesi vrijeme između kadrova, vrsta prijelaza između kadrova, postavke o klizaču, dugmetu Play i ostalome.

animacija %>% 
  animation_opts(frame = 500,         # milisekunde od početka jednog do početka drugog kadra
                 transition = frame,  # vrijeme prijelaza izmedu kadrova (u milisek.)
                 easing = c("linear","bounce","quad","cubic","sin","exp","circle","elastic","back"),
                 redraw = FALSE)      # da li cijela grafika treba biti ponovo nacrtana

Ako se želi dodati osjećaj pauze između kadrova, vrijednost za transition postavi se na vrijednost manju od vrijednosti argumenta frame. Standardno je transition jednak frame tako da nema pauze.

Easing je vrsta glatkog prijelaza od jednog kadra do drugog. Putanja točke može poprimit razne oblike i efekte poput pravca, sinusoide, kvadratne krivulje, odudaranja, itd. Svakoj varijanti se može dodati opcija -in, -out ili -in-out kako bi se odredilo odnosi li se kretnja na ulazak i/ili izlazak iz trenutačne pozicije.

Redraw se odnosi na ponovno crtanje cijele grafike. To je uglavnom potrebno tamo gdje svi grafički elementi nisu prenosivi, no ako je nepotrebno, može se ugasiti s obzirom na to da može usporiti animaciju.

easing = “back”
animacija %>% 
  animation_opts(easing = "back")

Slika 48: Prikaz prijelaza easing = ‘back’

easing = “bounce-in-out”
animacija %>% 
  animation_opts(easing = "bounce-in-out")

Slika 49: Prikaz prijelaza easing = ‘bounce-in-out’

Pauze između kadrova
animacija %>% 
  animation_opts(frame = 500,
                 transition = 300)

Slika 50: Animacija s pauzama između kadrova

Klizač i dugme Start
animacija %>%
  animation_slider(currentvalue = list(font = list(color="blue", size = 18))) %>% 
  animation_button(x = 0, xanchor="left", y = 1, yanchor = "bottom")

Slika 51: Prikaz animacije s dugmetom Start na vrhu osi y i uređenim natpisom na klizaču

Animaciju je, kao i sve ostale grafikone, moguće urediti prema svim prije spomenutim uputama. U nastavku se nalazi primjer sa svim gradovima iz podatkovnog okvira txhousing gdje je graf uređen na način da je izostavljena vremenska traka, odnosno klizač, dok se vrijeme prikazuje u pozadini grafikona. Dugme Start postavljeno je na vrhu grafikona, raspon osi y proširen je da se mjehurići ne odsijeku iz prikaza, dok je os x transformirana u logaritamsku skalu kako bi joj razvukli lijevi kraj, tj. kako bi se bolje vidjela gusta skupina ‘mjehurića’ na manjim vrijednostima osi x. Dodani su naslovi osi i naslov grafikona na hrvatskom jeziku.

Nedostajuće vrijednosti nadomještene su iz prethodnog mjeseca tamo gdje postoje, kako bi svi prijelazi između kadrova bili glatki. Na taj će način gradovi kojima nedostaju vrijednosti ostati na istom mjestu do idućeg kadra gdje podaci postoje. Prije toga smo pomoću funkcije table() provjerili da se svi gradovi pojavljuju u svakom mjesecu, tako da znamo da je redoslijed gradova konzistentan. Ovakvo nadomještanje podataka nije savršeno, ali će poslužiti za demonstraciju animacije.

# nedostajuće vrijednosti ćemo, za potrebe animacije, nadomjestiti s vrijednostima iz mjeseca prije kako bi mjehurići u takvim situacijama mirovali na mjestu.

for (i in 47:nrow(txhousing)){
  if (is.na(txhousing$listings[i]) | is.na(txhousing$sales[i])) {
    txhousing$listings[i] <- txhousing$listings[i-46]
    txhousing$sales[i] <- txhousing$sales[i-46]
    txhousing$volume[i] <- txhousing$volume[i-46]
  }
}
# Prikaz za zadnjih pet godina: 

txhousing %>% filter(year > 2010) %>% 
  
  plot_ly(x = ~listings, y = ~sales) %>%
  
  add_text(x = 3000, y = 7000, 
           text = ~format(as.Date(paste0(year, "-", month, "-01")), "%b, %Y"), 
           frame = ~datum, 
           textfont = list(size = 90, color = toRGB("gray90")),
           showlegend = FALSE) %>% 
  
  add_markers(frame = ~datum, 
              ids = ~city,
              size = ~volume, 
              color = ~city, 
              marker = list(sizemode = "diameter"),
              showlegend = FALSE) %>% 
  
  animation_slider(hide = TRUE) %>% 
  
  animation_button(x = 0.05, xanchor="left", y = 0.95, yanchor = "bottom") %>%
  
  layout(title = list(text = "Animacija mjehurića",
                      font = list(size = 26),
                      x = 0.5,
                      y = 0.9),
         yaxis = list(range = c(0, 11000),
                      title = "Broj prodanih nekretnina u TX"),
         xaxis = list(title = "Broj izlistanih nekretnina na tržištu u Texasu",
                      range = c(2, 5),
                      type = "log"))

Slika 52: Prikaz animacije bez klizača, s prikazom vremena u pozadini grafikona

Više o atributima animacije može se pronaći, između ostalog, i na poveznici o atributima animacije.

Akumulirane animacije

Izdvojimo sada samo Houston i prikažimo dinamičkim grafikonom kretanje prodaje nekretnina u tom gradu kao animirani vremenski niz.

Obična putanja
#library(dplyr) - ako je potrebno
#library(purrr) - ako je potrebno

houston <- txhousing %>% filter(city == "Houston") %>% group_by(year) %>% summarise(Sales = sum(sales))


 houston %>% split(.$year) %>%   
  accumulate(~bind_rows(.x, .y)) %>%
  bind_rows(.id ="frame") %>%
  plot_ly(x = ~ year, y = ~Sales) %>%
   add_lines(frame = ~frame, 
             showlegend = FALSE) %>%
   layout(title =  "Kretanje godišnjeg broja prodanih nekretnina u Houstonu",
          margin = list(t = 100)) %>%
   animation_opts(frame = 500, 
                  transition = 0, 
                  redraw = FALSE)

Slika 53: Primjer akumulirane animacije

Ispunjena površina ispod putanje

Za ispunjenu površinu ispod grafa potrebno je dodati argument fill. U ovom slučaju podešen je na “tozeroy”, odnosno na popunjavanje do osi x (\(y = 0\)). Boja ispune može se zadati argumentom fillcolor.

Uz to je dodana funkcija animation_opts() kako bi se vrijeme tranzicije uklonilo. Možete isprobati kakva je animacija bez toga.

 houston %>% split(.$year) %>%   
  accumulate(~bind_rows(.x, .y)) %>%
  bind_rows(.id ="frame") %>%
  plot_ly(x = ~ year, y = ~Sales, 
          fill = 'tozeroy') %>%
   add_lines(frame = ~frame, 
             showlegend = FALSE) %>%
   layout(title =  "Kretanje godišnjeg broja prodanih nekretnina u Houstonu",
          margin = list(t = 100)) %>%
   animation_opts(transition = 0, 
                  redraw = FALSE)

Slika 54: Primjer akumulirane animacije s ispunjenom površinom ispod grafa

Kadar akumulirane animacije mora sadržavati podatke za sadašnju vremensku točku, ali i za sve prijašnje vremenske točke. Radi toga se podatkovni okvir mora transformirati na takav način kao da, na primjer, jednostavan niz 1; 2; 3 proširimo u niz 1; 1, 2; 1, 2, 3. Za takvu transformaciju koriste se funkcije accumulate() iz paketa purrr i bind_rows() iz paketa dplyr (oba paketa pripadaju kolekciji tidyverse).

Karte

Plotly podržava nekoliko vrsta vizualizacija podataka projiciranih na geografske karte. U ovoj lekciji spomenut ćemo kartograme (engl. choropleth) i raspršene grafikone čija je pozadina proizvoljni stil i isječak iz geografske karte.

Kartogrami

Kartogrami su općenito sve tematske karte na kojima su shematski, simbolima ili određenim grafičkim rješenjima prikazani traženi statistički podatci. U ovom poglavlju bavit ćemo se geografskim kartama s ucrtanim granicama svih država svijeta, gdje različitim bojama možemo predočiti i uspoređivati vrijednosti zadane po državama.

Kartogram se može dobiti funkcijom plot_geo() ili plot_ly(type='choropleth').

Ta funkcija u sebi ima već ugrađene političke karte svih država svijeta (prema bazi Natural Eartha), a za Sjedinjene Američke Države postoji i podjela po svakoj državi članici. Za detaljnije karte, kao na primjer županije u Hrvatskoj, potrebne su vanjske GeoJSON-datoteke.

Ugrađene karte

U primjeru ćemo pokazati procijenjeni broj stanovnika svake države u 2020. godini i to usporediti s UN-ovom projekcijom za 2100. godinu. Podaci se mogu preuzeti iz datoteke populacija.csv.

Ugrađena karta svijeta raspoznaje države po troslovnom kôdu ISO standarda pa su i te informacije dodane ovim podacima.

populacija <- read.csv("podaci/populacija.csv", encoding = "UTF-8")
head(populacija)
  X   Drzava Kod  X2020  X2030  X2040  X2050  X2060  X2070  X2100
1 1  Burundi BDI  11891  15773  20253  25325  30701  36107  50904
2 2  Comoros COM    870   1063   1266   1472   1666   1841   2187
3 3 Djibouti DJI    988   1117   1217   1295   1346   1364   1332
4 4  Eritrea ERI   3546   4240   5114   6005   6836   7605   9062
5 5 Ethiopia ETH 114964 144944 175466 205411 232994 256441 294393
6 6    Kenya KEN  53771  66450  79470  91575 102398 111411 125424

Populacija 2020.godine
populacija %>% 
  plot_ly(type='choropleth',
          locations = populacija$Kod,
          z = populacija$X2020,
          colors = "BuGn")%>% 
  layout(title = "Procjena broja stanovnika za 2020. godinu") %>% 
  colorbar(title = "Broj \nstanovnika")

#------------ Drugi način ---------------#

populacija %>% 
  plot_geo() %>%
  add_trace(z= ~X2020, 
            locations = ~Kod,
            colors = "BuGn") %>% 
  layout(title = "Procjena broja stanovnika za 2020. godinu") %>% 
  colorbar(title = "Broj \nstanovnika")

Slika 55: Kartogram s prikazom broja stanovnika u 2020. godini po državama

Populacija 2100.godine
populacija %>% plot_geo() %>%
  add_trace(z= ~X2100, 
            locations = ~Kod,
            colors = "BuGn") %>% 
  layout(title = "Procjena broja stanovnika za 2100. godinu",
         margin = list(t = 100)) %>% 
  colorbar(title = "Broj \nstanovnika")

Slika 56: Kartogram s prikazom procjene broja stanovnika u 2100. godini po državama

Razlike
populacija %>% plot_geo() %>%
  add_trace(z= ~(X2100-X2020), 
            locations = ~Kod, 
            colors = c("green", "white", "red")) %>% 
  layout(title = "Procjena promjene broja stanovnika \nizmeđu 2020. i 2100. godine",
         margin = list(t = 100)) %>% 
  colorbar(title = "Razlike")

Slika 57: Kartogram s prikazom prirasta stanovnika u idućih 80 godina po državama, s vlastitim izborom spektra boja

Karte se mogu dodatno prilagoditi potrebama korisnika preko argumenta geo unutar funkcije layout(). Tom argumentu pridružuje se lista atributa od kojih ćemo navesti samo neke:

  • scope = world"| "usa"| "europe"| "asia"| "africa"| "north america"| "south america" — prikazuje samo naznačeni kontinent
  • center = list(lat = ~c.lat, lon = ~c.lon) — centriranje slike na tim koordinatama
  • showlake = TRUE/FALSE — prikaz jezera
  • lakecolor = toRGB("boja") — podešavanje boje jezera ako je showlake = TRUE
  • showcountries = TRUE/FALSE — prikaz međudržavnih granica
  • showframe = TRUE/FALSE — prikaz okvira oko slike
  • showcoastlines = TRUE/FALSE — prikaz granica koje uključuju i mora

Legenda se može podesiti funkcijom colorbar(), a ukloniti funkcijom hide_colorbar().

populacija %>% plot_geo() %>%
  
  add_trace(z= ~(X2100-X2020), 
            locations = ~Kod, 
            colors = "RdBu") %>% 
  
  layout(title = "Procjena promjene broja stanovnika u Africi \nizmeđu 2020. i 2100. godine",
         margin = list(t = 100),
         geo = list(scope = "africa",
                    showlakes = TRUE,
                    lakecolor = toRGB("SeaGreen"),
                    showframe = FALSE,
                    showcoastlines = FALSE)) %>% 
  
  colorbar(title = "Razlike")

Slika 58: Kartogram koji sadrži samo afrički kontinent, s vlastitim bojama i označenim jezerima

GeoJSON

Za granice koje nisu ugrađene u plotly, potrebna je GeoJSON datoteka koja sadrži jedinstveni kôd za svako područje te koordinate njegovih granica. Ona se proslijedi argumentu geojason, a može biti url ili lokalno pohranjena datoteka. Argumentu featureidkey proslijedi se ime varijable koja nosi jedinstveni ključ svakoga područja, u slučaju da postoji više opcija. Varijable sa svojstvima područja se inače mogu izlistati pomoću naredbe ime$features[[1]] i tako odabrati ključ. Treba samo paziti da su ti ključevi navedeni i u podatkovnom okviru iz kojeg se mapiraju lokacije (argument locations).

U sljedećem primjeru pokazat ćemo kartu županija Republike Hrvatske prema granicama definiranima na poveznici navedenoj u funkciji fromJSON(). Karta nije precizna i ima svojih mana, no ovdje se svakako samo želio pokazati primjer korištenja takve vrste datoteke.

Podaci mapirani na kartu mogu se preuzeti iz datotke zupanije.csv.

#library(rjson)

zupanije_JS <- fromJSON(file = "http://alas.matf.bg.ac.rs/~mi09109/hrv_regional.geojson")


zupanije <- read.csv2("podaci/zupanije.csv", stringsAsFactors = FALSE, encoding = "UTF-8", header = TRUE,colClasses=c(provnum_ne="character"))



zupanije %>% plot_ly() %>%
  add_trace(z = ~Pop.2011,
            type="choropleth",
            geojson=zupanije_JS,
            featureidkey="properties.provnum_ne",
            locations = zupanije$provnum_ne,
            showlegend = FALSE,
            hoverinfo = "text",
            text = ~paste(name, "\nPopulacija:", format(Pop.2011, big.mark = ","))) %>% 
  layout(geo = list(fitbounds = "locations",
                    showcoastlines = FALSE,
                    showframe = FALSE)) %>% 
  colorbar(title = "Broj \nstanovnika")

Slika 59: Kartogram koji sadrži hrvatske županije dobivenim iz vanjske GEOjason datoteke

Scattermapbox

Scattermapbox je vrsta raspršenoga grafa čija je pozadina geografska karta mapbox. Mapbox je dobar izvor raznih vrsti geografskih karata za izradu aplikacija i web-sadržaja, a više o slojevima i tipovima karata koje se mogu preuzeti može se pronaći na poveznici o mapbox-kartama u R-u ili poveznici o mapbox-slojevima u R-u.

Pomoću funkcije scattermapbox() točke, linije i drugi simboli mogu se mapirati na kartu varijablama lon i lat što predstavlja njihovu geografsku širinu i dužinu.

lokacija_srca = data.frame(Latitude=45.792312,Longitude=15.969617, Ime = "Srce")


lokacija_srca %>%  
  plot_ly(type = 'scattermapbox',
          lat = ~Latitude,
          lon = ~Longitude,
          marker = list(color = "red", size = 10),
          coloraxis = 'coloraxis',
          hoverinfo = "text",
          text = " SRCE ") %>%
  layout(title = "Lokacija Srca",
         mapbox = list(style="open-street-map",
                       zoom =14,
                       center= list(lon=15.9696, 
                                    lat=45.7923)))

Slika 60: Mapbox s označenom lokacijom Srca

Više o raznim vrstama mapa može se saznati na poveznici o mapama paketa plotly.

Za kraj ove lekcije ostaje još dokument koji sadrži sažetak osnovnih pojmova vezanih uz plotly: plotly cheet sheat.