6  Interaktívna vizualizácia

Vrátime sa ku vizualizácii. Videli sme, že balík ggplot2 produkuje elegantnú grafiku, ktorou môžeme bez hanby prezentovať svoje analýzy. Využiteľnosť grafickej informácie sa však za určitých okolností dá ešte zvýšiť – a to interaktivitou. Tým rozumieme napr. zväčšenie časti grafu (zoom), vyvolanie bublinovej nápovedy (tooltip) nájazdom kurzora, voľbu zobrazených premenných pomocou zaškrtávacieho zoznamu (checklist) či voľbu rozsahu pomocou posuvníkov (slider).

Inšpiráciou tejto kapitoly je veľké množstvo aplikácií interaktívnej grafiky na internete – počnúc rozsiahlou centrálnou galériou.
Praktický úvod a návod ku nástrojom HTML widgets a Shiny poskytuje napr. príspevok (Engel 2019, kap. 2).

Pochopiteľne, zobrazenie interaktívnych grafov je v dokumente typu HTML obmedzené a v PDF úplne statické. Preto je na čitateľovi tejto učebnice, aby si nasledujúce príklady sám vyskúšal.

6.1 ggvis

Jedným z ideových nasledovníkov balíku ggplot2 a celkovo gramatických pravidiel grafiky je balík ggvis (Chang a Wickham 2020). V skladaní komponentov ide ešte ďalej, integruje reťazenie príkazov (pipe) a pridáva napr. aj interaktívne prvky. Celý koncept je však zatiaľ skôr hudbou budúcnosti, pretože ďalší vývoj balíka je dočasne pozastavený v prospech dolaďovania súčasných projektov z ekosystému tidyverse.

Nasledujúci kód – pre začiatok – zobrazí jednoduchý bodový graf bez interaktívnych prvkov:

library(ggvis)
mtcars |>
  ggvis(x = ~disp, y = ~mpg) |>    # definuje úlohy
  dplyr::mutate(disp = disp / 61.0237) |> # konvertuje objem valcov na litre
  layer_points()   # pridá geometrickú vrstvu (body)

Ďalší už pre nastavenie histogramu (šírky intervalu) pridá posuvník (slider):

mtcars |> 
  ggvis(~wt) |> 
  layer_histograms(width =  input_slider(0, 2, step = 0.10, label = "width"))
detach("package:ggvis", unload = TRUE)

Okrem interaktívneho elementu input_slider sa núkajú aj input_checkbox, input_checkboxgroup, input_numeric, input_radiobuttons, input_select a input_text, ale napr. aj add_tooltip. Podrobnejší návod sa dá nájsť napr. na domácej stránke projektu.

Pod kapotou ggvis používa nízkoúrovňový jazyk Vega, ktorý podobne ako ggplot implementuje Wilkinsonovu grafickú gramatiku ale rozširuje ju o interaktívne prvky.

6.2 HTML widgets

JavaScript je pravdepodobne najviac využívaný skriptovací jazyk na tvorbu interaktívnych webstránok. Balík htmlwidgets poskytuje framework na prepojenie R s rôznymi interaktívnymi JavaScript-ovými knižnicami. Takto vytvorené interaktívne komponenty (widgety, prekl. aj ako vecičky) sa dajú:

  • využiť v príkazovom riadku R podobne ako tradičné grafy (v RStudiu cez Viewer),
  • zakomponovať do R Markdown dokumentov a Shiny web aplikácií,
  • uložiť ako samostatné webové stránky na jednoduché zdielanie cez email, cloudové úložiská a pod.

6.2.1 Plotly

Jedným z najužitočnejších balíkov v tejto triede je plotly, pretože na interaktívny dokáže premeniť akýkoľvek graf ggplot. Pohrajte sa s aktívnymi prvkami grafu, napríklad vypínanie/zapínanie vrstiev, zoomovanie atď.:

library(ggplot2)
p <- ggplot(data = mpg, mapping = aes(x = displ, y = cty, color = drv)) + 
  geom_point()
detach("package:ggplot2")
library(plotly)
ggplotly(p)

Balík má však aj svoje vlastné vysokoúrovňové funkcie, napríklad

plot_ly(data = ggplot2::mpg, 
        x = ~displ, y = ~cty, color = ~drv,
        type = "scatter", mode = "markers") |>  
  layout(
    title = "Závislosť dojazdu od objemu podľa typu náhonu",
    legend = list(title = list(text = '<b> drv </b>')))

ktoré zvládnu aj 3D (vrátane možnosti rotácie):

plot_ly(data = ggplot2::mpg, 
        x = ~displ, y = ~cty, z = ~cyl, color = ~drv, 
        size = 20,
        type = "scatter3d", mode = "markers")

Zobrazením premennej na grafický atribút frame je možné vytvoriť animáciu.

gapminder::gapminder |> 
  plot_ly(
    x = ~gdpPercap, 
    y = ~lifeExp, 
    size = ~pop, 
    color = ~continent, 
    frame = ~year, 
    text = ~country, 
    hoverinfo = "text",
    fill = ~''  # toto rieši záplavu nezmyselných varovných hlásení o line.width
  ) |> 
  add_markers() |> 
  layout(          # https://plotly.com/r/reference/index/
    xaxis = list(  # https://plotly.com/r/reference/layout/xaxis/#layout-xaxis
      type = "log",
      title = "HDP na hlavu"
      ),
    yaxis = list(title = "Priemerná dĺžka života")
  ) |> 
  animation_slider( currentvalue = list(prefix = "Rok ") )  # iba formátovanie
detach("package:plotly", unload = TRUE)

Paleta grafov (a ich nastavení) v plotly je rozsiahle – od základných a štatistických grafov, cez špecificky vedecké, finančnícke, geografické mapy až po 3D (body, povrch, vrstevnice). Pre podrobný prehľad odporúčame pozrieť domovskú stránku plotly.com/r.

6.2.2 Iné

Na domovskej stránke htmlwidgets.org v sekcii Showcase nájdeme výber z bohatej triedy htmlwidgets, rozsiahlejší zoznam potom v sekcii Gallery. Mnohé z nich sa spomínajú aj v časti Interactive charts na stránke R Graph Gallery. Pre nás neskôr môžu byť užitočné napr. nasledujúce widgety:

  • 3D graf pomocou rglwidget je alternatíva ku plotly, ale vďaka nasvieteniu bodov je priestorový dojem lepší. Keďže tento widget na pozadí využíva balík rgl, je štýl i syntax podobný základnej grafike R. Pridávanie ďalších interaktívnych prvkov okrem rotácie je ťiež trochu komplikovanejšie než pri plotly.
library(rgl)
This build of rgl does not include OpenGL functions.  Use
 rglwidget() to display results, e.g. via options(rgl.printRglwidget = TRUE).
with(iris, rgl::plot3d(Sepal.Length, Sepal.Width, Petal.Length, 
                  type="s", col=as.numeric(Species)))
rgl::rglwidget()
  • Graf časových radov je pomocou dygraph efektnejší a analýza efektívnejšia.
dygraphs::dygraph(nhtemp, main = "New Haven Temperatures") |> 
  dygraphs::dyRangeSelector(dateWindow = c("1920-01-01", "1960-01-01"))


  • Mapy sú základom priestorovej analýzy geoúdajov, widget leaflet dokáže okrem posunu a priblíženia pridať mapové vrstvy, značky, línie, polygóny, vyskakovacie okná, vytvárať tématické mapy a pracovať s rôznymi druhmi projekcií.
leaflet::leaflet() |>
  leaflet::addTiles() |>  # defaultné OpenStreetMap mapové diely
  leaflet::addMarkers(lng=17.11526, lat=48.15198, popup="Stavebná fakulta STU")


  • Výnimka medzi grafmi, no iste užitočná vecička, je interaktívna tabuľka pomocou balíkov DT alebo reactable. Niektoré v sebe dokážu integrovať aj grafy (reactablefmtr).
DT::datatable(mtcars, options = list(pageLength = 5))


Zaujímavým doplnkom ku HTML widgets je balík crosstalk, ktorým sa dajú HTML widgets (tie ktoré crosstalk podporujú) prepojiť, takže akcia v jednom vyvolá reakciu aj v ďalších.

6.3 Shiny

Widgety htmlwidgets sú mocným nástrojom a dajú sa ľahko zakomponovať do samostatných dokumentov. Ak však treba väčšiu flexibilitu a prispôsobiteľnosť užívateľským vstupom, vtedy je balík shiny (Chang et al. 2022) rozumnejšou voľbou. Jeho nevýhodou je to, že kód sa už nedokáže vykonať iba v samotnom internetovom prehliadači, ale je naň potrebný beh vlastného servera.

Použitie ilustrujeme pomocou datasetu mpg na príklade grafu závislosti zdvihového objemu disp a dojazdu mimo mesta hwy s odlíšením roku výroby modelu year pomocou priehľadnosti a druhu paliva fl 1 farebne (a názvom modelu namiesto bodu). Graf by bol pre všetkých 234 modelov áut neprehľadný, preto predpokladajme, že nás v jednom momente zaujímajú dáta iba jedného konkrétneho výrobcu manufacturer. Statický graf pomocou ggplot2 dostaneme nasledovne:

library(ggplot2)
mpg |> 
  dplyr::filter(manufacturer == "chevrolet") |> 
  ggplot(aes(x = displ, y = hwy, alpha = year, color = fl)) +
  geom_text(aes(label = model), position="jitter") +
  scale_alpha(range = c(0.4, 1))

Poloha je pre časté prekrytia zámerne trochu rozochvená (jitter) a spodná hranica priehľadnosti je pre dobrú viditeľnosť zvýšená. Ak by sme chceli zobraziť takéto grafy pre všetkých výrobcov, pomocou fazetovania by vzniklo 15 subgrafov. Nechajme radšej užívateľa, nech si výrobcu vyberie priamo z rolovacieho menu pri grafe:

library(shiny)
# Užívateľské prostredie:
ui <- fluidPage(    # použi fluid Bootstrap layout
  # nadpis stránky
  titlePanel("Mileage per galon related to displacement"),
  # vytvor riadok s bočným panelom
  sidebarLayout(      
    # definuj bočný panel s jedným vstupom
    sidebarPanel(
      selectInput("manuf", "Manufacturer:", 
                  choices=unique(mpg$manufacturer)),
      hr(),  # horizontal rule
      helpText("Data from ggplot2.")
    ),
    # vytvor miesto pre graf
    mainPanel(
      plotOutput("mileagePlot")  
    )
  )
)
# Server:
server <- function(input, output) { # definuj server pre Shiny app
  # zaplň miesto vytvorené pre graf
  output$mileagePlot <- renderPlot({
    mpg |> 
      dplyr::filter(manufacturer == input$manuf) |> 
      ggplot(aes(x = displ, y = hwy, alpha = year, color = fl)) +
      geom_text(aes(label = model), position="jitter") +
      scale_alpha(range = c(0.4, 1)) +
      # aby nedochádzalo k zmene mierky pre rôzny subset údajov:
      scale_x_continuous(limits = range(mpg$displ)) +  
      scale_y_continuous(limits = range(mpg$hwy))
  })
}
# Skombinovanie frontend-u a backendu-u.
shinyApp(ui, server)  

Aplikácia vytvorí server, v ktorom beží jedna inštancia Rka a výsledok zobrazí v záložke Viewer alebo v externom internetovom prehliadači.

Ak chceme svoju interaktívnu shiny aplikáciu poskytnúť iným ľuďom, ktorí nemajú Rko nainštalované, alebo ho nevedia obsluhovať, mǒžeme vytvoriť server prístupný pod verejnou IP adresou. Alebo použiť službu, ktorá to urobí za nás. Jednou z takých je shinyapps.io. Stačí sa zaregistrovať a po prihlásení nasledovať jednoduchý postup, ako načítať shiny aplikáciu na server. Účet zadarmo má obmedzenie na 5 aplikácií, ktoré môžu bežať najviac 25 hodín/mesiac. Podrobný návod ku službe sa nachádza v dokumentácii produktov tímu okolo RStudio (od roku 2022 pod názvom Posit).

Napríklad naša aplikácia s interaktívnou voľbou výrobcu automobilov sa dá vyskúšať na adrese bacigal.shinyapps.io/shinyapp. Aby sme ju mohli nahrať na server, uložili sme R script do osobitného súboru app.R v adresári shinyapp. Pochopiteľne, názvy sa dajú zvoliť. Potom sme nainštaloval balík rsconnect, nakonfigurovali ho poskytnutím tokenu a hesla (z účtu na shinyapps.io), načítali balík a nahrali aplikáciu na server (treba mať už správne nastavený pracovný adresár pomocou setwd):

install.packages('rsconnect')
rsconnect::setAccountInfo(name='bacigal',
                          token='.................................',
                          secret='.........................................')
rsconnect::deployApp('shinyapp/')

V účte sme nastavili parameter timeout, po ktorom sa aplikácia po poslednom použití uspí, na 5 min (štandardne je nastavené 15 min), aby sa 25-hodinový limit míňal čo najpomalšie.

Viac o tvorbe Shiny aplikácií sa píše v oficiálnej dokumentácii na domovskej stránke, a ešte detailnejšie v publikácii (Wickham 2020).

6.4 Interaktívna podpora tvorby grafov ggplot

Interaktivita sa dá využiť aj na tvorbu statických grafov. Dobrým príkladom je balík ggraptR, ktorý začiatočníkom veľmi výrazne uľahčuje tvorbu základných grafov (scatter, line, path, density 2D, bin 2D, hex) možnosťou nastavenia komponentov aesthetic, theme, facet a aggregation. Výsledný graf sa dá exportovať ako obrázok alebo zdrojový script v R. Rovnako sa dajú tvoriť tabuľky súhrnov (napr. priemer podľa skupín), tie žiaľ už nejde exportovať. Nasledujúcim príkazom sa spustí lokálny server a otvorí nové okno vo webovom prehliadači:

ggraptR::ggraptR()

Štandardne sa pri spustení načíta dataset diamonds, no nie je problém načítať akýkoľvek iný dátový rámec.

Alternatívou ku ggraptR je prídavný modul (tzv. add-in) do RStudia, balík esquisse, ktorý sa po inštalácii spustí buď z ponuky [Addins] alebo príkazom (aj s konkrétnym datasetom):

esquisse::esquisser(iris)

6.5 Cvičenie

Pomocná nápoveda ku riešeniu je uvedená v zátvorke na konci úloh.

  1. Prvý príklad v kapitole o ggvis študijného materiálu upravte tak, aby sa body závislosti dojazdu od zdvihového objemu zafarbili p odľa diskrétnej premennej, ktorú si používateľ interaktívne zvolí v rolovacom menu. Diskrétne premenné v mtcars predtým konvertujte na faktor. (input_select, map=as.name)

  2. Predošlý príklad implementujte pomocou ggplot ako aplikáciu Shiny: vľavo nech je rolovacie menu, vpravo graf a pod ním tabuľka priemerov oboch premenných v jednotlivých skupinách (interaktívne zvolenej) diskrétnej premennej. (funkcie selectInput, plotOutput, tableOutput, aes_string, group_by + summarize, across)

  3. Poskytnite aplikáciu z predošlého príkladu online (formou odkazu, napr. pomocou služby Shinyapps). Ak sa vám predošlý príklad nepodarilo urobiť, poskytnite jednoduchú aplikáciu na výpočet mocniny čísla zadaného z textového poľa, alebo niečo iné – originálne, vaše.

  4. Pomocou ggplot2 a plotly v interaktívnom bodovom grafe zobrazte závislosť dojazdu od zdvihového objemu s farebným odlíšením počtu valcov tak, aby tooltip obsahoval iba informáciu o modeli auta a súradniciach bodu. Vedeli by ste text tooltip-u formátovať tak, aby bol model auta prvý a hodnoty parametrov auta boli uvedené aj s jednotkami? (argument tooltip, aes text)

  5. Zobrazte príklad takého htmlwidget-u (zo všetkých dostupných, ale nespomínaných v tejto kapitole), ktorý je pre vás zaujímavý a užitočný (vysvetlite prečo).


  1. Význam skratiek: c – CNG, d – diesel, e – ethanol (E85), p – premium (high octane), r – regular.↩︎