shiny
shiny
permet de faire des applications interactives avec R. Pour fonctionner, ces applications nécessitent une machine, le serveur, qui doit faire tourner R tout au long de leur utilisation.
Une application shiny
s’écrit généralement sous la forme suivante :
shinyApp()
pour lancer l’application.library(shiny)
<- fluidPage()
ui
<- function(input, output) {}
server
shinyApp(ui = ui, server = server)
La partie UI récupère les inputs de l’utilisateur et affiche les outputs calculés par le serveur.
Les inputs permettent de récupérer des instructions de l’utilisateur et sont généralement déclarés par des fonctions de la forme ****Input(inputId = "id", ...)
ou des clique-boutons comme actionButton(inputId = "id", ...)
. De façon non exhaustive :
Les outputs déterminent des éléments de l’application dédiées à l’affichage d’éléments graphiques ou textuels et sont déclarés par des fonctions de la forme ****Output(outputId = "id", ...)
. Les plus utiles :
plotOutput(outputId = "id", ...)
textOutput(outputId = "id", ...)
dataTableOutput(outputId = "id", ...)
Pour modifier les outputs et donc influer sur les éléments affichés dans l’application, il faut :
output$id = ...
render****({ ... })
(renderPlot
, renderText
, renderDataTable
, …)input$id
<- fluidPage(
ui sliderInput(inputId = "num",
label = "Choose a number",
value = 25, min = 1, max = 100),
plotOutput("hist")
)
<- function(input, output) {
server $hist <- renderPlot({
outputhist(rnorm(input$num))
}) }
De cette façon, l’histogramme affiché par ouput$hist
est automatiquement mis à jour lorsque l’utilisateur modifie le slider représenté par input$num
. C’est ce qui est appelé la réactivité (plus généralement la notion d’observateur en programmation orientée objet, qui permet de construire des objets qui s’actualisent automatiquement lorsque d’autres objets changent).
Le code R doit être sauvegardé soit : dans un fichier nommé app.R
; soit dans deux fichiers, ui.R
et server.R
.
Une application shiny
peut être déposée sur https://www.shinyapps.io/ pour pouvoir être accessible en ligne par tous. La version gratuite du service permet de partager jusqu’à 5 applications, utilisables jusqu’à 25 heures par mois. Un tutoriel pour publier les applications sur le site est disponible à l’adresse https://shiny.rstudio.com/articles/shinyapps.html.
La fonction server
n’est appelée qu’une unique fois au lancement de l’application. Pour qu’une variable soit modifiée par l’utilisateur, elle doit donc être déclarée comme réactive. Par exemple, comme vu précédemment, les variables d’output (spécifiées par output$id
) doivent ensuite être encapsulées dans des méthodes du type render****()
. Cela a pour effet de rendre la variable output$id
réactive : dès lors qu’une des variables d’input (spécifiées par input$id
) qui intervient change, la variable d’output est mise à jour en conséquence. Notons que, en l’absence d’une méthode render****()
, le code suivant rendrait une erreur au moment du lancement de l’application :
<- function(input, output) {
server $hist <- hist(rnorm(input$num))
output }
Il est également possible de définir des variables intermédiaires (ni output, ni input) réactives.
reactive({ ... })
Cette fonction permet de rendre une expression réactive, i.e. qui sera mise à jour dès lors que des variables, de type input ou elles-mêmes réactives, et intervenant dans l’expression, seront modifiées. A noter toutefois le fait qu’une variable définie ainsi se comporte comme une fonction : elle doit être appelée sans oublier les parenthèses.
<- function(input, output) {
server = rnorm(100) # Lancé une unique fois lorsque l'application est démarrée
x = reactive({ x[1:input$num] })
mydata $hist <- renderPlot({
outputhist( mydata() )
}) }
isolate({ ... })
Cette fonction, qui s’utilise dans la définition d’une expression réactive, permet de casser la dépendance réactive d’une variable réactive. Ainsi, dans le code suivant :
<- function(input, output) {
server = rnorm(100) # Lancé une unique fois lorsque l'application est démarrée
x = reactive({ x[1:input$num] })
mydata $hist <- renderPlot({
outputhist( mydata() , title = isolate({ input$title }))
}) }
le titre du graphique n’est pas mis à jour lorsque la variable input$title
change. Par contre, lorsque la variable input$num
change, la variable réactive mydata()
est mise à jour, ce qui entraine également la mise à jour d’output$hist
. Dans ce cas, la dernière valeur de input$title
sera utilisée.
observeEvent(reactiveExpression, { ... })
Cette commande permet d’exécuter du code dès lors que la variable reactiveExpression
, et seulement celle-ci, est mise à jour.
observeEvent(input$num, {
print("New input detected!!")
print(input$title)
})
La fonction observe({ ... })
est similaire, mais à la différence de observeEvent
, est exécutée dès lors que n’importe quelle expression réactive à l’intérieur de { ... }
est mise à jour.
eventReactive(reactiveExpression, { ... })
Cette fonction est similaire à observeEvent
mais retourne une expression réactive, mise à jour uniquement lorsque reactiveExpression
change. Cela permet donc de définir une variable réactive dépendante d’un déclencheur.
<- function(input, output) {
server = rnorm(100) # Lancé une unique fois lorsque l'application est démarrée
x = reactiveEvent(input$num, { x[1:length(input$title)] })
mydata $hist <- renderPlot({
outputhist( mydata() , title = isolate({ input$title }))
}) }
reactiveValues( ... )
Cette fonction retourne une liste de valeurs réactives, qui peuvent ensuite être manipulées de façon dynamique.
<- function(input, output) {
server = reactiveValues(x = rnorm(100), y = NA)
mydata observe({
$y = mydata$x * input$num
mydata
})$hist <- renderPlot({
outputhist( mydata() , title = isolate({ input$title }))
}) }
reactiveVal(.)
se comporte similairement, mais ne contient qu’une seule valeur, et une variable var = reactiveVal(0)
définie par cette méthode, se comporte alors comme une fonction get/set : elle doit être appelée avec les parenthèses var()
, et est mise à jour avec la nouvelle valeur en argument var(1)
.
Il existe un moyen de récupérer la position d’un click de l’utilisateur sur un graphique, qui pourra ensuite être utilisé comme input dans le reste de l’application. La fonction ui
doit être modifiée pour rendre possible le suivi de la souris sur le graphique :
= fluidPage(
ui plotOutput(outputId = "plot", click = "click")
)
Désormais, la variable input$click
contient les informations du click. Par exemple, on peut accéder à la position du click via input$click$x
et input$click$y
.
= function(input, output) {
server = reactiveValues(x = 0, y = 0)
myclicks observeEvent(input$click, {
$x = c(myclicks$x, input$click$x)
myclicks$y = c(myclicks$y, input$click$y)
myclicks
})$plot = renderPlot({
outputggplot(data.frame(x = myclicks$x, y = myclicks$y)) +
aes(x = x, y = y) +
geom_point() +
coord_cartesian(xlim = c(-10, 10), ylim = c(-10, 10))
}) }
conditionalPanel(condition, ...)
Ce sont des éléments de la fonction ui
, qui n’apparaissent que lorsque la condition
est vérifiée. A noter que cette condition doit être codée en javascript (en particulier, les éléments d’une liste sont appelés avec le .
plutôt que le $
, par exemple input.num
).
= fluidPage(
ui actionButton(inputId = "btn", "Clique-moi"),
conditionalPanel("input.btn", actionButton(inputId = "btn1", "Clique-moi aussi")),
conditionalPanel("input.btn1", actionButton(inputId = "btn2", "Félicitations"))
)
Par défaut, les variables accessibles dans la fonction ui
, et donc sur lesquelles il est possible de conditionner le conditionalPanel
, sont celles contenues dans les listes input$...
et output$...
déjà existantes. Si l’on veut conditionner sur d’autres variables, potentiellement après des manipulations dans la fonction server
, il est nécessaire de définir d’autres éléments de la liste output
, puis de les rendre visibles à l’UI. Par exemple, pour rendre visible un élément après un click :
= fluidPage(
ui "Clique sur (0,0)",
plotOutput(outputId = "plot", click = "click"),
conditionalPanel("output.showpanel", textOutput(outputId = "text"))
)
= function(input, output) {
server $plot = renderPlot({
outputggplot(data.frame(x = 0, y = 0)) +
aes(x = x, y = y) +
geom_point() +
coord_cartesian(xlim = c(-10, 10), ylim = c(-10, 10))
})
# Définit une variable réactive qui va prendre la valeur 1 dès lors que
# l'utilisateur aura cliqué sur l'origine du graphique
= reactiveVal(0)
hasclicked observeEvent(input$click, {
if (sqrt(input$click$x^2 + input$click$y^2) < .1) {
hasclicked(1)
}
})
# Définit un élément de l'output, qui sera à terme visible dans l'ui
$showpanel = reactive({
outputreturn(hasclicked())
})outputOptions(output, "showpanel", suspendWhenHidden = FALSE)
# l'argument 'suspendWhenHidden = FALSE' permet de rendre visible cette
# variable dans l'ui
# Affiche un message de félicitations si l'utilisateur a cliqué sur l'origine
$text = renderText({
output"Bravo, tu as cliqué sur l'origine."
}) }
renderUI
La fonction renderUI
permet d’adapter les objets UI avec des valeurs calculés dans la partie serveur. L’appel dans la partie UI se fait par la fonction spécifique uiOutput()
. Dans ce cas là, les objets UI sont définis dans la partie serveur du code source et peuvent être dépendant d’une variable réactive ou d’un input.
<- fluidPage(
ui uiOutput(outputId = "uiInput"),
textOutput(outputId = "text")
)
<- function(input, output){
server <- reactive(norm(100))
x $uiInput <- renderUI({
outputnumericInput(inputId = "num", label = "choose a number",
value = mean(x()))
})$text <- renderText({
output$num
input
}) }
req()
https://shiny.rstudio.com/articles/req.htmlshiny
: https://github.com/rstudio-education/shiny.rstudio.com-tutorial