Recapitulare: pornim de la sablon

library(shiny)

# interfata utilizator 
ui <- fluidPage('Text oarecare')

# server
server <- function(input, output) {}

# rulare
shinyApp(ui = ui, server = server)

Reactivitate

Input

  • intuitiv: functioneaza similar cu Excel;

  • obiectele de tip output se modifica la fiecare schimbare a celor de tip input;

  • vrem sa controlam modul in care se fac aceste modificari (implicit instant);

  • cand folosim inputId=‘nume’ intr-o functie Input(), in partea de server valoarea aceea va fi apelata cu input$nume
    • valoarea depinde de tipul functiei Input();
  • la modificarile facute de utilizator, valorile lui input$nume se actuallizeaza instant

  • valorile input$… functioneaza doar in interiorul functiilor reactive de tip render(); nu pot fi apelate reactiv in afara unei functii render();

output$hist <- renderPlot({hist(rnorm(input$num))})
  • daca uitam sa folosim renderPlot:
output$hist <- hist(rnorm(input$num))

in codul

library(shiny)

ui <- fluidPage(
   sliderInput(inputId = 'num', label='alegeti un numar:', value=4, min=-10, max=10),
   plotOutput('hist')

   )

server <- function(input, output) {
  output$hist <- hist(rnorm(input$num))
   
}

shinyApp(ui = ui, server = server)

obtinem eroarea:

Error in .getReactiveEnvironment()$currentContext() : Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)

Reactivitatea se realizeaza in 2 pasi:

  1. Utilizatorul modifica o valoare controlata de o functie Input(); In acel moment vechea valoare este considerata invalida;

  2. Functia render(), care are rol de observator, corecteaza valoarea in codul ei (ruleaza tot codul) - de fiecare data cand o valoare este invalidata.

Functii care controleaza reactivitatea

1. render()

  • folosim functii render() pentru a afisa obiecte in ui;

  • rezultatul lor este salvat ca si componente ale output$;

  • Functiile render() se asociaza cu valori reactive;

  • Returneaza un obiect in functie de tipul de render() folosit (plot, table, etc…vezi Shiny Cheat Sheet)

  • functiile render() au un singur argument:

renderPlot( {} )

reprezentat de codul R dintre acolade:

- putem avea oricate linii de cod;
- va raspunde la toate modificarile valorilor reactive;
- codul va fi rulat in intregime la fiecare modificare a unei valori reactive input$...
library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "num", 
    label = "Choose a number", 
    value = 25, min = 1, max = 100),
  textInput(inputId = "title", 
    label = "Write a title",
    value = "Histogram of Random Normal Values"),
  plotOutput("hist")
)

server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num), main = input$title)
  })
}

shinyApp(ui = ui, server = server)
  • avem doua valori de input: num si title; la schimbarea oricareia dintre ele histograma se actualizeaza;

2. reactive()

Sa luam un exemplu cu doua outputs:

library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "num", 
    label = "Choose a number", 
    value = 25, min = 1, max = 100),
  plotOutput("hist"),
  verbatimTextOutput("stats")
)

server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num))
  })
  output$stats <- renderPrint({
    summary(rnorm(input$num))
  })
}

shinyApp(ui = ui, server = server)
  • avem 1 input, 2 outputs

  • dar la fiecare modificare de input, fiecare render() genereaza alt set de date;

  • am vrea sa afisam summary pentru acelasi set pentru care desenam histograma

  • dar putem folosi input doar in interiorul unei functii reactive

  • pentru a putea folosi obiecte reactive in mai multe functii render() folosim reactive() pentru a defini obiecte reactive pe baza valorilor din input:

data <- reactive({rnorm(input$num)})
  • data este o expresie reactiva

  • reactive() functioneaza ca si render() dar creeaza expresii reactive (data)

  • Expresiile reactive:
    • sunt apelate ca si functii: data() fara argumente dar cu paranteze!
    • ‘stiu’ daca sunt valide sau invalide
    • isi pastreaza valoarea (valida) pentru a fi folosite in mai multe functii render();
library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "num", 
    label = "Choose a number", 
    value = 25, min = 1, max = 100),
  plotOutput("hist"),
  verbatimTextOutput("stats")
)

server <- function(input, output) {
  
  data <- reactive({
    rnorm(input$num)
  })
  
  output$hist <- renderPlot({
    hist(data())
  })
  output$stats <- renderPrint({
    summary(data())
  })
}

shinyApp(ui = ui, server = server)

Acum histograma si summary sunt calculate pe baza acelorasi date, data().

3. isolate()

  • folosim isolate() pentru a opri reactiile la modificarea unui input

De exemplu in aplicatia

library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "num", 
    label = "Choose a number", 
    value = 25, min = 1, max = 100),
  textInput(inputId = "title", 
    label = "Write a title",
    value = "Histogram of Random Normal Values"),
  plotOutput("hist")
)

server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num), main = input$title)
  })
}

shinyApp(ui = ui, server = server)

la fiecare modificare a unei litere din titlu se recalculeaza histograma…

  • vrem sa modificam acest comportament:
library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "num", 
    label = "Choose a number", 
    value = 25, min = 1, max = 100),
  textInput(inputId = "title", 
    label = "Write a title",
    value = "Histogram of Random Normal Values"),
  plotOutput("hist")
)

server <- function(input, output) {
  output$hist <- renderPlot({
    hist(rnorm(input$num), main = isolate(input$title))
  })
}

shinyApp(ui = ui, server = server)
  • rezultatul lui isolate nu este actualizat de fiecare data cand input$title se modifica;

  • dar se actualizeaza de fiecare data cand alta valoare reactiva se modifica!

4. observeEvent()

Folosim observeEvent() cand dorim sa efectuam operatii pe server, care nu afiseaza nimic in ui (salvare fisiere, incarcare date, etc.)

  • folosim ActionButton() impreuna cu observeEvent():
actionButton(inputId='clicks', label='click me, please!')

Daca il adaugam asa la template:

library(shiny)

ui <- fluidPage(
  actionButton(inputId='clicks',label='click me, please!' )
)

server <- function(input, output){}

shinyApp(ui=ui, server=server)

apare un buton care nu face nimic.

Reactivitatea pentru buton se construieste cu

observeEvent(input$clicks, {print(input$clicks)})
  • primul argument este o lista de valori reactive a caror modificare conduce la rularea codului din {}

  • creeaza un obiect fara afisare pe ui, dar reactioneaza la schimbarile din lista input.

  • Daca in cod {} apar si alte valori reactive, acelea nu se vor actualiza;

  • codul e tratat ca si cum ar fi in functia isolate();

library(shiny)

ui <- fluidPage(
  actionButton(inputId = "clicks", 
    label = "Click me")
)

server <- function(input, output) {
  observeEvent(input$clicks, {
    print(as.numeric(input$clicks))
   
  })
}

shinyApp(ui = ui, server = server)

5. observe()

  • similar cu observeEvent(), render() si isolate() la un loc

  • are doar un argument, cod R in {}

observe({print(input$clicks)})
  • raspunde la toate schimbarile reactive din input…

6. eventReactive()

  • folosim eventReactive() cand dorim sa amanam reactiile la modificari
library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "num", 
    label = "Choose a number", 
    value = 25, min = 1, max = 100),
  actionButton(inputId = "go", 
    label = "Update"),
  plotOutput("hist")
)

server <- function(input, output) {
  data <- eventReactive(input$go, {
    rnorm(input$num) 
  })
  
  output$hist <- renderPlot({
    hist(data())
  })
}

shinyApp(ui = ui, server = server)
  • folosim actionButton()

  • functia eventReactive() raspunde doar la anumite schimbari;

  • creeaza o expresie reactiva (la fel ca si reactive())

data <- eventReactive(input$go,
                      {
                        rnorm(input$num)
                      })

reactioneaza doar la modificarea lui input$go, dar ia in calcul valorile actualizate ale tuturor celorlaltor input..

In renderPlot folosim data().

7. reactiveValues()

  • input este o lista de valori reactive;

  • nu putem modifica aceste valori in partea de server;

  • putem insa sa ne creem noi o lista de valori reactive pe care apoi sa le modificam in cod

library(shiny)

ui <- fluidPage(
  actionButton(inputId = "norm", label = "Normal"),
  actionButton(inputId = "unif", label = "Uniform"),
  plotOutput("hist")
)

server <- function(input, output) {

  rv <- reactiveValues(data = rnorm(100))

  observeEvent(input$norm, { rv$data <- rnorm(100) })
  observeEvent(input$unif, { rv$data <- runif(100) })

  output$hist <- renderPlot({ 
    hist(rv$data) 
  })
}

shinyApp(ui = ui, server = server)
  • folosim observeEvent() pentru a modifica valorile reactive create de reactiveValues();

  • rv e o lista in care putem adauga si alte componente:

library(shiny)

ui <- fluidPage(
  actionButton(inputId = "norm", label = "Normal"),
  actionButton(inputId = "unif", label = "Uniform"),
  plotOutput("hist")
)

server <- function(input, output) {

  rv <- reactiveValues(data =rnorm(100), data1='normal')

  observeEvent(input$norm, { 
    rv$data <- rnorm(100) 
    rv$data1='normal'
    })
  observeEvent(input$unif, { 
    rv$data <- runif(100) 
    rv$data1='uniform'
    })

  output$hist <- renderPlot({ 
    hist(rv$data, main=rv$data1) 
  })
}

shinyApp(ui = ui, server = server)