- Architecture d’une application Shiny
- Notion de réactivité de objets
- Pratique sur exemples simples
- Comment partager une application
06/02/2021
Les fichiers seront sur le SOTR Rennes :
https://stateofther.github.io/rennes/.
La séance est enregistrée et sera disponible par ici :
Une application est avant tout un outil adapté à la diffusion & à la collaboration :
Une application est avant tout un outil adapté à la diffusion & à la collaboration :
…et tout ça depuis R !!!
Une application Shiny peut servir à :
Livres & tutos
Une application Shiny comporte deux composants majeurs qui vont intéragir en permanence lors de l’utilisation :
shinyApp( ui = fluidPage( titlePanel("Bienvenue sur ta 1e application !"), sidebarLayout( sidebarPanel( strong("BARRE LATERALE"), br(), "C'est ici que vous pourrez rentrer les informations/instructions utilisées par l'application pour visualiser différents éléments" ), mainPanel( strong("PANNEAU PRINCIPAL:"), br(), "C'est là que seront représentées les sorties (graphiques, tableaux, texte, images etc.)" ) ) ), server = function(input, output) {} )
Plus pratique pour les grosses applications :
ui <- fluidPage( titlePanel("Bienvenue sur ta 1e application !"), sidebarLayout( sidebarPanel( strong("BARRE LATERALE"), br(), "C'est ici que vous pourrez rentrer les informations/instructions utilisées par l'application pour visualiser différents éléments" ), mainPanel( strong("PANNEAU PRINCIPAL:"), br(), "C'est là que seront représentées les sorties (graphiques, tableaux, texte, images etc.)" ) ) ) server<- function(input, output) {} shinyApp(ui, server)
Un aperçu de ce que ça donne :
#remotes::install_github("allisonhorst/palmerpenguins") data(penguins,package = "palmerpenguins") penguins <- penguins %>% rename(bill_l = bill_length_mm, bill_d = bill_depth_mm, flip_l = flipper_length_mm, bm = body_mass_g) penguins %>% print(n=2)
## # A tibble: 344 x 8 ## species island bill_l bill_d flip_l bm sex year ## <fct> <fct> <dbl> <dbl> <int> <int> <fct> <int> ## 1 Adelie Torgersen 39.1 18.7 181 3750 male 2007 ## 2 Adelie Torgersen 39.5 17.4 186 3800 female 2007 ## # … with 342 more rows
Tronche de pingouin :
## species island bill_l bill_d ## Adelie :152 Biscoe :168 Min. :32.10 Min. :13.10 ## Chinstrap: 68 Dream :124 1st Qu.:39.23 1st Qu.:15.60 ## Gentoo :124 Torgersen: 52 Median :44.45 Median :17.30 ## Mean :43.92 Mean :17.15 ## 3rd Qu.:48.50 3rd Qu.:18.70 ## Max. :59.60 Max. :21.50 ## NA's :2 NA's :2 ## flip_l bm sex year ## Min. :172.0 Min. :2700 female:165 Min. :2007 ## 1st Qu.:190.0 1st Qu.:3550 male :168 1st Qu.:2007 ## Median :197.0 Median :4050 NA's : 11 Median :2008 ## Mean :200.9 Mean :4202 Mean :2008 ## 3rd Qu.:213.0 3rd Qu.:4750 3rd Qu.:2009 ## Max. :231.0 Max. :6300 Max. :2009 ## NA's :2 NA's :2
Exemple de graph à partir duquel vous travaillerez:
En tripatouillant autour de ce jeu de données et de ce graph, nous allons essayer de réaliser qlq exercices pratiques de niveau croissant:
Comment a-t-on fait ?
ui <- fluidPage( titlePanel("Ma Donnée de Piafs"), sidebarLayout( sidebarPanel( "Pour l'instant, pas d'intéractivité, vous dîtes seulement à l'application de représenter une figure 'figée'." ) , mainPanel( "Ci-dessous notre jolie figure :", plotOutput("pengPlot") ) ) ) server<- function(input, output) { output$pengPlot = renderPlot({ penguins %>% ggplot() + aes( x= bill_l, y=bill_d, col = species) + geom_point() + labs( x = 'Bill length in mm') + labs(y = 'Bill depth in mm') + labs(color = "Species")+ theme_light() }) } shinyApp(ui, server)
Comment a-t-on fait ?
output$pengPlot = renderPlot({ penguins %>% ggplot() + aes( x= bill_l, y=bill_d, col = species) + geom_point() + labs( x = 'Bill length in mm') + labs(y = 'Bill depth in mm') + labs(color = "Species")+ theme_light() })
Comment a-t-on fait ?
mainPanel( "Ci-dessous notre jolie figure :", plotOutput("pengPlot") )
Comment a-t-on fait ?
renderPlot() plotOutput()
Les fonctions Shiny permettent de
Les fonctions Shiny permettent de
On verra dans ce TD quelques fonctions Shiny. Commençons par les fonctions appartenant à la 3e catégorie.
Génèrent des sorties de différents types dans l’UI
Prennent en entrée une / plusieurs lignes de code générant cette sortie
L’objet répond à chaque fois qu’une valeur réactive présente dans le code change
Fonction (Server) | Creates | UI (appelle l’obj. créé dans server) |
---|---|---|
renderDataTable() | An interactive table | dataTableOutput() |
renderImage() | An image | imageOutput() |
renderPlot() | A plot | plotOutput() |
renderPrint() | A code block of printed output | print() |
renderTable() | A table | tableOutput() |
renderText() | A character string | textOutput() |
renderUI() | A Shiny UI element | uiOutput() |
Faciliter l’interactivité avec le package plotly (déjà chargé). Permet d’otenir les valeurs aux points en survolant le graph, de zoomer, de télécharger le graph en .png etc. Il suffit de remplacer les fonctions graphiques par :
renderPlotly() plotlyOutput()
Une application Shiny comporte deux composants majeurs qui vont intéragir en permanence lors de l’utilisation :
“output” permet de communiquer de “SERVER” vers “UI”
“input” permet de communiquer de “UI” vers “SERVER”
Une multiplicité de “Widgets” sont à votre disposition pour faire intéragir “UI” et “SERVER”.
Pour en avoir un aperçu, allez faire un tour dans la widget gallery
Une multiplicité de “Widgets” sont à votre disposition pour faire intéragir “UI” et “SERVER”.
Pour en avoir un aperçu, allez faire un tour dans la widget gallery
Dans ce TD, on va voir quelques widgets, comment les mobiliser en fonction de nos besoins dans l’applciation, et comment les faire insérer dans notre code pour obtenir les fonctionnalités escomptées.
Créer un menu déroulant pour sélectionner l’espèce que l’on veut représenter.
selectInput("NOM DE L'INPUT", label = "NOM/CONSIGNE POUR L'UTILISATEUR", choices = list("NomChoix1" = "Choix1", "NomChoix2" = "Choix2",..., "NomChoixn" = "Choixn",), selected = [Choix par défaut], multiple=[TRUE si plusieurs choix autorisés])
A vous de jouer!
ui <- fluidPage( ... sidebarPanel( strong("Ma belle barre latérale"), selectInput("select", label = h3("Select Species"), choices = list("ADELIE" = "Adelie", "CHINSTRAP" = "Chinstrap", "GENTOO" = "Gentoo"), selected = "Adelie", multiple=T) ) ... ) )
... server<- function(input, output) { output$pengPlot = renderPlotly({ penguins %>% filter(species%in%input$select) %>% ggplot() + aes( x= bill_l, y=bill_d,col = species) + geom_point() + labs( x = 'Bill length in mm') + labs(y = 'Bill depth in mm') + labs(color = "Species")+ theme_light() }) } ...
Créer une glissière de sélection de plage temporelle couverte par les données représentées.
sliderInput("NOM DE L'INPUT", label = "TITRE", min = "min value", max = "max value", value = c("min value","max value"))
A vous de jouer!
ui <- fluidPage( ... selectInput("select", label = h3("Select Species"), choices = list("ADELIE" = "Adelie", "CHINSTRAP" = "Chinstrap", "GENTOO" ="Gentoo"), selected = "Adelie", multiple=T), sliderInput("slider", label = h3("Slider"), min = 2007, max = 2009, value = c(2007,2009)) ... )
server<- function(input, output) { output$pengPlot = renderPlotly({ penguins %>% filter(species%in%input$select) %>% filter(year>=input$slider[1]) %>% filter(year<=input$slider[2]) %>% ggplot() + aes( x= bill_l, y=bill_d,col = species) + geom_point() + labs( x = 'Bill length in mm') + labs(y = 'Bill depth in mm') + labs(color = "Species")+ theme_light() }) }
Permettre de représenter l’ajustement de régressions linéaires (utiliser les codes ggplot de MP Etienne dispo sur le SOTR - Rennes)
checkboxInput("NOM DE L'INPUT", label = "CONSIGNE", value = FALSE)
A vous de jouer !
ui <- fluidPage( ... selectInput("select", label = h3("Choisir une espèce"), choices = list("ADELIE" = "Adelie", "CHINSTRAP" = "Chinstrap", "GENTOO" ="Gentoo"), selected = "Adelie", multiple=T), sliderInput("slider", label = h3("Choisir une période"), min = 2007, max = 2009, value = c(2007,2009)), checkboxInput("checkbox", label = "Draw regression", value = FALSE) ... )
server<- function(input, output) { output$pengPlot = renderPlotly({ penguins %>% ... ...->toplot if(input$checkbox==T){ toplot<-toplot+geom_smooth(method = 'lm', se = FALSE) } toplot }) }
Utiliser checkboxGroup pour sélectionner le type de régression (linéraire vs loess) à représenter graphiquement. Utiliser checkbox pour ajouter l’enveloppe dincertitude ou non.
(outil analogue = radioButtons; mais sélection d’un seul choix possible)
checkboxGroupInput("NOM DE L'INPUT", label = "Consigne", choices=c("CHOIX1"="choix1","CHOIX2"="choix2"))
ui <- fluidPage( ... selectInput("select", label = h3("Choisir une espèce"), choices = list("ADELIE" = "Adelie", "CHINSTRAP" = "Chinstrap", "GENTOO" ="Gentoo"), selected = "Adelie", multiple=T), sliderInput("slider", label = h3("Choisir une période"), min = 2007, max = 2009, value = c(2007,2009)), checkboxGroupInput("checkbox1", label = "Draw regression", choices=c("Linear reg."="linear","Loess"="loess")), checkboxInput("checkbox2", label = "Represent uncertainty", value = FALSE) ... )
server<- function(input, output) { output$pengPlot = renderPlotly({ penguins %>% ... ...->toplot if("linear"%in%input$checkbox1){ toplot<-toplot+geom_smooth(method = 'lm', se = input$checkbox2) } if("loess"%in%input$checkbox1){ toplot<-toplot+geom_smooth(method = 'loess', se = input$checkbox2) } toplot }) }
Ajoutez sous votre graph une table de données réactive présentant les valeurs de la table penguins sélectionnées pour le graph.
A vous de jouer !
ui <- fluidPage( ... mainPanel( "Panneau principal: ici sont représentées les sorties désirées", plotlyOutput("pengPlot"), dataTableOutput("table") ) ... )
server<- function(input, output) { output$pengPlot = renderPlotly({ penguins %>% filter(species%in%input$select) %>% filter(year>=input$slider[1]) %>% filter(year<=input$slider[2]) %>% ggplot() + ...+ theme_light()->toplot ... }) output$table = renderDataTable({ penguins %>% filter(species%in%input$select) %>% filter(year>=input$slider[1]) %>% filter(year<=input$slider[2]) }) }
En procédant ainsi, l’appli fonctionne mais vous écrivez deux fois le traiement de la table penguins…
En procédant ainsi, l’appli fonctionne mais vous écrivez deux fois le traiement de la table penguins…
On va donc créer un seul et unique objet réactif qui sera le fruit du traitement de penguins et l’on utilisera cet objet pour la figure et la table.
Pour ce faire on utilise une fonction Shiny : reactive().
Cette fonction confère un statut réactif à un objet. A chaque fois qu’un “input” sera modifié, le script créant notre objet sera relancé et les scripts traitant cet objet le seront également.
server<- function(input, output) { penguinsBIS<-reactive({penguins%>% filter(species%in%input$select) %>% filter(year>=input$slider[1]) %>% filter(year<=input$slider[2])}) output$pengPlot = renderPlotly({ penguinsBIS() %>% ggplot() + ... ...->toplot ... }) output$table = renderDataTable({ penguinsBIS() }) }
A l’aide de selectInput, mettre en place une sélection des données à représenter basée sur
ui <- fluidPage( ... selectInput("spsel", label = h3("Choisir l'espèce"), choices = list("ADELIE" = "Adelie", "CHINSTRAP" = "Chinstrap", "GENTOO" ="Gentoo"), selected="Adelie", multiple=F), selectInput("islandsel", label = h3("Choisir l'île"), choices = list("BISCOE" = "Biscoe", "DREAM" = "Dream", "TORGENSEN" ="Torgersen"), selected="Biscoe", multiple=F), selectInput("sexsel", label = h3("Choisir le sexe"), choices = list("Males" = "male", "Femelles" = "female", "Unrecorded"=NA), selected="male", multiple=F) ) ... )
server<- function(input, output) { output$pengPlot = renderPlotly({ penguins %>% filter(species%in%input$spsel)%>% filter(island%in%input$islandsel)%>% filter(sex%in%input$sexsel)%>% ggplot() + aes( x= flip_l, y=bm) + geom_point() + labs(x = 'Flipper length') + labs(y = 'Body mass') + theme_light() }) }
En procédant ainsi, on met en évidence que certaines combinaisons de critères ne sont pas possiles : seule l’espèce Adélie est échantillonnée sur toutes les îles ..!
En procédant ainsi, on met en évidence que certaines combinaisons de critères ne sont pas possiles : seule l’espèce Adélie est échantillonnée sur toutes les îles ..!
On va donc chercher à contraindre les choix des menus déroulants aux seules combinaisons possibles.
Ici on va utiliser une fonction d’“update” :
updateSelectInput(session,"NOM DU selectInput A UPDATER", "CONSIGNE", choices = " NOUVEAUX CHOIX", selected=" NOUVEAUX CHOIX ACTIFS")
(session sera à préciser à la fois dans la fonction update et comme paramètre d’entrée de la fonction SERVER)
Et on aura beson d’une nouvelle fonction Shiny : observeEvent().
Cette fonction nous permet de contrôler la réactivité (l’activité) de notre updateSelectInput.
On va ainsi conditionner la mise à jour des champs proposés pour “islands” à l’espèce sélectionnée (donc dès qu’il y a un changement d’espèce) ; et les champs proposés pour “sex” à la combinaison espèce-île choisie (donc dès qu’il y a un changement d’espèce ou d’île).
On crée un flux d’information de l’“UI” vers le “SERVER” puis du “SERVER” vers l’“UI” etc.
ui <- fluidPage( ... selectInput("spsel", label = h3("Choisir l'espèce"), choices = list("ADELIE" = "Adelie", "CHINSTRAP" = "Chinstrap", "GENTOO" ="Gentoo"), selected="Adelie", multiple=F), selectInput("islandsel", label = h3("Choisir l'île"), choices = list("BISCOE" = "Biscoe", "DREAM" = "Dream", "TORGENSEN" ="Torgersen"), selected="Biscoe", multiple=F), selectInput("sexsel", label = h3("Choisir le sexe"), choices = list("Males" = "male", "Femelles" = "female", "Unrecorded"=NA), selected="male", multiple=F) ) ... )
server<- function(input, output, session) { observeEvent(input$spsel, { newislands <- as.character(unique(penguins$island[which(penguins$species%in%input$spsel)])) updateSelectInput(session,"islandsel", "Choisir l'île", choices = c(newislands), selected=c(newislands)[1]) }) observeEvent(input$spsel, { newsex <- as.character(unique(penguins$sex[which(penguins$species%in%input$spsel & penguins$island%in%input$islandsel)])) updateSelectInput(session,"sexsel", "Choisir le sexe", choices = c(newsex), selected=c(newsex)[1]) }) observeEvent(input$islandsel, { newsex <- as.character(unique(penguins$sex[which(penguins$species%in%input$spsel & penguins$island%in%input$islandsel)])) updateSelectInput(session,"sexsel", "Choisir le sexe", choices = c(newsex), selected=c(newsex)[1]) }) }
Autres fonctions Shiny permettant de contrôler la réactivité des objets
Nous avons vu un exemple d’application simple : Une seule fenêtre et un seul graph. Mais on peut compelxifier la structure de l’application en utilisant différentes tables dans notre paneau principal et en affichant plusieurs grapiques sur la page.
Des panneaux peuvent être créer pour disposer de plusieurs pages d’affichage.
Nous avons vu un exemple d’application simple : Une seule fenêtre et un seul graph. Mais on peut compelxifier la structure de l’application en utilisant différentes tables dans notre paneau principal et en affichant plusieurs grapiques sur la page.
Des panneaux peuvent être créer pour disposer de plusieurs pages d’affichage.
La mise en page d’une application Shiny repose sur une grille définie par 12 colonnes et 12 lignes.
Utilisation de packages spécifique :
(cyborg, superhero, darkly, united etc. Il faut être indulgent pour les noms de thèmes, les mecs sont à la fois geeks et américains)
Utilisation de packages spécifique :
Utilisation de packages spécifique :
De nombreuses applications Shiny présentes sur internet fournissent un lien vers le code source. Ne pas hésiter à aller piocher ce qui vous intéresse dans ces applications et les insérer dans la votre.
Deux cas de figure
Définir le besoin :
Une application fonctionne sur un serveur. Ce serveur peut-être
Compte gratuit donne accès à 5 applications maximum. Sinon, si on veut en créer une nouvelle il faut en retirer une.
Préliminaire :
Compte gratuit donne accès à 5 applications maximum. Sinon, si on veut en créer une nouvelle il faut en retirer une.
Cliquer sur le petit icône bleu en haut à droite de la fenêtre où s’affiche votre application.
Entrer vos identifiant et mot de passe shiny.io.
Sélectionner les dossiers/fichiers nécessaires au fonctionnement de votre application
Lancer la procédure (+/- long suivant la complexité de votre application). Une fenêtre vers votre application en ligne s’affichera une fois l’application déployée sur les serveurs de RStudio
Don’t Worry…
Don’t Worry…
Be Shiny