Poglavje 1 Statični dashboard

1.1 Priprava

Cilj današnjega predavanja je, da spoznamo knjižnico Shiny in strukturo njenih aplikacij. Spoznali bomo osnovne gradnike uporabniškega vmesnika, zaradi česar se bomo zaenkrat omejili le na statične aplikacije. Interaktivne aplikacije bomo spoznali v naslednjem poglavju.

Najprej je seveda pomembno, da si namestimo potreben paket Shiny z ukazom install.packages.

Odpremo RStudio in v konzolo vpišemo ukaz:

install.packages(shiny)

Ko je namestitev končana, naložimo paket in z ukazom runExample preverimo, če deluje:

library(shiny)

runExample("01_hello")

Če vse deluje pravilno, bi se moralo odpreti okno, kot je prikazano na tej sliki:

Postavitev elementov je odvisna od velikosti odprtega okna in se lahko nekoliko razlikuje od primera na zgornji sliki. V vsakem primeru lahko z drsnikom spreminjamo število košev, ki v grafu predstavljajo število stolpcev histograma. Na ta način lahko avtomatsko spreminjamo prikaz histograma. Ko zaključimo z uporabo aplikacije, s klikom na gumb X v zgornjem desnem kotu aplikacijo zapremo. Na ta način ustavimo proces v ozadju. Ko se vrnemo v Rstudio, lahko preberemo sporočilo: Listening on http://127.0.0.1:7844. Rstudio je pripravljen na izvedbo novega ukaza. To pomeni, da je aplikacija uspešno zaprta. Če Rstudio ni pripravljen na nov ukaz, je aplikacija še vedno odprta nekje v ozadju.

Poleg prvega primera “01_hello” lahko poženemo tudi druge že pripravljene primere. Tako dobimo občutek, kaj vse nam nudi paket Shiny.

runExample("01_hello") # histogram
runExample("02_text") # tabele in data frame-i
runExample("03_reactivity") # reaktivni izrazi
runExample("04_mpg") # globalne spremenljivke
runExample("05_sliders") # drsniki
runExample("06_tabsets") # zavihki
runExample("07_widgets") # tekst za pomoč in akcijski gumbi
runExample("08_html") # aplikacija Shiny sestavljena iz HTML
runExample("09_upload") # primer prenašanja datotek
runExample("10_download") # primer nalaganja datotek
runExample("11_timer") # samodejni timer

1.2 Moja prva Shiny aplikacija

Novo aplikacijo Shiny lahko ustvarimo s čarovnikom. V meniju izberemo File -> New File -> Shiny Web App. Ta proces privzeto ustvari mapo z izbranim imenom in pripravljeno skripto app.R, ki predstavlja začetni primer.

Prvo aplikacijo bomo ustvarili kar sami. Odprimo novo skripto R in vpišimo te ukaze:

library(shiny)

ui <- fluidPage(
  titlePanel("Moja prva Shiny aplikacija")
)

server <- function(input, output){}


shinyApp(ui = ui, server = server)

Dokler skripte R ne shranimo, bo orodna vrstica na vrhu izgledala takole:

Ko pa skripto shranimo, se orodna vrstica spremeni v:

Kadar Rstudio v zadnji vrstici skripte zazna ukaz shinyApp, samodejno prilagodi orodno vrstico za delo s Shiny R-jem. Še opozorilo: če vas R vpraša, v katerem kodiranju želite shraniti skripto, izberite UTF8. Paket Shiny za pravilno delovanje uporablja to kodiranje.

Sedaj s klikom na gumb Run App poženimo našo prvo aplikacijo. Prikaže se nam okno:



Čeprav ukazov iz zgornje skripte še nismo razložili, smo ustvarili našo prvo in najbolj osnovno aplikacijo Shiny. Aplikacija nam izpiše le naslov. V nadaljevanju si bomo podrobneje ogledali, katere gradnike nudi knjižnica Shiny.

1.3 Osnovni sestavni deli

Naša skripta vsebuje vse potrebne sestavne dele za delovanje aplikacije. Kodo znotraj vsake skripte delimo na 3 glavne sestavne dele:

  • objekt za izdelavo uporabniškega vmesnika imenovan ui,
  • strežniško funkcijo server in
  • klic funkcije shinyApp.

V uporabniškem vmesniku določimo razporeditev uporabljenih komponent in obliko naše aplikacije. Znotraj njega definiramo vse komponente spletne strani, kot so besedilo, polja za vnos besedila, polja za izris grafa, itd. Uporabniški vmesnik definiramo s klicem funkcije Shiny, ki sestavi spletno stran v jeziku HTML (v prejšnjem primeru je to fluidPage(), ki lahko avtomatsko razporeja elemente dashboard-a).

V strežniški funkciji pripada vsakemu izhodu (izpis teksta, izris grafa…) del kode, ki se izvede ob uporabnikovem dejanju oz. ko pride do spremembe. Strežniško funkcijo definiramo tako, da sprejme dva parametra: vhod in izhod. Ko sta ti dve komponenti pripravljeni, lahko kličemo ukaz shinyApp(ui = < moj_vmesnik >, server = < moj_strežnik >), ki aplikacijo sestavi in požene.

Če si predstavljamo uporabniški vmesnik kot komponento, ki naši aplikaciji daje obliko, si lahko strežniško funkcijo predstavimo kot komponento, ki naši aplikaciji daje odzivnost. Znotraj nje definiramo vsa pravila in ukaze, ki so potrebni za interaktivno delovanje aplikacije. Delovanje strežniške funkcije bomo spoznali v naslednjem predavanju, danes pa se posvetimo uporabniškem vmesniku.

1.4 Formatiranje teksta

Preden se naučimo uporabljati bolj zapletene objekte, si oglejmo, kako v aplikacijah Shiny oblikujemo besedilo. Ko poženemo aplikacije, se te prepišejo v jezik HTML. Če želimo besedilo naših aplikacij preoblikovati, moramo navodila za preoblikovanje spremeniti v jezik HTML. Shiny R ponuja vrsto funkcij za oblikovanje besedila, ki se ujemajo z oznakami programskega jezika HTML. Nekaj primerov:

Funkcija v R Oznaka HTML Pomen
p() <p> Odstavek teksta.
h1() <h1> Naslov prvega nivoja.
h2() <h2> Naslov drugega nivoja.
h6() <h6> Naslov šestega nivoja.
strong() <strong> Krepko označeno besedilo.
em() <em> Ležeče označeno besedilo.
a() <a> Povezava.
br() <br> Prekinitev vrstice (prazna vrstica).
code() <code> Tekst v obliki kode.

R funkcije poleg besedila, ki ga želimo izpisati, sprejmejo tudi parametre, ki definirajo slog, bravo… Te definiramo v slogu programskega jezika CSS (Cascading Style Sheet), oziroma tako, da jih podamo v obliki” "<argument1> : <vrednost1>; <argument2> : <vrednost2>; "..".

Poglejmo si preprost primer:

ui <- fluidPage(
  titlePan"l("Moja prva Shiny aplikac"ja"),
  h2("Lorem ipsum"), #podnaslov
  
  #odsek teksta

  p("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborem."),
    
  br(), # prekinitev

  strong("Kerpko označen tekst."),

  br(), # prekinitev

  p("Odstavek teksta, ki je zeleno obarvan.", 
    style = "color:green; font-size:8pt") #CSS stil
)

V prvem primeru smo že spoznali funkcijo titlePanel, ki izpiše naslov aplikacije. Enak napis bi dobili, če bi funkcijo titlePanel zamenjali s funkcijo h2. Vse elemente, ki jih želimo prikazati na strani (fluidPage), podamo kot parametre tej funkciji. V našem primeru smo najprej dodali dva naslova, nato odstavek s tekstom, ki se avtomatsko prilagaja velikosti strani. Temu sledi še odebeljen tekst in na koncu poljubno formatiran tekst z uporabo CSS.

1.5 Stranski pano in vnos slik

Če aplikaciji dodamo različne elemente, se bodo ti prikazali vsak v svoji vrstici. Zato paket Shiny vsebuje funkcije, s katerimi lahko razporejamo objekte. V sklopu tega predavanja bomo spoznali le eno razporeditev, na tretjem predavanju pa bomo spoznali še druge razporeditve.

Funkcija sidebarLayout razdeli stran na stranski in glavni pano. Funkcija prejme dva vhodna parametra, sidebarPanel in mainPanel, kjer vsak vsebuje svoje komponente (tekst, polja, drsnike, grafe, itd.).

Poglejmo si najbolj preprosto uporabo tega gradnika:

ui <- fluidPage(
  titlePanel("Moja prva Shiny aplikacja"),

  # Razporediev sidebarLayout
  sidebarLayout(

  # Stranski pano
  sidebarPanel("Stranski pano"),

  # Glavni pano
  mainPanel("Glavni pano")
  )

)

Prikaže se nam okno z dvema gradnikoma, ki vsebujeta le tekst. Leva tretjina zaslona je privzeto namenjena stranskemu panoju, desni dve tretjini pa glavnemu panoju. V primeru, da je celotno okno preozko, pa se lahko stranski pano premakne tudi na vrh zaslona. Več o ročni nastavitvi postavitve panojev bomo povedali na tretjem predavanju.

V spodnjem primeru nadgradimo zgornji primer tako, da vsak pano vsebuje več gradnikov in dodamo logotip Data Science:

ui <- fluidPage(
  titlePanel("Moja prva Shiny aplikacija"),

  # Razporediev sidebarLayout
  sidebarLayout(
    
    # Stranski pano
    sidebarPanel(
  
    h2("Stranski pano"), # Naslov
  
    "V stranski pano lahko dodamo podamo poljubno število elementov, \
kot je na primer ta tekst.", # Tekst

  ), 

  # Glavni pano

  mainPanel(
    h2("Glavni pano"), # Naslov
  
    p("V glavni pano ravno tako lahko dodamo poljubno število elementov. \
    Dodajmo še logotip Data Science:"), # Tekst
  
    br(), # Dodamo malo prostora 
  
    img(src = "images\\DS_FRI_logo.png", 
        align = "center"), # Slika
  
    p("Pozor: slike morajo biti znotraj mape www!")
    )
  )
)

Aplikacija ima na levi strani stranski pano, na desni pa glavnega. Za vsako komponento v panojih smo klicali funkcijo, ki izpiše panoju kodo HTML za dani objekt.

V tem primeru smo aplikaciji dodali tudi sliko. V aplikacijo Shiny lahko vnesemo sliko s funkcijo img(), vendar se mora v mapi z izvorno skripto nahajati mapa www, v kateri je shranjena slika. Vse poti, ki jih podamo funkciji, so relativne glede na to mapo, zato je v prejšnjem primeru naša slika v mapi www/images. V mapi www poleg slik lahko hranimo tudi datoteke s pravili za oblikovanje, tekste in druge zunanje elemente, ki jih vnašamo v našo spletno aplikacijo.

1.6 Kontrolni Widgeti

Widget-i so pomembni elementi uporabniškega vmesnika, ker nam omogočajo interakcijo z aplikacijo. V tem poglavju na kratko pogledamo kateri so osnovni widget-i paketa Shiny, kako delujejo, pa bomo pokazali na naslednjem predavanju, saj moramo za to prej predstaviti strežniško funkcijo. Omenimo le, da jim kot prvi argument podamo identifikacijsko ime (lahko vsebuje le črke, številke ali podčrtaj), s katerim lahko dostopamo do posameznih vrednosti widget-a.

V spodnji tabeli so prikazani widgeti paketa Shiny:

funcija opis
actionButton Action Button
checkboxGroupInput A group of check boxes
checkboxInput A single check box
dateInput A calendar to aid date selection
dateRangeInput A pair of calendars for selecting a date range
fileInput A file upload control wizard
helpText Help text that can be added to an input form
numericInput A field to enter numbers
radioButtons A set of radio buttons
selectInput A box with choices to select from
sliderInput A slider bar
submitButton A submit button
textInput A field to enter text

Poglejmo si, kako definiramo sliderInput, checkboxInput, dateInput in selectInput ter jih dodajmo v naš primer:

ui <- fluidPage(

titlePanel("Moja prva Shiny aplikacija"),

# Razporeditev sidebarLayout

sidebarLayout(

  # Stranski pano
  sidebarPanel(
    h2("Stranski pano"), # Naslov
    "V stranski pano lahko dodamo podamo poljubno število elementov, \
    kot je na primer ta tekst.", # Tekst
    
    sliderInput(
      inputId = "drsnik", # Identifikacijsko ime
      label = "Izberite vrednost:", # Besedilo na drsniku
      min = 0, # Minimalna vrednost
      max = 100, # Maksimalna vrednost
      value = 50, # Začetna vrednost
      step = 2 # Velikost koraka po drsniku
    ),

    checkboxGroupInput(
      inputId = "checkbox", # Id. ime
      label = "Izberite pravilne vrednosti za x, če \
          x > -2 in x < 4:", # Besedilo na checkboxu
      choices = c(1, 5, 3.2, 10), # Možne izbire
      )
    ), 

    # Glavni pano
    mainPanel(
      h2("Glavni pano"), # Naslov
      p("V glavni pano ravno tako lahko dodamo poljubno število elementov. \
      Dodajmo še logotip Data Science:"), # Tekst
      br(), # Dodamo malo prostora 
      img(src = "images\\DS_FRI_logo.png", 
      align = "center"), # Slika
      p("Pozor: slike morajo biti znotraj mape www!"),

    dateInput(
      inputId = "vnos_datuma", # Id. ime
      label = "Izberi datum rojstva:",# Besedilo
      language = "sl", # Jezik izpisa datuma
      min = "1900-1-1", # Minimalna možna vrednost
      max = date() # Maksimalna možna vrednost
      ),

    selectInput(
      inputId = "izbire", # Identifikacijsko ime
      label = "Izberite možnosti:", # Besedilo na drsniku
      choices = c("Plan 1", # Izbire
                "Plan 2",
                "Plan 3",
                "Plan 4"),
      selected = "Plan 2", # Privzeta izbira
      multiple = FALSE # Več izbir
      )
    )
  )
)

Vsi objekti imajo vsaj dva skupna atributa: input_id, ki določa ime, s katerim dostopamo do vrednosti objekta, in label, ki na objektu izpiše tekst z navodili ali pojasnilom. Nato določimo druge atribute, kot na primer možne izbire, začetno vrednost, zgornje in spodnje meje vrednosti, ki jih objekt lahko zajame… V zgornjem primeru si lahko pomen atributov pogledamo v komentarjih, če pa želimo uporabiti kakšen drug widget, si lahko pomagamo z R-jevim ukazom ?, npr. ?selectInput. Tako dostopamo do strani za pomoč, ki za vsak gradnik opisujejo uporabo njegovih atributov.

V tem primeru se drsnik, polje za izbiro datuma in gumbi za izbiro sicer odzivajo, vendar še niso interaktivni, ker ne vplivajo na ostale gradnike.

Za konec si poglejmo še kako izgledajo widget-i aplikacije Shiny, ki jih še nismo obravnavali:

1.7 Domača naloga

Zamislite si, da sestavljate aplikacijo, ki bo beležila nove uporabnike vaših storitev. Vaša naloga je, da sestavite uporabniški vmesnik za dashboard, ki bo beležil:

  • ime,
  • datum rojstva,
  • spol,
  • kraj bivanja,
  • datum začetka uporabe storitve,
  • storitev, na katero se naroča (možnosti A, B, C - ena možna izbira).

Poleg teh vnosnih polj naj aplikacija vsebuje še naslov “Stranke” in gumb, ki bo potrdil shranjevanje podatkov v datoteko. Vsi vhodi naj bodo v levem stolpcu razporeditve “sidebar” (desnega bomo dopolnili v naslednji domači nalogi). Rešitev naj izgleda približno tako: