library(knitr)
library(rmdformats)

## Global options
options(max.print="75")
opts_chunk$set(echo=TRUE,
                 cache=TRUE,
               prompt=FALSE,
               tidy=TRUE,
               comment=NA,
               message=FALSE,
               warning=FALSE)
opts_knit$set(width=75)

Qu'est-ce qu'une application Shiny ?

Shiny est un framework d'applications web pour R

Le succès du package (pour les utilisateurs R) réside dans :

un exemple d'application Shiny en ligne

Le principe de fonctionnement d'une application Shiny

Une application Shiny contient 2 fichiers :

Nota Bene : parfois on trouve seulement un seul fichier nommé app.R qui contient l'ui et le serveur sous forme d'objets R et l'application est lancée via l'instruction shinyApp(ui,server)

Votre première application Shiny....

... vue de l'extérieur

Dans RStudio,

File > New File > Shiny Web App

Un fois le projet créé, cliquez sur le bouton "Run app" ou exécutez runApp('nom_de_lapplication')!

... et de l'intérieur :

Dans sa version "unique file", le template d'une application Shiny est de la forme :

library(shiny)

ui <- fluidPage()

server <- function(input, output) {}

shinyApp(ui = ui, server = server)

Les fonctions shiny côté UI

2 types de fonctions : xInput et xOutput

Ces deux grandes familles de fonctions s'intéressent aux inputs que l'utilisateur va vouloir passer au serveur et les output que le serveur lui restituera.

les différents inputs

un chiffre ou une plage de chiffres

A saisir :

numericInput(inputId = "Numeric", label = "Choisissez un nombre entre 0 et 100",
value = 50, min = 0, max = 100, step = 10)

ou sous forme de slider :

sliderInput("slider", "Nombre :",
    min = 0, max = 1000, value = 500)
sliderInput("slider", "Nombre :",
    min = 0, max = 1000, value = c(100,300))

du texte

textInput(inputId = "texte", label = "Saisissez du texte", value = "exemple")
textAreaInput(inputId = "texte", label = "Une plus grosse boîte de texte")

une liste

Avec un choix unique...

selectInput(inputId = "idSelect", label = "Choisissez: ",
            selected = 3,
            choices = c("choix 1" = 1, "choix 2" = 2, "choix 3" = 3))

ou à choix multiples :

selectInput(inputId = "idSelect", label = "Choisissez: ",
            selected = 3,
            multiple = TRUE,
            choices = c("choix 1" = 1, "choix 2" = 2, "choix 3" = 3))

les checkbox simples

checkboxInput(inputId = "check1", label = "Cochez (ou pas): ", value = TRUE)

les checkbox multiples

checkboxGroupInput(inputId = "checkmultiple", label = "Cochez (ou pas)", choices = c("check 1", "check 2", "check 3", "check 4"),selected = "check 1",inline = TRUE)

les dates

dateInput(inputId = "ladate", label = "Une date",
          value = Sys.Date()-10,
          format = "dd/mm/yy",
          language = "fr",
          startview = "year", 
          weekstart = 1)

ou une plage de temps :

dateRangeInput(inputId = "plagedate", label = "Une plage de temps : ", language = "fr")

un fichier

fileInput(inputId = "fichier_importe", label = "Fichier à importer", multiple = FALSE, accept = c("text/csv",
          "text/comma-separated-values,text/plain",
          ".csv"))

une action via un bouton

actionButton(inputId = "idActionButton", label = "Il clique à gauche")

FYI : la liste des fonctions qui finissent par Input dans le package Shiny :

ls('package:shiny', pattern = "Input$")

les différents outputs :

htmlOutput(outputId = "renduhtml")
imageOutput(outputId = "image")
plotOutput(outputId = "graphe")
tableOutput(outputId = "table1")
dataTableOutput(outputId ="table2")
textOutput(outputId = "texte")
verbatimTextOutput(outputId = "untextepluslong")
uiOutput(outputId = "unelementui")

FYI : la liste des fonctions qui finissent par Output dans le package Shiny :

ls('package:shiny', pattern = "Output$")

Les fonctions shiny côté serveur

Le serveur assemble les inputs pour les restituer à l'utilisateur sous forme d'output. L'art de faire du Shiny consiste à réaliser ce montage côté serveur. A cet effet, 3 règles à observer impérativement pour ne pas avoir d'ennuis :

  1. Les objets à afficher dans les fonctions *Output côté UI devront être écrite sous la forme output$x côté serveur :

Exemple :

ui <- fluidPage(
  plotOutput("unplot")
)

server <- function(input, output) {
  output$unplot <- # du code R pour faire le plot en question)
}

shinyApp(ui = ui, server = server)
  1. Dès lors qu'on utilise une instruction du type output$x <-dans le serveur, l'instruction en question est nécessairement suivie d'une fonction render*()

L'exemple devient donc :

Exemple :

ui <- fluidPage(
  plotOutput("unplot")
)

server <- function(input, output) {
  output$unplot <- renderPlot({# du code R})
}

shinyApp(ui = ui, server = server)

Nota Bene : attention aux accolades à l'intérieur de cette famille de fonctions

A disposition, la liste de toutes les fonctions render*()

ls('package:shiny', pattern = "^render")

Elles fonctionnent main dans la main avec les fonctions *Output() de l'UI.

  1. Pour appeler des inputs de l'UI dans le serveur afin de programmer les fonctions render*(), la syntaxe est de la forme : input$x
ui <- fluidPage(
  # l'input fourni par l'utilisateur est un nombre entier renseigné par sliderInput() : 
  sliderInput(inputId = "classes", "Nombre de classes", min = 1, 
              max =50, value = 30),
  # l'output retourné à l'utilisateur est un plot nommé "unplot"
  plotOutput("unplot")
)

server <- function(input, output) {
  output$unplot <- renderPlot({ # l'objet unplot est transmis à plotOutput via output$unplot

    #selection de la variable dans le jeu de données
    x    <- faithful[, 2]

    #  définition du nombre de classes avec l'input
    classes <- seq(min(x), max(x), length.out = input$classes + 1)

    # construction du plot 
    hist(x, breaks = classes, col = 'darkgray', border = 'white', 
         main = "Histogramme des temps d'attente entre 2 éruptions du geyser 'Old Faithful' ")
  })
}

shinyApp(ui = ui, server = server)

Exercice 1 :

Ajouter une boîte de texte sous le graphique qui nous renseigne sur le temps moyen d'attente entre 2 éruptions.

De la réactivité des fonctions côté serveur...

Les fonctions render*() ont une réactivité immédiate. C'est à dire qu'à chaque couple input\$inputId <-> output\$outputId la mise à jour est instantanée dès lors que input\$inputId change de valeur.

Les valeurs réactives marchent de pair avec les fonctions réactives.

Dès que l'input change, les valeurs dites "réactives" influent sur les fonctions qui utilisent pour leur notifier que les paramètres qu'ils utilisent sont invalides : tous les blocs de code qui contiennent l'input sont donc (ré)évalués. Les fonctions réactives mettent à jour les output$outputId

Les inputs sont en fait une liste de valeurs réactives et il n'est possible d'appeler des ces valeurs que dans des fonctions réactives : les valeurs réactives notifient les fonctions réactives qui y répondent.

  1. Quel code est utilisé par la fonction réactive ?
  2. A quelle valeur réactive l'objet créé par la fonction réactive répond ?

Restituer avec les fonctions render*()

Chaque fois que les inputs utilisés dans une fonction render*() changent c'est l'intégralité du bloc de code qui est (ré)évalué.

Question :

Dans l'exemple précédent, le calcul de la moyenne est-il recalculé à un moment ?

Exercice 2 :

Ajouter à l'application précédente un input qui serait du texte à saisir et qui permettrait à l'utilisateur de changer le titre du graphique

Modulariser avec les fonctions reactive()

Les fonctions reactive() servent à découper les applications shiny en petits modules qui optimisent le flux de valeurs réactives et des fonctions associées

Elle permettent de créer des objets, réactifs, liés à un input qui seront utilisés à plusieurs endroits de l'application.

variable_a_reutiliser <- reactive({expression impliquant des inputs})
...
output$nom_de_loutput <- render*({fonction1(paramètre = variable_a_reutiliser())})

output$nom_d_un_autre_output <- render*({fonction2(paramètre = variable_a_reutiliser())})

Nota Bene : attention aux parenthèses de la fonction réactive nouvellement générée. En cas d'oubli, il se passe ce qui se passe quand on demande à R une fonction sans parenthèses == l'affichage du code source

Exercice 3 :

Ajouter un bloc de texte sous le graphique qui compte le nombre de caractères contenus dans le titre du graphique avec une fonction reactive()

Temporiser avec isolate()

isolate() est une fonction qui retourne une valeur non réactive.

La syntaxe est semblable à celle de reactive*() :

isolate({expression impliquant des inputs})

Avec des parenthèses car il s'agit d'une fonction et des accolades car il s'agit d'une expression R.

Au lancement de l'application, la valeur prise par le résultat de la fonction isolate() est celle qui correspond à l'évaluation de l'expression entre accolades avec les inputs correspondants.

Si les inputs changent au sein des accolades, l'output n'est pas mis à jour. Par contre, et c'est tout son intérêt, si isolate est au sein d'un bloc de code qui contient des variables réactives, chaque fois que ces valeurs changent, le bloc est réévalué, et l'expression au sein d'isolate() remise à jour en quelque sorte.

isolate() est un moyen de faire un refresh "local", conditionné par des variables environnantes qui elles sont réactives.

Exercice 4 : Utiliser isolate() pour isoler la réactivité de la mise à jour du titre du graphique : le comptage du nombre de caractères soit rester réactif, mais la mise à jour du titre ne doit se faire que quand le slider est modifié.

Déclencher du code avec des événements : observeEvent() et observe()

Ces deux fonctions ont une syntaxe différente pour une réaction identique :

observe({expression à évaluer liée à un input})

observeEvent(input$evenement, {expression à évaluer liée à l'input "evemenent"})

Ces fonctions attendent des interactions avec l'utilisateur, des "observations" au niveau des inputs avant d'exécuter les expressions entre accolades.

Il est possible d'assigner leur valeur à des outputs pour que ces derniers ne se (ré)exécute que quand l'utilisateur le demande et non pas sous forme d'un refresh instantané comme pour les valeurs réactives au sein des fonctions render*()

ui <- fluidPage(
  sliderInput(inputId = "num", 
    label = "Choisir un nombre",
    min = 1, max = 100, value = 25),
  actionButton(inputId = "go", 
    label = "Renvoyer la valeur prise par le bouton dans la console")
)

server <- function(input, output) {

  observeEvent(input$go, {
    print(as.numeric(input$num))
  })
}

shinyApp(ui = ui, server = server)

Souvent, les événements qui déclenchent du code ailleurs dans l'application sont des actionButton() comme dans l'exemple ci-dessus ou des

Temporiser du code avec eventReactive()

Dans le flux de réactivité et de mise à jour des valeurs du côté serveur et UI, il est parfois intéressant de temporiser ce flux en laissant la main au client putôt qu'en faisant les refresh instantanément, à l'image de reactive()

La fonction qui qui permet de conditionner la chaîne de réactivité à un événement est la fonction eventReactive()

Sa syntaxe est de la forme :

eventReactive(input qui donne le go, {expression à évaluer après le go})

Comme pour les fonctions reactive(), la fonctions eventReactive() renvoie une fonction et invite donc à utiliser les parenthèses à chaque fois qu'on souhaite s'y référer :

variable_a_reutiliser <- eventReactive(input qui donne le go, {expression impliquant des inputs})
...
output$nom_de_loutput <- render*({fonction1(paramètre = variable_a_reutiliser())})

output$nom_d_un_autre_output <- render*({fonction2(paramètre = variable_a_reutiliser())})

Exercice 5:

A partir d'un template vide, créer une application qui utilise la fonction actionButton comme déclencheur à afficher un chiffre au hasard compris entre 1 et 100 (fonction sample).

Exercice 6:

Ajouter à votre application un bouton de mise à jour du titre du graphique et du nombre de caractères qu'il contient au moyen d'un actionButton dès lors que l'utilisateur juge sa saisie terminée

En résumé

un schéma récapitulatif :

Un aide-mémoire : https://www.rstudio.com/wp-content/uploads/2015/08/shiny-french-cheatsheet.pdf

Customiser l'interface côté UI ?

en utilisant tout ce que vous savez d'html 5 via les tags$

Les tags sont la liste de fonctions R à disposition pour écrire du html depuis R :

library(magrittr)
names(tags) %>% head(50)

Ainsi...

tags$blockquote("une citation")

...est une fonction qui insère les balises correspondantes

Il est donc possible de rajouter tous les éléments statiques nécessaires à la construction de l'UI avec les tags simplement en les ajoutant dans l'ui :

ui <- fluidPage(
  tags$h1("Titre h1"), 
  tags$p(tags$code("I <3 R")), 
  tags$p(tags$code("I ",tags$strong("really"), "<3 R"))
)

server <- function(input, output) {
}

shinyApp(ui = ui, server = server)

en recyclant du HTML

Avec la fonction HTML() !

ui <- fluidPage(
HTML("<p>
  <code>
    I 
    <strong>really</strong>
    &lt;3 R
  </code>
</p>")
)

server <- function(input, output) {
}

shinyApp(ui = ui, server = server)

en utilisant les fonctions natives Shiny

Pour découper la page avec des div :

fluidRow()
column(3)
fluidRow("Ligne",
  column(3, "Colonne 1"), 
  column(3, "Colonne 2")
)
ui <- fluidPage(
  fluidRow("Ligne",
           column(width = 3, "Colonne 1", 
                  sliderInput(inputId = "slider", label = "slider", 
                              min=1, max = 5, value = 2)), 
           column(width = 3, "Colonne 2",
                  sliderInput(inputId = "slider", label = "slider", 
                              min=1, max = 5, value = 2))
  )
)

  server <- function(input, output) {
  }

shinyApp(ui = ui, server = server)

ou avec des fonctions *panel :

ls('package:shiny', pattern = "Panel$")

La fonction classiquement utilisée étant sidebarPanel couplée à mainPanel

ui <- fluidPage(
  mainPanel(
    fluidRow("Ligne",wellPanel(
      column(width = 3, "Colonne 1", 
             sliderInput(inputId = "slider", label = "slider", 
                         min=1, max = 5, value = 2))), 
      column(width = 3, "Colonne 2",
             sliderInput(inputId = "slider", label = "slider", 
                         min=1, max = 5, value = 2))
    )
  )
)

server <- function(input, output) {
}

shinyApp(ui = ui, server = server)

en créant un répertoire dédié aux aspects UI

Dès lors que l'on s'oriente vers une customisation avancée de l'apparence de la page, il est de bon ton de créer un répertoire à la racine de app.R ou ui.R et server.R afin d'y hoster la boîte à outils nécessaire au design

Ce répertoire doit s'appeler www :

dir.create("www")

C'est dans ce répertoire que viendra piocher le navigateur les images et le teplate css

en utilisant un template css

...et en le déposant dans le répertoire www

Exercice pour la prochaine fois : Réaliser une application Shiny de votre package de SEO !



Mapsred/ShinySEO documentation built on May 7, 2019, 2:47 p.m.