.. _fluidPage: https://shiny.rstudio.com/reference/shiny/1.3.2/fluidPage.html
.. _titlePanel: https://shiny.rstudio.com/reference/shiny/1.3.2/titlePanel.html
.. _mainPanel: https://shiny.rstudio.com/reference/shiny/1.3.2/mainPanel.html
.. _renderPlot: https://shiny.rstudio.com/reference/shiny/1.3.2/renderPlot.html
.. _shinyApp: https://shiny.rstudio.com/reference/shiny/1.3.2/shinyApp.html
.. _sidebarLayout: https://shiny.rstudio.com/reference/shiny/1.3.2/sidebarLayout.html
.. _sidebarPanel: https://shiny.rstudio.com/reference/shiny/1.3.2/sidebarPanel.html
.. _sliderInput: https://shiny.rstudio.com/reference/shiny/1.3.2/sliderInput.html
.. _shiny:
Construire des dashboards avec Shiny
====================================
Qu'est ce que Shiny ?
---------------------
`Shiny `__ est un package R utilisé pour construire des applications web interactives dans l'environnement R. On peut construire des applications indépendantes, hébergées sur une page web, intégrer des éléments interactifs dans un notebook ou construire des dashboards autonomes. Les dashboards Shiny sont des applications full HTML/CSS/Javascript et peuvent donc être personnalisés à l'envi.
Pour R, Shiny est l'équivalent de la solution Plotly/Dash de l'univers Python.
Structure d'une app Shiny
-------------------------
Une app Shiny est essentiellement structurée en deux composants, le front end (interface utilisateur) et le backend (serveur).
L'interface utilisateur (``ui``) définit l'aspect graphique du front end de l'application. On peut classer les éléments de l\'``ui`` en deux catégories:
- les ``input`` correspondants aux widgets d'entrée (slider, dropdown menus, buttons, check boxes, text, ...)
- les ``output`` (graphiques, tables, zones de texte, ...)
Les interactions avec les ``input`` sont détectées, les nouvelles valeurs capturées et transmises au ``server`` qui met à jour les ``output`` impactés.
Le ``server`` a en charge la préparation des données, et la construction des ``output`` correspondant aux interactions détectées sur l\'``ui``.
.. image:: images/15-shiny-fig01.png
:scale: 100 %
:align: center
Développer une application Shiny consiste donc à écrire le code des deux composants : ``ui`` et ``server``.
Le code générique d'une application Shiny (stockée dans un seul fichier :file:`app.R`) est le suivant::
# Define UI for the app
ui <- fluidPage( ... )
# Define server logic
server <- function(input, output) { ... }
# Run the application
shinyApp(ui = ui, server = server)
Pour améliorer la lisibilité et la maintenabilité, c'est mieux de structurer le code en trois fichiers:
- :file:`ui.R` pour le front end
- :file:`server.R` pour le back end
- :file:`global.R` pour les fonctions utilitaires (lecture des données, nettoyage, etc...)
.. tip::
Il est bien entendu possible de structurer le code en un plus grand nombre de fichiers. Les fichiers élémentaires sont importés avec la fonction :func:`source`.
.. tip::
Pour une application vraiment complexe, on s'intéressera à la `modularisation `_.
Une première app
----------------
Pour illustrer le concept d'application Shiny, on va écrire une première app basée sur le dataset `Gapminder `_.
La première étape est de créer le fichier qui va héberger le code : :command:`File > New File > Shiny Web App...`.
Le fichier est déjà renseigné pour faire fonctionner une app de démonstration. Démarrer l'application avec la commande :command:`Run App` pour vérifier le bon fonctionnement. Le code sera modifié pour développer notre propre application.
Importation
...........
Plusieurs packages seront nécessaires::
library(shiny)
library(gapminder)
library(dplyr)
library(ggplot2)
Front end (``ui``)
..................
On peut définir une ``ui`` de façon très simple. La première version contient simplement deux composants, qui sont passés comme paramètres à la fonction :func:`fluidpage`::
ui <- fluidPage(
# Application title
titlePanel("Life Expectancy vs GDP per Capita"),
# Main panel
mainPanel(plotOutput(outputId = "plot"))
)
La fonction fluidPage_ construit un layout de type ``grid`` constitué de lignes, elles mêmes constituées de colonnes. Les lignes assurent que les éléments qu'elles contiennent apparaissent alignés horizontalement (si le navigateur dispose de l'espace nécessaire). Les colonnes définissent l'espace horizontal alloué à chaque élément. Cet espace horizontal est découpé en 12 unités. La fonction fluidPage_ redessine dynamiquement les composants pour utiliser la totalité de l'espace disponible.
On peut ajouter un titre avec la fonction titlePanel_ function. L'appel de cette fonction crée un tad ``title`` dans la balise ``head`` de la page HTML.
La partie principale de l'affichage est géré dans la fonction mainPanel_.
Back end (``server``)
.....................
Le ``server`` est utilisé pour fournir les données aux composants de l\'``ui``
.. code-block:: r
server <- function(input, output) {
output$plot <- renderPlot({
gapminder %>%
filter(year==2007) %>%
ggplot(aes(x=gdpPercap, y=lifeExp)) +
geom_point()
})
}
La :func:`server` comporte 2 paramètres:
- ``input`` est une référence vers les widgets d'entrée de l\'``ui``
- ``output`` est une référence vers les widgets d'affichage de l\'``ui``
La fonction renderPlot_ retourne un graphique ``ggplot2`` qui est lié à un composant de l\'``ui`` référencé par son ``outputId`` (``plot`` dans le cas présent).
Démarrer l'application
......................
Une fois les composants ``ui`` et ``server`` définis, l'application est démarrée avec la fonction shinyApp_::
shinyApp(ui = ui, server = server)
.. admonition:: Exercice
Régler les paramètres du graphique pour obtenir un résultat similaire à celui du graphique ci dessous.
.. image:: images/15-shiny-fig02.png
:scale: 100 %
:align: center
Améliorer le layout
-------------------
Shiny est une solution full HTML/CSS/Javascript et permet donc la construction de pages web complexes. Sans rentrer dans la complexité du design web, nous allons utiliser quelques `composants Shiny `_ pour ajouter des fonctionnalités à cette première version statique.
.. tip::
Pour une conception approfondie, on consultera le `Application layout guide `_.
On va remplacer ici le composant mainPanel_ par un sidebarLayout_. Ce composant construit une page en deux parties. Le composant ``sidebar`` occupe 1/3 de l'espace alors que le composant ``main`` occupe les 2/3 restants. Le composant ``sidebar`` est affiché avec une couleur de fonc distincte et c'est là qu'il est convenu de placer les widgets de contrôle. Le composant ``main`` occupe les 2/3 restants de l'espace horizontal et on l'utilise traditionnellement pour positionner les graphiques, tables, ... Il faut lui passer 2 paramètres:
- un sidebarPanel_ contenant un widget de contrôle sliderInput_
- un mainPanel_ contenant les graphiques, tables, texte, ...
Le code de l\'``ui`` est donné ci dessous. Le code du ``server`` n'a pas besoin d'être modifié::
ui <- fluidPage(
# Application title
titlePanel("Life Expectancy vs GDP per Capita"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "years",
label = "Year",
min = 1952,
max = 2007,
step = 5,
value = 2007)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput(outputId = "plot")
)
)
)
Un ``slider`` est bien présent sur le layout mais on peut constater que le lien n'a pas été établi avec le ``server`` et le graphique est statique.
.. image:: images/15-shiny-fig03.png
:scale: 100 %
:align: center
Ajouter de l'interactivité
--------------------------
Chaque composant de l\'``ui`` possède un ``id`` ce qui permet les références croisées entre le ``server`` et l\'``ui``. Chaque composant de contrôle de l\'``ui`` est référencé par son ``inputId`` et chaque composant d'affichage est référencé par son ``outputId`` en utilisant l'opérateur ``$`` (``input$id`` et ``output$id``):
- ``sliderInput`` est un widget de contrôle avec un paramètre ``inputId = "years``. On le référencera coté ``server`` avec la syntaxe ``input$years``
- ``plotOutput`` est un widget d'affichage avec un paramètre ``outputId = "plot"``. On le référencera coté ``server`` avec la syntaxe ``output$plot``
Coté ``server``, le code utilise l'élément sliderInput_ pour filtrer le dataset Gapminder:
.. code-block:: r
server <- function(input, output) {
output$plot <- renderPlot({
gapminder %>%
filter(year==input$years) %>%
ggplot(aes(x=gdpPercap, y=lifeExp, color=continent, size=pop)) +
geom_point()
})
}
On dispose maintenant d'un affichage dynamique qui utilise les valeur du widget de contrôle pour sélectionner les données à afficher.
.. admonition:: Exercice
Le composant sliderInput_ possède un paramètre ``animate``. Quel est son rôle. Vérifier son fonctionnement correct.
Expressions réactives
.....................
Dans une application Shiny, chaque fois qu'un widget de contrôle est modifié, toutes les expressions qui le référencent sont modifiées. Dans l'exemple précédent, chaque fois que le sliderInput_ est modifié la fonction :func:`renderPlot` est appelée pour construire un nouveau graphique. Cette façon de faire est efficace, aucun calcul additionnel n'est déclenché.
Ce n'est pas toujours le cas, et parfois des expressions sont inutilement ré évaluées, ce qui ralentit l'application. Pour éviter ceci, on met en oeuvre le concept d'expressions réactives. Cette façon de faire permet de contrôler les parties de l'application nécessitant une nouvelle évaluation. Une expression réactive est une expression utilisant un widget de contrôle et retournant une valeur. L'expression réactive est ré évaluée chaque fois que le widget de contrôle est modifié. On met ça en oeuvre avec la fonction :func:`reactive`.
Les expressions réactives mettent leur valeur en cache et sont alertées lorsque les valeurs sont à rafraichir. La première fois qu'une expression réactive est utilisée, elle est évaluée et la valeur de retour est stockée en mémoire (mise en cache). Lorsqu'elle est à nouveau sollicitée, si le widget de contrôle n'a pas été modifié, elle retourne le résultat de son évaluation sans aucun calcul, ce qui fluidifie le comportement de l'application.
Une expression réactive ne retourne le résultat en cache que s'il est pertinent. Si le widget de contrôle a été modifié, elle est à nouveau évaluée, et le résultat correspondant mis en cache lui aussi. On s'assure ainsi que l'évaluation est faite une et une seule fois pour chacune des valeurs du widget de contrôle.
Pour une mise en oeuvre, on consultera le tutorial sur `les expressions réactives `_.
Ressources additionnelles
-------------------------
- `Le tutoriel Shiny officiel `_
- `The Shiny Cheat sheet `_
- `How to start with Shiny `_
- `Un autre tutorial Shiny `_
- `Une liste d'articles `_
- La `référence des fonctions Shiny `_