Alors ?
Moi ça m’est déjà arrivé.
En fait, ça m’arrive quasiment tout le temps.
Au début, on code, on code, on utilise la fonction reactive()
, on ajoute quelques filtres.
Et puis on paramètre un filtre pour qu’il change la liste des choix d’un autre filtre.
Et puis on rajoute des calculs intermédiaires sur les données pour renseigner encore un troisième filtre.
Et puis on code des nouvelles features, par exemple cliquer sur un tableau pour faire apparaître un nouvel onglet.
Etc.
Plus vous en rajoutez, plus ça devient du spaghetti code !
Vous créez des liens de dépendance dans tous les sens.
Et au bout d’un moment, votre app devient fragile.
Si on clique trop vite sur les filtres, ils se mettent à clignoter dans tous les sens.
Quand on clique sur le tableau, ça recharge tellement de fois que vous préférez fermer les yeux pendant 10 secondes le temps que ça stabilise.
À chaque nouveau changement du code, vous avez peur que ça pète dans tous les sens.
:/
Bravo.
Vous avez créé un monstre.
Il est temps de changer de poste et de laisser le bouzin à votre successeur :D
Non sérieusement.
Vous êtes un type bien.
Donc vous allez faire une petite factorisation et revoir tout ça.
C’est l’objectif de cet article :
Vous aider à comprendre pleinement la réactivité, les liens de dépendances et d’interdépendances, et vous donner des stratégies afin de concevoir une application Shiny rapide, fluide, robuste, et facile à maintenir.
La réactivité permet de mettre à jour automatiquement une sortie (par exemple : un graphique, un tableau, une carte Leaflet, ou n’importe quel code HTML) lorsqu’une entrée est modifiée (un textInput
, un selectInput
, un jeu de données, etc.).
On va partir d’un exemple avec le jeu de données iris
.
Vous pouvez directement copier-coller le code ci-dessous dans votre console R pour essayer :
Vous pouvez retrouver le code ou visualiser l’application grâce aux liens ci-dessus.
On a une application très simple :
input$species
, qui permet de choisir l’espèce.data
qui est défini en utilisant la fonction reactive()
(on va y revenir).data()
pour tracer un graphique.Tous les objets qu’on manipule ici sont des objets réactifs.
Un objet peut être réactif de deux manières différentes :
Toutes les fonctions en Shiny qui terminent par Input
(comme selectInput
, textInput
, shinyWidgets::pickerInput
, dateInput
, etc.) sont des entrées réactives.
On pourrait aussi dire que tous les objets à l’intérieur de input
(dans notre exemple : input$species
) sont des entrées réactives.
Toutes les fonctions en Shiny qui terminent par Output
(comme plotOutput
, dataTableOutput
, leaflet::leafletOutput
, etc.) sont des sorties réactives.
Pareil, on peut aussi dire que tous les objets qui s’écrivent output$nom
(dans notre exemple : output$plot
) sont des sorties réactives.
À retenir : Lorsqu’une sortie réactive utilise une entrée réactive dans son code, alors la sortie est automatiquement recalculée dès que l’entrée change.
Ensuite, on a les cas hybrides, qui sont à la fois des entrées et des sorties réactives.
Dans notre exemple, c’est le cas de data
.
data
est un objet réactif parce que nous avons utilisé la fonction reactive()
.
C’est à la fois une sortie réactive parce qu’il va être automatiquement recalculé dès que les entrées réactives qu’il utilise changent. Ici, dès que input$species
va changer, alors data
va être recalculé.
Mais c’est aussi une entrée réactive parce que dès que data
va changer, alors l’affichage du graphique output$plot
va être automatiquement recalculé.
Quand on change input$species
, le jeu de données data
est recalculé, ce qui provoque le recalcul du graphique output$plot
.
Bon.
Là on a une application très simple. C’est ensuite que ça se complique.
Et si on n’avait pas utilisé reactive()
?
Ah oui ! Bonne question.
Si on écrit directement :
Shiny n’est pas content et retourne une erreur :
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.)
En effet, on n’a pas le droit d’utiliser une valeur réactive en dehors d’un contexte réactif.
Un contexte réactif, ça va être :
reactive
(ou eventReactive
)observe
(ou observeEvent
)output
, dont la fonction démarre toujours par render
(renderPlot
dans notre exemple)Donc on n’a pas le droit d’utiliser input$species
dans la nature.
req
, isolate
, etc.Avant de parler stratégie ou de voir des exemples plus complexes, je pense que ça peut être utile de faire le tour des fonctions qui existent autour de la réactivité en Shiny.
Parfois, on prend des habitudes et on ignore complètement l’existence de certaines fonctions.
Par exemple, ça m’a mis des années avant de découvrir la fonction req
! Avant, je mettais des is.null()
partout (et je ne suis pas le seul !).
Je vais aussi en profiter pour vous montrer des exemples où ces fonctions sont particulièrement adaptées.
reactive()
On en a déjà parlé, la fonction reactive()
permet de créer un objet réactif qui est à la fois une entrée réactive et une sortie réactive.
Ça veut dire que l’objet va se mettre à jour automatiquement si les entrées qu’il utilise changent, et il va automatiquement déclencher la mise à jour des sorties où il est utilisé.
La fonction reactive()
agit comme la déclaration d’une fonction :
return
pour retourner un résultat avant la fin du bloc de code.Ce dernier point est crucial et est une distinction importante avec la fonction observe()
que nous allons voir juste après.
En effet, il est inutile de calculer un jeu de données qui est utilisé dans un onglet de votre appli tant que l’utilisateur ne clique pas sur cet onglet !
Exemple avec deux onglets :
Pour illustrer ce concept, je vais vous montrer un autre exemple avec deux onglets, un pour le jeu de données iris
et l’autre avec le jeu de données mtcars
.
Dans le chargement de mtcars
, j’ajoute un temps d’attente de 5 secondes pour simuler le chargement d’un gros jeu de données.
Voici le code :
Essayez l’application en cliquant sur le lien juste au-dessus.
Vous allez voir que quand vous visitez l’onglet mtcars
, le graphique prend environ 5 secondes à charger, ce qui démontre que le reactive data_mtcars
n’est pas lu tant qu’il n’est pas demandé.
Si ensuite vous changez d’onglet et revenez sur mtcars
, il n’y a pas de rechargement puisque les données n’ont pas changé.
Enfin, il faut garder en tête que pour utiliser l’objet réactif ainsi créé, il faut mettre des parenthèses. D’où l’utilisation de data_iris()$Sepal.Length
dans l’exemple ci-dessus. Après tout, comme on a dit que reactive()
agissait comme la déclaration d’une fonction, c’est assez logique !
À retenir :
reactive()
agit de manière similaire à la déclaration d’une fonction.reactive()
n’est pas lu tant que le reactive en question n’est pas appelé.data()
.eventReactive()
Et si on ne souhaitait PAS que notre objet soit réactif à tous les filtres ?
Par exemple :
Par exemple :
À chaque fois que l’utilisateur touche un filtre, ça recalcule pendant plusieurs secondes.
Alors si on doit toucher 5 ou 6 filtres, ou même si on doit cocher 10 cases dans un selectInput
, ça peut vite devenir très pénible !
Dans ce cas, on va préférer ajouter un bouton « Valider » ou « Filtrer », et c’est seulement quand on clique sur ce bouton que le calcul est enclenché.
Exemple avec un bouton :
Voici le code de notre premier exemple mis à jour :
Il y a deux changements :
actionButton
dans la partie UI.reactive()
par la fonction eventReactive()
.Vous remarquerez en essayant l’application que le graphique ne se met pas à jour tant que vous ne cliquez pas sur le bouton.
L’idée de eventReactive
est de spécifier les objets qui permettront de déclencher le calcul.
Ainsi, le calcul de data
se déclenchera seulement si on clique sur le bouton ET qu’on a besoin de data
pour l’affichage. Cette 2e condition vient toujours du fait qu’un reactive n’est pas calculé tant qu’on ne l’appelle pas.
Vous noterez que j’ai rajouté un argement ignoreNULL = FALSE
à mon eventReactive()
.
Les arguments de eventReactive
En effet, cette fonction contient deux arguments très utiles par moment :
ignoreNULL
(est TRUE
par défaut) : Permet de ne pas déclencher le calcul si l’input est NULL (ou vaut 0 dans le cas d’un actionButton
). Si j’avais gardé la valeur par défaut, alors le calcul ne se serait pas déclenché (et le graphique pas affiché) tant que je ne clique pas sur le bouton une première fois.ignoreInit
(est FALSE
par défaut) : Permet de ne pas déclencher le calcul lorsque le eventReactive()
est créé.Dans mon cas, je veux que lorsque l’utilisateur arrive sur la page, il voit le graphique directement sans avoir à cliquer. Donc ignoreInit = FALSE
et ignoreNULL = FALSE
.
L’intérêt principal ici est que le graphique ne se recharge pas tant qu’on ne clique pas sur le bouton.
Ça veut dire qu’en tant qu’utilisateur, j’ai tout le temps pour choisir mes filtres (supposons qu’il y en ait plein !) sans avoir une appli qui se rafraîchit à chaque clic.
On reste sur quelque chose de simple.
À retenir :
eventReactive
lorsqu’on souhaite que la variable réactive réagisse à un nombre limité d’entrées réactives, typiquement le clic sur un bouton.reactive
, eventReactive
n’est lu que lorsque la variable est nécessaire.eventReactive
contient deux arguments ignoreNULL
et ignoreInit
à connaître.observe()
La fonction observe()
est très versatile et vous sera utile dès que vous souhaitez faire une opération qui dépend de plusieurs variables réactives.
En effet, la fonction observe()
crée un contexte réactif, ce qui donne l’autorisation d’utiliser des variables réactives, et ensuite vous êtes libre de faire ce que vous voulez :
output$plot <-
à l’intérieur d’un observe
par exemple)Exemple d’enregistrement d’une information en base :
Prenons un exemple sur le premier cas. Je souhaite enregistrer toutes les occurrences de clic sur le bouton « Submit ».
Voici le code de ma nouvelle application :
J’ai seulement rajouté un bloc observe()
dans la partie server.
Ce bloc peut être vu comme une sortie réactive.
Il va être réexécuté à chaque fois qu’une des entrées réactives qu’il utilise sera modifiée.
Dans notre cas : À chaque fois que l’utilisateur clique sur le bouton.
Ici j’enregistre l’information dans un fichier CSV, mais ce serait pareil pour une base de données.
A contrario de la fonction reactive()
, le code à l’intérieur d’une fonction observe()
est lu dès le démarrage de la session.
Si le code à l’intérieur du observe()
est long à exécuter, mais pas nécessaire immédiatement, on ralentit l’application au démarrage pour rien.
Pour rien ?
Pas forcément en fait.
Parfois, on va préférer un temps de chargement plus long au démarrage pour favoriser une navigation plus fluide par la suite.
Ça dépend beaucoup de l’application en question et des attentes des utilisateurs.
Petite subtilité dans notre exemple : on fait un test sur input$submit
en testant pour qu’il soit plus grand que 0, afin d’éviter d’enregistrer l’information tant que le bouton n’a pas été cliqué.
Mais qu’est-ce qui se passe si l’input n’existe pas encore ? Là ça marche, mais peut-être qu’on a juste du bol.
On verra comment rendre l’application plus robuste avec la fonction req()
ou avec observeEvent
.
Pour l’instant ça tourne bien dans notre petite application.
À retenir :
observe()
est utilisée pour faire des opérations utilisant des objets réactifs.observe()
est lu dès le démarrage de la session.observeEvent()
observeEvent
est à observe
ce que eventReactive
est à reactive
.
L’idée est la même.
Au lieu d’être réactif à TOUS les inputs à l’intérieur du bloc de code, on spécifie une seule entrée réactive.
Exemple d’enregistrement d’une information en base :
En fait, le code que j’ai présenté dans la section précédente serait plus adapté avec un observeEvent
puisqu’il permettrait de se passer de la condition if
:
Même plus besoin d’avoir la condition if
, puisque par défaut la fonction observeEvent
vient avec l’argument ignoreNULL = TRUE
.
Tant que le bouton n’est pas cliqué, le code n’est pas lu.
À l’instar de eventReactive
, il existe aussi un argument ignoreInit
qui est FALSE
par défaut.
Comme pour observe
, le code de observeEvent
est lu dès le lancement de la session. Mais comme en général la variable réactive qui permet de déclencher le code est NULL
au lancement, ça ne pose pas de problème pour la performance.
Vous remarquerez qu’on a codé deux fois la même application, mais une fois avec observe
, et une fois avec observeEvent
.
Ça montre qu’il n’y a pas qu’une seule manière de coder vos applis. Néanmoins, dans la plupart des situations, il y a une manière qui se démarque des autres en étant plus adaptée ou plus performante.
À retenir :
observeEvent
lorsqu’on souhaite que le morceau de code ne réagisse qu’à un nombre limité d’entrées réactives, typiquement le clic sur un bouton.observeEvent()
est lu dès le démarrage de la session, sauf si ignoreInit
vaut TRUE
.observeEvent()
contient deux arguments ignoreNULL
et ignoreInit
à connaître.reactiveValues()
Quoi ? Encore une fonction pour créer une variable réactive ?!
Bah oui.
reactive()
c’est très bien, mais ça ne crée pas tout à fait une variable réactive.
En effet, on ne peut pas modifier data()
une fois qu’on l’a créé. Plus exactement, la seule manière de modifier data()
consiste à refaire le calcul à l’intérieur du reactive()
.
Et si je veux :
Comment on fait ?
La problématique classique, c’est quand le calcul dans le reactive()
est long.
Quand ça prend au moins quelques secondes.
Typiquement une requête SQL.
Exemple avec plusieurs variables reactive()
:
La première approche, c’est déjà d’utiliser plusieurs fois la fonction reactive()
.
Par exemple, une variable reactive()
pour taper dans la base, et une pour appliquer les filtres.
Prenez ce code :
Dans cette application, j’ai rajouté un temps d’attente de 5 secondes au chargement du jeu de données pour simuler une requête SQL.
Donc quand vous chargez l’appli, ça prend 5 secondes avant que le graphique ne s’affiche.
Dans ce cas, si on n’avait qu’un seul reactive()
, il faudrait refaire systématiquement la requête de 5 secondes à chaque fois qu’on change le filtre.
Mais heureusement, j’ai été plus malin (!), j’ai créé un deuxième reactive()
qui utilise le résultat de la requête et qui applique le filtre. Ainsi, si on change le filtre, pas besoin de refaire la requête.
Bon, ça c’est un cas simple qui consiste à morceler les reactive()
en plusieurs fois pour éviter de refaire des opérations pour rien.
Exemple de modification des données :
L’autre cas d’usage plus complexe, ça va être celui où on veut faire un tout petit changement au jeu de données : On veut changer une valeur ou rajouter une ligne.
L’approche naïve, ce serait d’enregistrer le changement dans la base de données, et de refaire la requête.
Sauf que ça va être long pour rien, ça va utiliser des ressources sur la base, et l’utilisateur va pas comprendre pourquoi c’est lent.
Reprenons l’exemple où on enregistre les clics sur le bouton dans un fichier CSV. Cette fois-ci, je veux afficher le tableau des logs et le mettre à jour automatiquement à chaque clic.
Voici un premier essai avec eventReactive
:
Ça commence à devenir plus compliqué.
D’abord, on a le observeEvent
qui permet d’enregistrer les clics sur le bouton dans le fichier CSV.
Ensuite, on a une variable réactive logs()
qui va lire le fichier CSV. Cette variable va être rafraîchie à chaque fois qu’on clique sur le bouton.
J’ai volontairement rajouté un temps d’attente de 2 secondes pour simuler le fait que le fichier soit lourd ou qu’on fasse une requête lente sur une base de données.
Si vous utilisez cette application, vous allez voir qu’à chaque fois que vous cliquez sur le bouton, le tableau se rafraîchit, sauf que ça prend 2 secondes puisque ça recharge complètement le fichier en entier.
Clairement, on fait des calculs pour rien et on aimerait bien juste rajouter la dernière ligne à notre variable réactive logs()
.
Sauf que.. on ne peut pas modifier un reactive()
.
D’où l’intérêt d’utiliser reactiveValues()
dans ce cas.
Autre problème
Vous aurez peut-être remarqué que le observeEvent
et le reactiveValues
réagissent sur la même entrée réactive input$submit
.
Si le reactiveValues
est calculé avant le observeEvent
, alors le tableau ne sera pas le bon.
L’ordre des blocs de code n’a aucun impact sur qui est calculé en premier.
En fait, on pourrait utiliser l’argument priority
de ces fonctions, mais c’est en général par là que commencent les ennuis et le spaghetti code.
On va voir que utiliser reactiveValues
permet de résoudre ce problème aussi.
Le même exemple avec reactiveValues()
:
Je pense que ce bout de code mérite quelques explications.
Dans un premier temps, j’initialise mon reactiveValues
:
reactiveValues
s’utilise exactement comme si vous déclariez une liste : vous mettez les objets de la liste les uns après les autres séparés par des virgules. Ensuite, les objets sont accessibles en utilisant values$logs
ou values[["logs"]]
.
Dans notre exemple on n’a qu’un seul objet à l’intérieur, mais on pourrait ajouter d’autres variables. Toutes les variables à l’intérieur du reactiveValues
sont indépendantes et vivent leurs vies de leur côté.
Ça veut dire qu’il est inutile de créer plusieurs reactiveValues()
. Le seul intérêt serait pour organiser votre code et réunir des variables similaires ensemble. Personnellement, je préfère avoir une seule liste que j’appelle values
.
Ensuite, on n’est pas obligés de déclarer la valeur tout de suite. Potentiellement, je pourrais écrire values <- reactiveValues()
, et plus tard j’assigne une valeur à values$logs
.
Mais j’aime bien déclarer les valeurs tout de suite, en général en haut de mon fichier, pour des raisons de clarté et de maintenance du code. Ça permet d’avoir la liste explicite des valeurs qui sont contenus dans mon reactiveValues
.
Ensuite, je remplis ma valeur :
Ça c’est un bout de code qui va être lu dès le début (puisqu’il s’agit d’un observe
) et qui sera jamais relu ensuite parce qu’il n’y a pas d’entrée réactive à l’intérieur.
Je le mets simplement pour lire le fichier, s’il existe, au démarrage de la session.
Petite subtilité
Puisque values$logs
est une variable réactive, pourquoi ce bout de code n’est pas réexécuté quand values$logs
change ?
En fait, Shiny va automatiquement faire la distinction selon qu’on assigne une valeur à values$logs
(donc on écrit values$logs <- ...
) ou bien si on utilise values$logs
à l’intérieur d’un calcul.
Dans le premier cas, on ne veut pas redéclencher le calcul du bloc de code, sinon on serait bloqué dans une boucle infinie où on recalcule values$logs
à chaque fois.
Dans le deuxième cas, le calcul sera effectivement redéclenché comme pour une variable réactive classique. Dans notre exemple, on retrouve ce cas dans le bloc de output$logs_table
Là, de nouveau, je pourrais faire autrement, par exemple on peut le mettre directement dans le reactiveValues
. Ça donnerait :
Là on est vraiment sur du niveau de détails. En terme de performance, ça ne va avoir aucun impact. C’est purement du goût personnel et une manière d’organiser le code.
Moi j’aime bien initialiser mes variables dans le reactiveValues
avec des NULL
, et je les remplis ensuite dans le reste de l’application.
Finalement, le troisième bloc de code :
Dans ce bloc de code, on a réuni les deux étapes qu’on faisait avant dans un bloc observeEvent
puis dans un bloc eventReactive
.
Le problème de compétitivité pour savoir qui sera calculé le premier entre observeEvent
et eventReactive
ne se pose plus du tout puisqu’on fait les deux étapes dans le même bloc, à la suite.
C’est un gros avantage d’utiliser reactiveValues
, puisqu’on peut tout mettre dans un seul bloc observe
(ou observeEvent
).
Finalement, on modifie directement le jeu de données. On fait un rbind
pour rajouter la nouvelle ligne.
Pas besoin de repasser par la case lecture du fichier CSV.
Résultat : L’application est beaucoup plus fluide.
À retenir :
reactiveValues
quand on souhaite modifier le jeu de données par la suite et que refaire le calcul d’origine n’est soit pas possible, ou pas souhaitable pour des raisons de performance.reactiveValues
s’initialise comme une liste. C’est une liste de valeurs réactives indépendantes les unes des autres qu’on va pouvoir utiliser et manipuler tout au long de l’application.values$logs<-
ne crée pas de réactivité. C’est seulement quand on va utiliser la variable dans un calcul qu’elle va être réactive.isolate()
Bon.
On a vu le plus dur.
La fonction isolate()
est très simple : Elle permet de supprimer temporairement la nature réactive d’une entrée réactive.
Si j’écris isolate(input$filtre)
, ça va utiliser la valeur contenue dans cet objet. Si input$filtre
change dans l’application, alors ce bloc de code ne sera pas recalculé parce qu’on a utilisé isolate()
.
J’ai une petite confession à vous faire sur cette fonction.
En écrivant cet article, ça m’a fait énormément réfléchir sur la manière dont j’utilise les outils la réactivité en Shiny.
Je partais avec certaines idées que j’ai remises en question durant l’écriture.
Et là, je suis en train de chercher des exemples avec isolate()
.
Pas juste des exemples théoriques limités pour montrer comment la fonction marche. Vous savez lire la documentation.
On est là pour savoir précisément dans quelle situation vous devez utiliser telle ou telle fonction.
Eh ben là, j’arrive pas à trouver de problèmes où utiliser isolate()
est pertinent.
J’ai parcouru plein d’anciens projets de code, et à chaque fois j’utilise isolate()
pour de mauvaises raisons. Parce que j’ai mal codé. Je crée de la dette technique.
Typiquement, j’utilise un reactiveValues()
au lieu d’un reactive()
. Ça complexifie mon code sans raison et je me retrouve à devoir utiliser isolate()
.
Conclusion : On utilise isolate()
par facilité.
Parce que ça peut permettre de trouver une solution rapide et simple à un problème de réactivité.
Et en soi, ce n’est pas une mauvaise raison. Mais il faut garder à l’esprit qu’on crée une dette qu’on devra payer plus tard.
Avec du code convolué.
Donc utilisez cette fonction avec parcimonie. Si vous voyez que vous l’utilisez à tout va, posez-vous la question s’il n’y a pas un meilleur moyen. Par meilleur moyen, j’entends :
Si à un moment je trouve une utilisation particulièrement pertinente à cette fonction, je reviendrai sur cette section. Je suis aussi ouvert à vos suggestions dans les commentaires !
req()
Si la fonction is.null()
est votre fonction la plus utilisée dans une appli Shiny, alors vous allez aimer cette section.
req()
, pour require, permet de ne pas poursuivre la lecture du code tant que la variable à l’intérieur de req()
ne vaut pas une valeur autre que :
NULL
FALSE
""
(chaîne de caractère vide)0
dans l’unique cas où il s’agit d’un input
se rapportant à un actionButton
Même si 90% du temps vous allez l’utiliser pour le cas spécifique où la variable vaut NULL
, il ne faut pas oublier les autres cas.
Parfois la valeur FALSE
est une valeur légitime. Dans ce cas, vous n’échapperez pas au is.null()
.
Je vous propose de prendre une version légèrement modifiée du premier exemple que nous avons étudié :
La seule différence, c’est qu’au lieu de spécifier directement le selectInput
dans la partie UI, j’utilise un uiOutput
et je définis mon filtre dans la partie serveur.
C’est assez commun de procéder ainsi dès que les paramètres du filtre en question dépendent de variables qui n’existent que du côté serveur.
Ici c’est pas vraiment justifié, mais ça suffira pour montrer ce que je veux montrer.
Si on n’est pas trop attentif quand on démarre l’application, on ne voit pas trop de différence par rapport à la première appli.
Rafraîchissez la page plusieurs fois.
Vous allez voir qu’au chargement initial, un message d’erreur apparaît, mais qu’il est très rapidement remplacé par le graphique.
La raison ?
Quand Shiny va lire ce morceau de code :
L’entrée input$species
n’existe pas encore.
Elle est créé une fraction de seconde plus tard. Ce qui redéclenche le calcul des données et finit par afficher le graphique.
Mais ça explique le clignotement furtif de l’erreur.
Dans ce cas, c’est seulement un clignotement de message d’erreur.
C’est la honte mais ça passe encore.
Dans d’autres situations, ça va juste faire planter l’appli.
Le réflexe du noob, c’est de rajouter un test :
Eh bien non, Shiny vous donne une fonction bien plus pratique :
En utilisant req()
, Shiny va vérifier si input$species
est une valeur égale à NULL
.
Si oui, alors il va couper court à tout calcul et s’arrêter là. Il reviendra quand il aura une valeur.
Je vous conseille d’user et d’abuser de req()
, ça va rendre vos applications plus robustes.
Dans ce petit exemple, on devrait aussi le mettre dans les appels à data()
:
À retenir
req()
permet de rendre votre code plus robuste. Utilisez le à volonté !req()
ne teste pas que la valeur NULL
, mais aussi FALSE
, ""
, etc. Gardez ça en tête.On en a à présent terminé avec les fonctions de réactivité !
Dans ce chapitre, j’ai envie de vous montrer les quelques astuces que j’utilise ici et là et qui me facilitent pas mal la vie.
Ce ne sont pas des secrets de développeurs, mais plutôt des trucs où on peut facilement passer à côté, voire même des astuces un peu sales.
Sys.time()
On commence d’ailleurs par une astuce sale.
Franchement, quand je fais ça, je me dis que je crée de la dette technique.
Mais vraiment c’est bien pratique.
Je donne un exemple plus bas dans la section sur les filtres interdépendants.
L’idée, c’est de forcer la réactivité.
Vous avez une variable réactive et vous voulez garantir sa modification.
La fonction Sys.time()
permet de donner une valeur que vous n’avez encore jamais donnée auparavant.
Bah oui parce que avant c’était le passé.
On n’a jamais été maintenant avant maintenant, si vous préférez.
Je l’utilise en général de deux manières différentes :
df$update <- Sys.time()
.bindCache
Ce n’est pas tout à fait une astuce, mais c’est nouveau, donc peut-être que vous ne connaissez pas encore.
Cette fonction existe depuis shiny 1.6 et permet de retenir le résultat d’un reactive()
dans le cache.
Elle va être particulièrement utile si les conditions suivantes sont réunies :
On va enregistrer le résultat d’un calcul.
Reprenons l’exemple de base mais en simulant un calcul long pour le reactive()
:
Si l’appli n’a jamais été lancée, alors elle va prendre 5 secondes pour calculer data()
.
Pour être plus précis, on devrait dire : « Si le processus R n’est pas encore créé ».
Idem à chaque changement de filtre.
Mais dès que data()
aura été calculée pour un filtre, alors le calcul n’aura jamais lieu de nouveau puisqu’il aura été enregistré !
À retenir
bindCache()
permet d’enregistrer des valeurs réactives en cache pour améliorer la performance de votre application.bindCache()
dans votre contexte. Il ne faut surtout pas l’ajouter automatiquement à tous vos reactive()
!observeEvent
Bon.
Je vais peut-être passer pour un débile, mais j’ai mis longtemps à découvrir cette “astuce”.
Comme on écrit tout le temps :
Je pensais qu’on ne pouvait mettre qu’une seule variable pour déclencher le calcul du observeEvent
!
Mais pas du tout !
Vous pouvez très bien écrire :
Ou bien (c’est différent) :
Ainsi, votre observeEvent
sera calculé à chaque fois qu’une des deux variables sera modifiée !
Attention toutefois : On a deux manières d’écrire qui ont deux comportements différents.
Vous savez que par défaut, ignoreNULL = TRUE
, pour la fonction observeEvent
.
Dans la première situation, si une des deux variables vaut NULL
, alors le code ne sera pas lu.
Il faudra que les deux variables soient différentes de NULL
pour déclencher le calcul.
Dans le second cas, on n’a pas ce comportement puisque :
En effet, un vecteur qui contient une valeur NULL
n’est pas NULL
. La valeur NULL
est juste virée.
Donc si une des deux variables vaut NULL
, le code peut tout de même être lu.
Voilà.
Deux situations, deux conséquences.
À vous de voir ce qui vous va le mieux.
Et bien sûr, c’est aussi valable pour eventReactive
.
shinydashboard
Dans les sections suivantes, je vais répertorier les cas d’usage les plus courants.
Comme ça, la prochaine fois que vous vous retrouvez dans cette situation, vous pourrez revenir ici et trouver des solutions.
Je souhaite aussi compléter ce chapitre au fil du temps lorsque je rencontre de nouvelles situations.
D’ailleurs, si vous avez un problème ou des questions sur la réactivité, n’hésitez pas à laisser un commentaire en bas de la page !
Je serais ravi de vous donner un coup de main, et potentiellement de compléter le guide avec votre exemple !
On commence par un exemple simple et classique : shinydashboard
.
Typiquement, vous avez des onglets dans la barre de menu à gauche.
Et chaque onglet va vous présenter un ensemble de tableaux, graphiques, cartes, et autres visualisations.
Évidemment, chaque dashboard est unique, et à l’intérieur d’une page, on peut trouver moults complications.
Ces complications sont traitées dans les sections suivantes (ou alors, expliquez-moi votre situation en commentaire).
L’idée de base d’un dashboard est simple : Tant qu’on n’entre pas sur une page, inutile de charger les données de cette page.
La plupart des utilisateurs ne vont même pas visiter tous les menus. Ils vont aller directement à celui qui les intéresse, et c’est tout.
Voici un squelette de code simple :
Essayez l’application par vous-même.
Elle met simplement ce principe fondamental en Shiny : Tant que l’onglet n’est pas cliqué, on ne demande pas l’affichage des sorties.
En corollaire, on ne demande pas le chargement des données.
On le remarque bien parce que j’ai mis des délais de 5 secondes sur chaque menu.
On attend 5 secondes au démarrage. Et tant qu’on ne clique pas sur le 2e menu, on n’a pas les 5 secondes de chargement supplémentaires.
C’est aussi valable avec les tabsetPanel
d’ailleurs.
Le problème dans Shiny, c’est qu’on n’a jamais du code qui est aussi simple.
Je vous vois arriver tout de suite. « Mais oui mais moi c’est pas pareil à cause que… ».
Non.
Y’a pas de à cause que.
Le code va évidemment être plus compliqué.
Vous allez avoir plusieurs jeux de données, plein de sorties, peut-être même des onglets dans la page, etc.
On pense souvent à l’envers quand on fait du Shiny. On veut que Shiny s’adapte à notre logique de pensée. Non. Adaptez votre logique de pensée à Shiny.
Le principe de base : Tant que la page n’est pas affichée, on ne charge pas les données.
Et pour ça, votre meilleur ami est la fonction reactive()
.
Créez une fonction reactive()
par page. Ou même créez-en plusieurs.
Mais n’utilisez pas la même fonction reactive()
qui contient plusieurs objets pour plusieurs pages.
En bonus : Si votre jeu de données est souvent le même, utilisez bindCache
.
Un cas d’usage super classique, mais néanmoins particulièrement complexe, va être dans le cas de filtres interdépendants.
Par filtres interdépendants, j’entends le fait que sélectionner un premier filtre va influencer les choix des autres filtres.
Dans cette section, j’utilise des exemples volontairement simplistes pour qu’on puisse y comprendre quelque chose.
Même si votre application est complexe, essayez de vous ramener à un raisonnement simple.
Qui dépend de quoi ?
Autre chose : J’utilise le terme filtre parce que c’est ce qui est le plus courant. Mais en fait, je fais référence à n’importe quelle entrée réactive. Par exemple, ça peut être un jeu de données réactif.
Si vous pensez que votre situation est différente, laissez-moi un petit commentaire et je jetterai un coup d’œil (Sauf si votre fichier fait 5000 lignes. Essayez de créer un exemple reproductible).
On va démarrer avec une question Stackoverflow :
Shiny Interdependent Filters values.
En fait la question ne nous intéresse pas, puisque personne n’a mentionné le problème sous-jacent que nous allons discuter.
D’abord, voyons le code, que j’ai légèrement retouché (pour le simplifier et supprimer les dépendances) :
J’ai fait un autre changement : J’ai rajouté un délai de 2 secondes au chargement du tableau.
Toujours pour la même raison : on imagine que ce soit un tableau long à calculer.
Bon.
Essayez d’utiliser l’application et changez juste un filtre.
Vous allez voir le tableau qui va se recharger trois fois d’affilée :
Super la performance ! Il va être content l’utilisateur.
Pourquoi ça fait ça ?
output$tableprint
. Ça c’est pour le premier chargement.observe
, qui va modifier input$filter2
. Et comme input$filter2
est modifié, ça redéclenche output$tableprint
.observe
est lui aussi recalculé (comme input$filter2
a changé), donc rebelotte, on recalcule le tableau.Comme je l’ai déjà dit, il y a des tonnes de manière de faire la même chose en Shiny. Mais il y a certaines manières qui sont plus performantes que d’autres.
Quand les données sont légères et qu’il n’y a qu’un seul utilisateur, ça va.
Mais gardez en tête que même un ralentissement de 100 millisecondes peut vous poser des problèmes.
Les ralentissement sont amplifiés par la taille des données, le nombre d’utilisateurs concurrents, et les répétitions des calculs.
Notre exemple est simpliste, mais il y a en fait plein de situations différentes, qui ont chacune une solution la plus adaptée :
observeEvent(input$filtre_3)
puisque tout changement de filtre va éventuellement changer Filtre 3 et on en est sûr.C’est le plus simple. On vire les observe
:
Rien de super spécial ici.
Ça marche comme avant, sauf que le tableau ne se recharge pas 3 fois de suite.
Et, évidemment, les filtres ne sont plus interdépendants.
Par contre, vous remarquerez que si on change plein de valeurs d’affilées, ça recharge à chaque fois.
C’est là que le bouton trouve son utilité.
Ici je rajoute un eventReactive
qui permet de mettre à jour les données seulement quand on clique sur le bouton.
Le reste du temps, il n’y a que les filtres qui se mettent à jour de manière interdépendantes.
Évidemment, on peut aussi combiner les deux solutions : Ajouter un bouton et supprimer l’interdépendance.
De manière générale j’aime bien la solution du bouton dès que l’application utilise des volumes importants de données et de nombreux filtres.
Ça simplifie énormément les choses.
Et côté utilisateur, ça se passe bien aussi.
Vous noterez que j’ai aussi séparé le calcul des données et le rendu du tableau.
J’aime bien que chaque chose soit à sa place. On ne mélange pas la manipulation de données et le calcul des sorties.
Si toutefois vous tenez à tout mélanger, alors il faut faire attention et bien faire la distinction entre les deux blocs de code suivants :
et
Même s’ils se ressemblent beaucoup, ces deux blocs sont fondamentalement différents, et vous allez trouver un comportement très différent sur l’appli.
Dans le premier cas, on crée une sortie réactive qui dépend des entrées réactives des filtres. Ça veut dire que le tableau va se mettre à jour quand on change les filtres !
Ce qui n’est pas du tout ce qu’on pensait faire.
Le bouton submit
permet seulement de recréer l’assignation de la sortie réactive, mais il n’apporte rien de spécial.
Dans le deuxième cas, la sortie réactive output$tableprint
ne dépend plus du tout des filtres. Il utilise un data.frame qui est recalculé seulement quand on clique sur le bouton.
Je vous invite à essayer par vous-même et faire vos propres tests.
Cette fois-ci, on va utiliser une particularité de notre exemple :
Si la 2e condition n’est pas respectée, on va voir comment faire en Solution 4.
Donc si on change n’importe quel filtre, Filtre 3 va forcément être modifié à la fin des haricots.
Ça peut paraître un cas particulier qui n’arrive jamais, mais en fait si, ça arrive régulièrement, surtout quand on souhaite avoir des filtres qui dépendent les uns des autres.
Typiquement, Filtre 1 correspond au pays, Filtre 2 correspond à la région, et Filtre 3 correspond à la ville.
On peut même imaginer dans cette situation que tant que Filtre 1 n’est pas renseigné, alors Filtre 2 ne propose aucun choix.
Dans ce cas, c’est facile, il suffit juste d’utiliser Filtre 3 comme s’il s’agissait d’un bouton.
Ce code ressemble quasiment à celui de l’exemple précédent, mais avec input$filtre3
à la place du bouton, et on a complètement supprimé le bouton.
Dans ce contexte particulier, il s’agit de la meilleure solution.
C’est suffisamment fluide pour l’utilisateur et on ne recharge pas 36 fois le tableau pour rien.
Pour illustrer ce cas, on va devoir choisir un autre exemple.
Un exemple où :
Voici l’exemple :
On utilise un jeu de données qui contient des indicateurs tels que le PIB, les émissions de CO2 ou le taux de natalité de plein de pays pour plein d’années différentes.
On sélectionne l’indicateur, le pays, l’année, et on obtient la valeur de l’indicateur.
Problème : Les données ne sont pas disponibles pour tous les pays pour toutes les années. Il y a pas mal de données manquantes.
On évite à l’utilisateur de choisir une combinaison qui n’existe pas en s’assurant de proposer une liste des années pour laquelle il y aura forcément des données.
Par exemple : Les émissions de CO2 pour l’Afghanistan ne sont disponibles qu’entre 1960 et 2011. Le PIB est lui disponible jusqu’en 2014.
Le terme interdépendance est peut-être trop fort dans ce cas, mais on a bien un filtre (year
) qui dépend des autres filtres (indicator
et country
).
Si vous jouez un peu avec l’application, vous n’allez peut-être pas vous rendre compte du problème immédiatement (parce qu’il y a peu de données manquantes).
Mais il existe bel et bien :
On s’en aperçoit quand on change d’indicateur et que l’année qui était en sélection n’est plus disponible.
La logique est la suivante :
observeEvent
provoque la demande de mise à jour de input$year
data()
est mis à jour.output$text
est mis à jour. On voit le NA apparaître.input$year
a été mis à jour !data()
, puis output$text
sont re-calculés.D’où le double-chargement. Et le NA qui apparaît. Ou l’erreur, selon le contexte. Voire le crash dans certains cas.
Bon, bah c’est simple, pour éviter le double-chargement, on fait comme avant : On conditionne uniquement sur input$year
vu qu’il va forcément changer si on change les autres filtres !
Hm ?
Ou pas.
Si on prend l’année 1960 par exemple, qui est disponible pour le PIB et les émissions de CO2, la valeur n’est pas mise à jour.
C’est l’objet de cette section : Il n’y a pas de garantie que la valeur du filtre va changer.
Donc si on retire la réactivité sur data()
, la sortie ne sera pas du tout mise à jour.
Ce cas est compliqué parce qu’on a besoin de la réactivité sur data()
ET input$year
mais ces deux entrées réactives ne changent pas simultanément.
Il y a un léger délai entre les deux, ce qui provoque ce double chargement.
C’est pénible à la fin !
Comment ils font dans les autres langages ? (En vrai je sais pas, je suis preneur de vos retours)
Et puis franchement, dans ce cas, est-ce qu’on a envie de rajouter un bouton ? Ou de laisser l’utilisateur deviner quelles sont les combinaisons qui sont manquantes en supprimant l’interdépendance ?
On pourrait.. mais si on peut éviter c’est mieux.
Le problème est dans le fait que le changement des deux entrées n’est pas simultané.
On va donc le rendre simultané en utilisant une petite astuce avec reactiveValues
:
J’ai rajouté une variable réactive values$year
qui va en gros prendre le rôle de input$year
.
Au début :
Ce morceau de code me permet en gros de remplacer input$year
par values$year
. Si je m’arrêtais là, ça ne servirait à rien de spécial, mais l’appli marcherait toujours.
La grosse différence se situe ici :
Il y a toujours le updateSelectInput
qui va me permettre de mettre à jour l’interface.
Le changement, c’est surtout que je peux modifier values$year
immédiatement !
Problème de simultanéité ou de délai ? Réglé !
Il ne reste plus qu’à changer tous les input$year
en values$year
et le tour est joué.
Petite subtilité
Vous vous demandez peut-être pourquoi j'ai ajouté as.character()
.
Il faut absolument que values$year
soit identique à la future valeur de input$year
. Sinon, Shiny va détecter un changement et recharger la sortie.
Or, dans ce cas, input$year
est en character
par défaut. Je dois donc m'assurer que values$year
le soit aussi.
Si on ne souhaite pas supprimer l’interdépendance, ni ajouter un bouton, et qu’on n’est pas dans le cas de filtres interdépendants emboîtés, comment on fait ?
Bon.
Là c’est plus compliqué.
Mais j’ai pensé très fort et j’ai trouvé quelques solutions.
De manière générale, ça fait appel à deux principes :
Voici ma proposition :
Laissez-moi vous guider dans le code.
Dans un premier temps, j’initialise des valeurs réactives :
Les trois premières variables vont me permettre d’enregistrer la liste des choix des filtres.
En confrontant la liste des choix présente avec la liste des choix future, je vais pouvoir déterminer si un filtre est prêt ou non.
En gros, un filtre est prêt si la liste des choix présente est égale à la liste des choix future.
La dernière variable est un intermédiaire permettant de déclencher le calcul des données une fois que tous les filtres sont prêts.
Le gros du travail se trouve dans le deuxième bloc :
Ce bloc va être relu à chaque fois que les filtres sont modifiés.
Le problème qu’on avait avant, c’était que les données étaient calculés immédiatement après qu’un filtre change.
Ici c’est différent.
On va parcourir les filtres un par un.
Si le filtre est « prêt », donc si sa liste de choix a déjà été mise à jour, alors on continue.
Sinon, alors on met à jour sa liste de choix, puis on recommence de zéro.
Pour « recommencer de zéro », j’utilise return(NULL)
. Ça sert à arrêter la lecture du code. Comme je viens de modifier un input
, le bloc observe()
va être redéclencher.
Et ainsi de suite, jusqu’à ce que tous les filtres soient prêts.
À ce moment-là, je change la valeur de values$filter_ready
en utilisant l’astuce avec Sys.time()
.
Le calcul des données est alors déclenché, puis la mise à jour du tableau.
On pourrait aussi se passer de la variable intermédiaire et tout mettre dans le observe()
, mais on y perd en organisation du code.
C’est pas mal, non ?
Ça marche, mais c’est pas parfait.
Le plus gros reproche qu’on pourrait faire, c’est que si la liste des choix des filtres est longue à calculer, alors le recalcul systématique va être peu performant.
Dans ce dernier cas, on peut être plus malin.
Par exemple avec des variables intermédiaires qui enregistrent le fait qu’un filtre est « prêt », et on invalide ces variables après la mise à jour du tableau.
On peut aussi essayer de mettre à jour simultanément tous les filtres d’un coup, au lieu de le faire de manière séquentielle.
Mais on crée alors un autre problème : Il y a un délai entre l’appel à updateSelectInput
et la mise à jour de l’input
correspondant.
J’ai trouvé que c’était déjà suffisamment compliqué sans trop en rajouter.
Personnellement, je pense qu’il vaut mieux privilégier une des trois premières solutions.
Même si cette dernière solution est supposée être « sans compromis », en fait on en fait un qui est caché : On complexifie le code et on y perd en temps de maintenance.
À vous de voir.
]]>r-base : Depends: r-recommended (= 4.0.0-1~bustercran.0) but it is not going to be installed
Woah!
R 4.0.0 is finally out!
That might sound super geeky, but that’s the first time I feel so excited for a major version of whatever technology.
I was still a kid when R 3.0.0 got out.
But nowadays, I do R every day. I eat R. I sleep R. I am R.
(Ok I’ll stop.)
This article is not here to tell you what’s new in R 4.0.0.
You can refer to this article instead:
Or the official message:
In this article, I want to show you how to upgrade R on Debian.
Because I struggled.
And I don’t want anyone else to struggle too.
Pretty much all my servers use Debian. It’s stable. It just works.
But when you install R on a fresh Debian, you get R 3.5.2.
Who would want to use R 3.5.2 in 2020?
So, to install the latest version, you must:
And in this article, I’ll suppose you have already done that.
That means you’re probably currently using R 3.6.3 and you want to upgrade.
But, like me, you got trouble.
If you open /etc/apt/sources.list
on your server, you should see this line:
On this line, we can see two things:
buster
, which is Debian 10.cran35
gives me access to the latest version of R 3.x.x.So if you want to update R, you simple update the repo address, then apt update
, and finally apt upgrade
.
Not this time.
But let’s try!
The new address is:
If I replace it in my /etc/apt/sources.list
file, and then I do:
A few libraries will be updated, but not R. Instead, we will get told this:
Why?
Because of conflicts in the dependencies.
You can try to force the install though:
Except it doesn’t work either.
You’ll get this message:
Not cool.
So what do we do?
In fact, everything is explained on the CRAN website.
Here: Installation instructions.
First, they give you the new repo address. That’s good. We’ve already done that.
And then they say:
Please refer to the section on bullseye above for hints on how to do the reinstallation of extension packages. Of course you need to substitute “bullseye” for “buster” if this is the distribution you are working with.
Oh.
Right.
Instead of writing sudo apt install r-base
, let’s try:
These commands uninstall R first. And then install it again with this new parameter.
And it works!
Happy R ;)
]]>r-base : Depends: r-recommended (= 4.0.0-1~bustercran.0) but it is not going to be installed
Woah.
R 4.0.0 est sorti !
Je vais peut-être passer pour un gros geek mais c’est la première fois que je ressens autant d’excitation pour un passage de version majeure d’une quelconque technologie !
J’étais trop petit pour R 3.0.0, mais aujourd’hui R est au cœur de mon activité, j’en mange au petit-déjeuner et j’en rêve même la nuit.
(Bon ok j’exagère peut-être un poil..)
Cet article n’a pas vocation à expliquer ce qui change dans R 4.0.0.
Si ça vous intéresse, vous pouvez vous référer à cet article (en anglais) :
Et aussi à l’annonce officiel de la part de la Core Team (en anglais aussi) :
Dans ce court article, j’aimerais juste vous montrer comment installer R sur Debian.
Parce que j’ai galéré.
Et si je peux éviter à d’autres personnes de galérer, eh bien tant mieux !
Quasiment tous mes serveurs tournent sous Debian. C’est stable. Ça juste marche, comme disent les sys admin.
Si vous installez R sous une Debian toute fraîche, vous allez avoir une vieille version : la 3.5.2. D’ailleurs c’est aussi vrai pour Ubuntu.
Pas glop.
Pour installer une version plus récente, il faut :
Et je vais supposer que vous avez déjà fait ça.
C’est-à-dire que quand vous ouvrez /etc/apt/sources.list
sur votre serveur, vous avez la ligne suivante :
Sur cette ligne on peut voir que j’utilise buster
, c’est-à-dire Debian 10.
Et le cran35
indique que je vais accéder aux repos pour les dernières versions de 3.x.x (c’est-à-dire la 3.6.3).
D’ordinaire, pour mettre à jour un programme, on met à jour l’adresse du repo, puis on apt update
et enfin on apt upgrade
.
Pas cette fois.
Mais essayons.
L’adresse du nouveau repo, c’est-à-dire pour 4.0.0, est la suivante :
Si je la remplace dans mon fichier /etc/apt/sources.list
, et qu’ensuite je fais :
Quelques librairies vont être mises à jour. Mais pour R en tant quel tel, rien ne va se passer. À la place, on va nous dire :
Qu’est-ce que ça veut dire exactement ?
En gros c’est une histoire de dépendances et de conflits non résolus.
Si on essaie de forcer la chose, en tapant :
On obtient le résultat suivant :
Pas cool.
Alors quelle est la solution ?
En fait c’est tout expliqué si vous allez sur le site de CRAN, mais il faut un peu fouiller.
Je fais référence à ces instructions d’installation.
On nous explique qu’on a bien fait de changer l’adresse du repo, ça OK.
Et ensuite on nous dit:
Please refer to the section on bullseye above for hints on how to do the reinstallation of extension packages. Of course you need to substitute “bullseye” for “buster” if this is the distribution you are working with.
Et c’est là tout le détail !
À la place d’écrire sudo apt install r-base
, il faut écrire :
D’abord on désinstalle R, et ensuite on le réinstalle avec ce paramètre supplémentaire.
Et là, ça marche !
Voili voiloù.
Bon R !
]]>Rather basic for a Shiny app.
Having a background in statistics, I learned over the past few years how to create cool dashboards to make my clients happy!
Except… when they asked me to deploy it on his server.
That’s when things got complicated.
Writing R code or creating Shiny dashboards has become easy to me.
But hosting an app?
Well.. that’s not my job.
The first time I faced the issue, the team I worked with decided on a completely different solution.
We had no idea how to set up a server, nor the skills to do it anyway, so… we had to find another way.
It’s not pretty.
Be prepared.
…
We installed R and the Shiny app on each user’s computer.
Yep.
That’s what we did.
Every time we wanted to give access to a new user, we had to go to his desk, install R on his machine, the libraries, the data, create a shortcut on his desktop…
When you think about it, we were quite creative!
But also incredibly stupid…
But we had no choice.
No skills, and no help from the IT department.
Today, I know there exists a wayyy better solution.
It’s easy:
That’s all.
.
Ok, ok… I may be oversimplifying it.
It’s not that easy the first time.
But once you know how to do it, you can make it work in half an hour.
Really.
It is that simple.
And today, with this guide, my goal is to teach you exactly how to host a Shiny app on an AWS server.
So, where to start?
First, we’ll use AWS.
AWS is for Amazon Web Services, i.e. all the services from Amazon in the cloud.
Of course, you could use other services, such as Azure from Microsoft, Digital Ocean, Rackspace, etc.
Doesn’t matter.
But AWS is the most popular tool.
It’s a handy skill to have.
Plus: When you create a new account on AWS, you have free access to some services for a whole year.
Hence, everything we’ll do in this guide can be done for free.
The benefits of this smart method are:
My goal is to write the ultimate guide on deploying a Shiny app on a server. Whenever you ask yourself How can I host a Shiny app? you can come back here and find the answer.
Until you remember exactly how to do it.
But I know from experience it’s easy to forget. So don’t hesitate to come back when you need.
I will explain each step with plenty of details, screenshots, drawings, maybe even videos!
Here are the steps:
In Part 1, we’ll get a basic Shiny app. It’s not exactly the goal of this guide to learn how to build a Shiny app, so we’ll get an already existing one.
In Part 2, I will show you how to create a new server in AWS. That’s the only part that is specific to AWS, but really you could use any other provider. I’m not even using AWS anymore nowadays.
In Part 3, you will learn how to install R and R Shiny on your brand new server. After you’re done with this chapter, you will have a working server and even already a Shiny app available on the internet.
In Part 4, we’ll deploy our app on the server so that it becomes available to all.
I could have stopped here. These steps are enough for a basic deployment. Except you will probably still have a few questions. That’s why I added more:
In Part 5, you will get a domain name. Yup, so far you had to type an IP address to access the app.
In Part 6, things are getting a bit more complicated. We’ll install nginx, generate TLS certificates, and secure you app with HTTPS. You didn’t understand any of the words of the previous sentence? No problem, every step will be detailed.
In Part 7, we will finally learn how to protect the app with a password. Not everyone want to have their app open to the world. I will show you different possibilities for different needs.
And then you’ll be good to go!
Let’s get started!
It’s not exactly the goal of this guide to teach you the Shiny framework.
So instead of spending time on that, let’s use an an app that already exists.
I chose the movie explorer from the Shiny gallery:
And I created a Gitlab repo that you can clone it easily: https://gitlab.datachamp.fr/charles/051-movie-explorer
Note that you’ll need git and a console for this tutorial. You can download git here: https://git-scm.com
Uh.. that’s it? We’re done already?
Yup! Enjoy as long as it’s easy.
In this chapter, we are going to create an AWS server to host our app.
The first step is to create an account on AWS.
Visit https://aws.amazon.com and create an account.
The steps are easy to follow so I trust you can do it by yourself!
Note that Amazon will ask for a payment method since for every paid service you use, you’ll need to pay for it.
But don’t worry.
When you open a new account, some services are free so that you can try them without burning any cash!
In fact, everything we’ll do in this tutorial can be achieved for free.
Once your account is created, log in and you’ll land on a page with all the available services:
In the search box, type EC2.
EC2 means Elastic Compute Cloud.
It’s basically a service in the cloud that is used for computation. Plus, it’s elastic, meaning you can stretch its power as much as you want. It’s flexible.
You can use it to:
S3 is better to store data, but you’ll still need to put a bit of data on your EC2 machine, like your R scripts!
The huge advantage of this service is that you can change the configuration at any time.
Today we won’t need a powerful server for our Shiny app.
But if tomorrow you want to run a heavy script on multiple cores, that’s not a problem. Simply upgrade the machine, run the script, and downgrade the machine again once you don’t need it anymore.
That’s it.
You’ll pay the powerful machine only for the couple of hours you used it.
Super convenient.
But anyway.
We’re not here for that.
Let’s create your first server!
In the search box, type EC2 and enter the service.
To the left, in the menu, click on Instances. An instance is a server.
And then, click on Launch Instance.
An AMI, for Amazon Machine Image, is a pre-packaged server with plenty of useful tools aleardy installed.
Some AMIs are prepared by Amazon.
You can create your own.
And you can even rent some to third-party companies (for example with proprietary software inside).
We’ll use a very basic AMI, the Ubuntu Server 18.04 LTS.
Click on Select.
The instance type corresponds to the power of your machine.
Do you need a small one or a very powerful one?
In this guide, no need for a costly one.
We will use the t2.micro type since it’s the only one that you can use for free when you have a new account.
It contains a CPU with only 1 core, only 1 GB of RAM, and an okay internet connection.
That will be enough for us. But if at some point you want to upgrade and you’re not sure which one to pick, simply visit the Amazon EC2 Instance Types.
Then, click on Next: Configure Instance Details.
We won’t modify anything in the next screen, so directly click on Next: Add Storage.
By default, AWS gives you 8 GB of space.
It’s up to you to decide whether you’ll need more or not.
Note that to stay within the free offer of a new account, you can to keep it below 30 GB.
For this tutorial, you can keep it at 8 GB.
Now, config is done!
You can click on Review and Launch!
A final screen will summarize all the options you picked. Give it a look and validate the launch of your instance by clicking on Launch!
Oh well… Yet another screen. What’s this thing with keys?
It’s simply a way to protect the access to your server.
Since it’s connected to the Internet, you can’t really keep the door open, or anyone will mess up with your server.
Not cool.
Instead, you’ll create a key pair in the form of a file that you’ll keep on your computer.
Every time you want to connect to your instance, you show up this file and you’ll get access.
Important: Do not lose this file. If you do, you will lose access to your server at the same time and you’ll have to restart from scratch!
Pick a name for your key, and download it.
Congrats! You have created your first server!
After a few seconds, your instance will be ready and you’ll see it in the Instances menu.
In the next chapter, we’ll see how to connect to your server with SSH and we’ll install R and R Shiny on it!
Keep reading !
In the previous chapter, we stopped right after the creation of your first server on AWS.
In this new chapter, you’ll learn:
Once your instance is created, AWS will display a Connect button. If you click on it, you get something like that:
These are the instructions to connect to your server.
But it’s not that easy, so let’s take it slowly. Plus, it’s different depending on your OS.
For Windows users
If you’re on Windows, you need to download an SSH client, since the OS doesn’t have one natively.
The most common one is PuTTY, which you can download on https://www.putty.org/.
Next, you have to transform the key you downloaded in the previous chapter. Remember, the file with the pem extension.
Then, run PuTTY and configure it with what AWS told you:
Finally, click “Open”, type “ubuntu” when you’re asked login as, say “Yes”, and a console will open!
That’s it.
You’re in!
For Ubuntu and Mac users
If you’re not on Windows, things are way easier.
Open a console, change the directory to be in the one that contains the pem key, and type the instruction given by AWS.
For example, for me it’s:
Say “yes”, and you’re in!
If you get an error message such as “WARNING: UNPROTECTED PRIVATE KEY FILE!”, it’s normal. You have to change the rights of the file by typing:
Next step is to install Shiny Server.
And of course, before that, we need to install R!
The Ubuntu repos contain an outdated version of R. So instead, we first add the CRAN repo:
Once it’s done, the CRAN repo is added and you can install R:
Once R is installed, we also need to install the shiny package before installing Shiny Server.
Run R in admin mode and install the package:
You can quit R by typing q() and n.
Ok.
That’s progress.
Final step: Download the last version of Shiny Server.
Go on https://www.rstudio.com/products/shiny/download-server/ and scroll down at the bottom of the page to see the instructions, with the version number.
For me, it’s:
The last two lines might be different if a new version is released.
Once you launch everything, say “yes”, and everything will get installed!
That’s it!
You don’t know it yet, but you already have a Shiny app running on your server now.
How do I see it?
That’s what we’ll see in the next part…
Let’s recap.
At the end of the previous chapter, I said you already had a Shiny app running on your server!
Indeed, it appeared by installing Shiny Server.
To access it, two steps:
Find the IP address on your server
When you start a new AWS server, an IP address is automatically affected to it.
Warning: The IP address changes each time you reboot the server. If you want a fixed IP address, you’ll need to visit the Elastic IP section and allocate an address to your server.
In your EC2 dashboard, there is a column IPv4 Public IP.
Find it and note the IP address.
For me, it’s 3.121.42.9 (the link will not work, I shutdown the server after publication of the guide).
And to access the Shiny app, you just need to type this address in your browser AND specify the port number: 3838.
Except… it doesn’t work.
Open the port 3838 on the firewall
The reason is that for security reasons, AWS blocks all the ports except 22 by default. Port 22 is the one we used to access the server via SSH in the previous chapter.
To change that, from your EC2 dashboard:
Type | Protocol | Port Range | Source | Description |
---|---|---|---|---|
SSH | TCP | 22 | Custom: 0.0.0.0/0 | |
Custom TCP Rule | TCP | 3838 | Custom: 0.0.0.0/0 |
Here are a couple of screenshots in case you’re lost:
.
Once the port is open, the app will load!
Let’s try again:
This time…
Boom!
It works!
Note: The address is a bit ugly right now. You must type an IP address followed by a port number, erk. In the next chapter, we’ll discuss how to have a readable address and get rid of the port number.
We obtain an HTML page with a couple of Shiny widgets in the right sidebar.
You may get an error message in one of the widgets: “An error has occurred”. This is totally normal, it comes from a missing package.
Okay, cool! But.. how to do put our app now?
To do so, we need to get back to the console of the server.
If you forgot, get back to the previous chapter: Install R and R Shiny
First, let’s explore what we have.
Explore your server
We just noticed that when you first install Shiny Server, you get a default app.
This app must be stored on the server, right?
So where is it?
Simply, it’s in /srv/shiny-server
. That’s where we will store all our apps.
Type the following in the console:
Notice there is a directory, sample-apps
, and a file, index.html
.
The file index.html
is what you saw when you accessed the app. And it calls the app that is in sample-apps
. If you dig deeper:
You find the usual server.R
and ui.R
, the foundations of any Shiny app!
By default, Shiny Server uses this directory to display apps. You can change it, but I don’t recommend it.
What we’ll do instead is:
/srv/shiny-server/
.Download the app on the server from Gitlab
First, let’s get back to the home folder:
And let’s clone the app from Gitlab:
Good!
Gitlab is really handy when it comes to sharing your app everywhere.
Except that, as we said before, Shiny Server expects the app to be in /srv/shiny-server/
.
Create a shortcut in /srv/shiny-server/
The following commands will create the shortcut:
Now, if you display the list of files, you’ll see the shortcut along the other files:
And we can remove the other files, by the way:
Alright!
We added our app in the right folder so… it should be accessible now!
Let’s test it.
If I type 3.121.42.9:3838 in my browser, I get…
Oops.
Not really welcoming. We’ll change that later.
Click on the link, and…
ERROR: An error has occurred. Check your logs or contact the app author for clarification.
Erk :(
…
Hm.
It seems the server has indeed noticed the changes, but it doesn’t work.
Why?
We’ll have to change the config a little bit.
The config file is in /etc/shiny-server/shiny-server.conf
.
We’ll open it and try to understand what’s going on:
The file should contain the following:
# Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;
# Define a server that listens on port 3838
server {
listen 3838;
# Define a location at the base URL
location / {
# Host the directory of Shiny Apps stored in this directory
site_dir /srv/shiny-server;
# Log all Shiny output to files in this directory
log_dir /var/log/shiny-server;
# When a user visits the base URL rather than a particular application,
# an index of the applications available in this directory will be shown.
directory_index on;
}
}
Let’s tackle it line by line:
run_as shiny
indicates the user behind the Shiny Server. When you logged in through SSH, you acted as user ubuntu
, the default one. But when we installed Shiny Server, we created a new user called shiny
, and that’s the one running the Shiny Server.listen 3838
. That’s the port. That’s why we added :3838
at the end of the URL.site_dir /srv/shiny-server
tells you where you need to put the files of the app. That’s indeed where we created the shortcut.log_dir /var/log/shiny-server
shows you where the logs are stored. That’s super useful in case something’s buggy. We’ll look into it very soon.directory_index on
, as the comment states, enables the server to show the directories when there is no index.html
. That’s what we saw when we went directly on 3.121.42.9:3838
. I don’t like it much, so I prefer to deactivate it: directory_index off
. Plus, I don’t want everyone to know all the apps I have.We need to make one important addition to this file. Right at the top, add:
This will enable you to keep the logs under any circumstances.
I don’t know why, but Shiny has the bad habit of deleting log files when it considers nothing important happens. But sometimes it does it even when the app crashes.
To take into account the changes, we need to reload the server:
Speaking of logs.. let’s see why our app crashes!
To display them:
I see two files.
The first one, named movie-explorer
, is our app. The second one is from the default app.
The numbers show you the date. Notice the first file was created on 2018-12-10 at 08:05:34. When the log files start to accumulate, it’s very handy to know when it was created.
If you don’t see any file, try to relaunch the app in your browser. That’s the issue of the disappearing log files I was mentioning before. It should be solved with the new config.
If I display the last lines of the log file, I see:
Ooooh, right!
We haven’t installed the libraries!
Obviously, it doesn’t work!
Plus, we need to install them for the user shiny
, as it’s the one running the Shiny Server.
Let’s do it:
You’ll get asked if you want to use a local library, say yes.
Once the package installed, try again launching the app.
This time, you’ll see some progress:
Something showed up!
..but then it crashed.
If you go back to the log files, you’ll notice a new file was created, and it says:
Another package is missing! dbplyr
.
Note: After installing the package for the user shiny
, you can go back to the main user ubuntu
by typing exit
in the console.
Now you know how to install the package.
Oh, and, spoiler alert, you’ll also need to install RSQLite
!
And finally…
Yay o/
Note: By the way, if you need to install the dplyr
package, you will need at least 2 GB of RAM on your server.
With AWS you can upgrade the server, install the package, and then downgrade once done.
After:
We FINALLY can access our app and share it with everyone in the world!
Good job ;)
Now, you probably have some questions left, like…
We’ll answer them in the next chapters.
We deployed the Shiny app on the AWS server that we had created. We just had to type 3.121.42.9:3838
in the browser and our app appeared magically!
Yay!
Just as a reminder from the previous chapter, here is what you have on the server:
/srv/shiny-server
directory./var/log/shiny-server
. This helps to understand why your app crashes./etc/shiny-server/shiny-server.conf
.These are the three directories you need to remember.
After a while, you get used to it.
Until then, don’t hesitate to reach out to the previous chapters.
Now..
I just said we can reach our Shiny app just by typing 3.121.42.9:3838
, and.. that’s kind of great!
But seriously, will you tell your friends to type and remember these numbers?
Or tell your client he has to use these numbers?
Not really..
What would be cool would be to have a really nice-looking URL.
For example, I host all my apps on shiny.datachamp.fr.
Easy to remember, and convenient for everyone.
You can find our app at this address: https://shiny.datachamp.fr/051-movie-explorer/.
How to do that?
Three steps:
If you’re not familiar with servers, this might feel complicated.
But stay with us. I’ll guide you through each step with screenshots, as usual.
The first step, if you want to use a domain name, is to get one!
This also means buying one.
Right.
It costs money.
Mine (charlesbordet.com) costs $14.99 per year.
But you can find cheaper ones, sometimes for less than 2$ per year.
I got mine at Hover.com, a company specialized in domain names.
I’ve been using them for years and never had any issues.
I’m also using OVH for .fr
domains since Hover doesn’t do them.
I will use Hover in the rest tutorial but don’t worry. Pretty much all domain providers have similar interfaces.
Alright!
So let’s say you have bought your cool new domain name!
Now, you must make the link between:
What do we use for that?
DNS of course!
DNS stands for Domain Name System.
It is one of the most fundamental services used all over the internet.
Even though pretty much nobody knows what it is.
It’s what allows you to type https://duckduckgo.com in your address bar rather than 23.21.193.169.
In a few words, it makes using the internet way friendlier!
And that’s exactly what we want to do!
We want people to type http://mysupercoolshinyapp.com rather than 3.121.42.9:3838
.
Your domain name provider will have a page where you can set up the DNS.
In Hover, simply click on the DNS tab:
You can see some default entries here that might be different than my screenshot.
If you’re curious to understand what’s going on in this table, check out the blue box. Otherwise, you can jump right after to continue the tutorial.
Each DNS entry indicates where to redirect users when they type your domain name.
Why not have only one entry?
Because you could have datachamp.fr
on one server, then shiny.datachamp.fr
on a different server, and so on. For each specific use, you must add a new row.
The TYPE column can be A to redirect the domain name towards the IP address of a server. It can be CNAME if you redirect the domain name towards another domain name. It can be MX for a mail server.
The HOST column is for the subdomain. The subdomain is everything before your domain. For example, when I type shiny.datachamp.fr
, this is a subdomain of datachamp.fr
. The star * means all subdomains. The @ means the main domain (datachamp.fr
in this case).
The VALUE column depends on the TYPE you chose. If you choose TYPE A, then you must enter an IP address.
The rest is unimportant for us today.
The default values here tell us that the main domain datachamp.fr
and all subdomains *.datachamp.fr
are redirected to the IP address 64.98.145.30.
That is not exactly what we want.
What I would like is to have the subdomain shiny.datachamp.fr
redirecting directly to the IP address of my AWS server.
To do so, remove both rows with TYPE A, and add this one instead:
If you don’t want to use a subdomain and you want your main domain to redirect to your AWS server, then write @
instead of shiny
for the hostname.
Finally, my table looks like that:
Now, every time someone types shiny.datachamp.fr
in his browser, he gets redirected automatically to my AWS server!
We’re not done though!
If you try by yourself, you’ll notice it doesn’t work.
Why?
We haven’t specified a port!
You still need to write shiny.datachamp.fr:3838
to access the app.
Meh..
That’s kind of annoying.
But we’re getting close!
There are two ways to solve this issue:
The Quick & Dirty port removal solution
If you want something quick that works right now, do the following:
sudo nano /etc/shiny-server/shiny-server.conf
.listen 3838
for listen 80
.sudo systemctl restart shiny-server
.And that’s it!
Note: Don’t forget to open port 80 in the AWS firewall. See the chapter Open the port on the firewall.
Now Shiny Server listens on port 80 instead of 3838.
Why 80?
80 is the default HTTP port.
In fact, when you type shiny.datachamp.fr
in your browser, the browser uses port 80 by default.
Before, we had to specify 3838
because it’s uncommon and it can’t guess it.
So, why is it a quick & dirty solution?
Quick, ok.
Dirty, because if tomorrow you want to install another service on your server, such as an RStudio Server, or a Jupyter Notebook server, or a website, you will face the same issue.
And only one service can listen to port 80 at a time.
If you have a Shiny Server and an RStudio Server on the same machine, they must use two different ports.
They can’t both use port 80.
But you still want to have two clean URL such as shiny.datachamp.fr
and rstudio.datachamp.fr
.
So, what to do?
The solution is to use a Reverse Proxy Server like Nginx.
I could tell you exactly what to type in the terminal and be done with it.
But that’s now how I teach.
I prefer to have you understand what you’re typing.
If tomorrow you want to do something slightly different, you’ll be able to do it.
So, let’s start with..
What the hell is a reverse proxy?
Think of your server like a house that has many doors.
65,535 doors exactly.
And let’s call these doors ports, okay? porte is the French word for door.
If you want to reach out for some information in the house, you must pass one of these ports, ask the guy behind, and then get back home with the information.
Well, Shiny Server is one of these information providers.
It’s located by default at port 3838.
And let’s say you also have a service at port 8080.
That’s the default port if you install RStudio Server.
If you knock at port 3322, nobody will reply, because nobody’s there.
This works.
But it’s not handy because you have to knock at the right door.
As we said before, you have to specify the port number in the URL, such as shiny.datachamp.fr:3838
and it’s ugly.
What if, instead, there was a doorman at the main entrance that could bring you to the right person?
You ring at the main entrance and ask for Shiny Server, and you get to it right away.
And if you want RStudio Server, you ring at the main entrance and ask for RStudio Server.
Wayy easier!
No messing around trying to find the right door.
This doorman is called a reserve proxy.
Nginx is the best reverse proxy server out there.
Instead of typing the port number, you can only type shiny.datachamp.fr
and talk to Nginx.
Nginx understands you need to access the Shiny Server, so it fetches the right information and get it back to you directly.
To do so, we need to:
Install Nginx
This is the easiest step.
After SSH-ing to your AWS instance (check part 3 and stop forgetting how to do it), type the following:
And Nginx is installed.
Easy.
Configure Nginx
Now for the tricky part.
First, change directory (cd
) to where Nginx is installed:
The ls
command list all the files in this directory.
What’s of interest for us is the nginx.conf
file, but we won’t touch it.
Instead, well jump to the sites-available
folder and create a new file specifically for Shiny Server.
And that’s where we write the config file:
There isn’t much going on here but it can look intimidating.
listen
The beginning states that the Nginx server now listens on port 80. Exactly what we wanted to do with our Shiny Server.
But since only ONE service can listen on port 80, we give it to Nginx.
In short, we’re telling our doorman to wait at the main entrance.
server_name
The server_name
is what the user types to access the app.
In my case, I want the user to access my Shiny app on the subdomain shiny.datachamp.fr
.
Replace the value with your own domain name.
location
Finally, everything inside location
is the reverse proxy happening.
We’re giving instructions to our doorman:
If the user comes and asks for shiny.datachamp.fr
, then guide him through the house at door 3838.
And that’s it!
If you want to use this config file for RStudio Server, you can add a second file called rstudio.conf
, and inside you put the same config and only replace two things:
server_name
There is one thing left to do.
Save the file and go back to the terminal.
We need to create a shortcut inside the sites-enabled
directory:
Why is that necessary?
The deal is that Nginx does NOT look at the sites-available
folder.
Only at the sites-enabled
folder.
So we create the conf file inside sites-available
and then we create a shortcut inside sites-enabled
.
Why? So that if tomorrow you want to temporarily deactivate your access to Shiny, you only have to delete the shortcut.
You don’t have to delete the entire conf file and regret it later.
Now that our reverse proxy is set up, we only need to restart Nginx to take into account the changes.
First:
This ensures that the conf file is correct.
If you get the error nginx: [emerg] unknown "connection_upgrade" variable
, that’s because the $http_upgrade
and $connection_upgrade
variables don’t exist.
In this case, open /etc/nginx/nginx.conf
and add the following lines inside the http bloc:
Save, and try to validate the syntax again with sudo nginx -t
.
And:
Congrats!
Note: Don’t forget to open port 80 in the AWS firewall. See the chapter Open the port on the firewall.
Your Shiny app is now accessible on a good-looking URL such as https://shiny.datachamp.fr/051-movie-explorer/!
The last part was not easy, but that’s the best way to do it.
Plus, it comes with other benefits.
Such as..
Making your app secure with HTTPS!
So we changed the ugly IP address of our Shiny app in a good-looking domain name, so that we could reach the app by typing an URL like http://shiny.datachamp.fr/051-movie-explorer.
Nice!
Except there is still a problem.
Especially if you want to put your app in production for a client.
The app is not secured.
You may have noticed the subtle message from your browser:
Uh oh…
Indeed, the app is using the HTTP protocol, which is not secure.
Nowadays, everyone uses HTTPS, the secured version of HTTP.
That’s why, in this chapter, we’ll talk about:
Let’s get started!
You certainly have heard of it, but you’re not sure how HTTPS works and why we need to use it.
Well. Imagine that each time you land on a webpage, you send a request from your home to the data center where the webpage is hosted.
Then, a request containing the webpage comes back to you so that your browser can display it.
These messages drive all over the world, from router to router, until they reach their destination.
You don’t control who own these routers, nor what they do with it.
And when you use HTTP, those messages are not encrypted.
It means that anyone can intercept the requests, open them, read them, even change them, and send them back without anyone knowing.
That’s called a Man-in-the-Middle attack.
It’s not cool when the requests contain passwords, data or your credit card numbers.
That’s why we decided to upgrade HTTP into something secure: HTTPS.
HTTPS guarantees that the messages are encrypted. If someone intercepts the request, they can’t do anything with it.
At first HTTPS was mostly used for e-commerce, where the data is confidential, but later on, it was used for the whole internet.
Because privacy is good.
That’s why your browser displays this big red warning.
In short. You want:
So we use HTTPS.
How?
First, we need certificates (SSL/TLS) for the server. And keys.
I won’t get into too many details, but the certificate says “Yea it’s me the real server” and it contains encrypting keys (a public and a private one).
You can get certificates from trusted organizations.
Most of the time, it costs money.
But not with Let’s Encrypt. It’s free and automatic.
So.. let’s use it.
They even have some small piece of software to automatically renew the certificates. Otherwise, they expire every 3 months.
So, you do this setup once, and then you won’t have to touch it again.
To get started, visit the Cerbot website:.
Choose:
Scroll down and copy/paste the installation instructions in your server. If you don’t remember how to do it, you can go back on the chapter Install R and R Shiny.
Then, you can automatically get a certificate by typing:
Don’t forget to change the domain name for yours.
The program will speak to you then:
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for shiny.datachamp.fr
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/shiny.datachamp.fr/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/shiny.datachamp.fr/privkey.pem
Your cert will expire on 2020-02-04. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
The most important part is where it says that your certificates and keys are in the /etc/letsencrypt/live/shiny.datachamp.fr
directory.
We’ll use that later.
For this section to work, you need to have an existing nginx
configuration.
That’s the optional section I wrote in the previous chapter. Check it here: Set up a reverse proxy with Nginx.
If you haven’t done it yet, go there and come back once it’s set up.
We’ll wait for you.
Remember, you had installed a piece of software called nginx
.
nginx
is a web server. One of its features is the reverse proxy that we set up last time.
In the previous chapter, I explained that Nginx is like a middleman. It listens on port 80 and then redirects visitors to the correct port (3838 if they ask for shiny for example).
Port 80, that was for the HTTP protocol.
But now, we want to use HTTPS.
The port for HTTPS is 443.
How do I know this?
Each protocol has its own dedicated port. It’s not really up to us. For example, SSH is on 22, FTP on 21, etc.
It’s all listed on this Wikipedia page: List of TCP and UDP port numbers.
It goes up to 1024, and then any number after 1024 is free to use. That’s why Shiny picked 3838 by default (but we can change it).
Anyway.
We’ll change the Nginx configuration to use 443 instead of 80.
As a reminder, here is the path to the config file:
You should get the file we modified last time.
We’ll change a few lines.
1. Change the port
First, let’s change the port number.
Find the two lines that start with listen
and replace them with the following ones:
2. Configure SSL
Next, let’s add the SSL parameters.
SSL, that’s the encrypting protocol. Think of it like HTTP + SSL = HTTPS.
We should say TLS now, but everyone keeps talking about SSL.
I’m no expert in SSL.
So I can’t tell you every tiny detail of it.
And that’s why I use Mozilla’s recommendations. They have an SSL Configuration Generator.
Check the link and add these lines in the config file:
That’s a lot.
And there are a few things to change:
The two first rows: ssl_certificate
and ssl_certificate_key
. You need to feed them with the certificates we generated earlier.
I changed them with:
(Of course, don’t forget to put your domain name)
3. Download the DH parameters
Then, the DH parameter.
The comment tells us to download them. Let’s do it:
And then change the path for ssl_dhparam
:
About the HSTS
section, I recommend you deactivate it for now.
HSTS is used to tell the browser: “From now on, and for years to come, you only use HTTPS for this domain and never HTTP.”
It’s a good practice.
Except if you have issues to set up HTTPS and want to come back to HTTP, you’re screwed.
And since it’s your first time, you should expect things to go wrong.
Even if you’re following my guide.
So let’s deactivate it for now, and only when we’re sure everything works we’ll activate it again. I’m adding an #
before:
4. Use the Let’s Encrypt certificate
Finally, we’re only left with the last line:
And… that’s it!
It’s test time!
Save the file, and check that there are no syntax errors:
You should get the following response:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
If not, it means you have done a syntax error. The error message should tell you what’s wrong.
Once the syntax is validated, let’s restart nginx
:
And finally.
Check your domain name (type https at the beginning): https://shiny.datachamp.fr/051-movie-explorer.
Note: Don’t forget to open port 443 in the AWS firewall. See the chapter Open the port on the firewall.
Your browser should display a nice lock with a comforting message:
Good job!
We’re done.
Almost.
I told you earlier to deactivate HSTS.
Now that everything is working smoothly, let’s activate it:
And we’ll do a second change.
In the SSL configuration generator, they added another server
bloc at the top of the file.
Well, this other bloc is listening on port 80, and its only job is to automatically redirect to port 443.
That’s for the users who will visit your app with HTTP.
Instead of getting a 404 page, they’ll get redirected to the secured page.
So, let’s add these few lines at the top of your config file:
Now we’re good!
Don’t forget to restart nginx
once again:
Now, your app is secured, and the data you send with it is encrypted.
Woah, this is getting serious!
We’re a bit far away from the R language now and we can say we entered the sysadmin world.
I don’t know about you, but I love this new world.
I find it super interesting to learn about how servers work, and even the Internet.
Please share in the comments if you’ve successfully set up HTTPS (or if you’re stuck somewhere).
In the meantime, there is one last topic to tackle in this guide: How to protect the app with a password.
It’s time to learn how to protect your Shiny app behind an authentication portal.
Most Shiny apps I create for clients are internal apps.
They use confidential data and the dashboard is intended to be used by a specific team.
Not by the public Internet.
In some companies, they isolate the app thanks to a VPN.
But sometimes, it’s not possible.
Sometimes you want to open your app to the Internet and restrict it to some users.
And you want to manage their rights.
You are at the right place.
From the simplest solution to the most complex one:
nginx
Now…
Before we start, a small disclaimer.
We’re talking about security.
I am NOT a security expert.
It’s a real job. My job is analyzing data. Not security.
You are responsible for the security of your applications.
You are responsible for your data.
This article is purely informative.
:)
Now that this is stated, let’s start!
There is no best way to secure a Shiny app.
Here are your possibilities:
Shiny Server Pro
Haha.
I’m kidding!
Use your web server - nginx
We have already used nginx
as a reverse proxy.
Remember.
nginx
is your doorman.
It redirects visitors to the right service. To the right port number.
Another role for this doorman could be to accept or deny access to these visitors.
Rather than letting anyone pass, it could ask for credentials.
This solution works well. It’s easy to set up. And it’s secured.
Use a third-party service - Auth0
auth0
is an authentication service you can integrate into any application.
It’s free up to 7000 users.
The biggest advantage is that you don’t have to worry about security.
That’s THEIR job.
Their expertise.
Not yours, nor mine.
So they know better than us!
However, using a third-party might be an issue for you. It creates dependency. You need to trust them.
Build your own authentication portal
Or: Do It Yourself.
If you’ve read this guide so far, well… you probably like to do things by yourself.
This is the most flexible solution, but also the less secure.
Since you’re not a security expert.
And me neither.
You’ve got to ask yourself…
Is Shiny secure?
Imagine that you add an authentication portal at the start of your Shiny app.
The logic of the code is:
“If the user logs in, then we show the rest of the app.”
First, it means the user does access to the Shiny Server to log in.
Is the rest of the code well hidden? How well?
Could the user manipulate the Javascript console? Or another backdoor we don’t know anything about?
Is the authentication portal resistant to SQL injection? To code injection? To brute force? To DoS attacks?
Answers aren’t always clear.
And even if you can answer to these questions, what about what you don’t know.
So…
This solution works.
And it’s not optimal for security.
Now let’s dig into each of the other solutions.
nginx
as an authentication serverThis is the simplest solution.
And the least flexible.
In short…
You write the credentials in a file.
Then you get an unwelcoming popup at each connexion to the app.
It’s a quick and dirty solution.
But it’s efficient if you have an app that you want to show only to a few people.
For example, to a client.
You want to show him what your work looks like, but to him only.
The simplest way is to host it on your server, create a password with nginx
, and share it with your client.
To set up this solution, we use the console once again.
As a prerequisite, you need to have followed along the instructions of the two previous chapters:
Your config file (the one at /etc/nginx/sites-available/shiny.conf
) should look like this:
This file is getting big! But as we completed it step by step, it’s not that scary.
Now, we add a new location
bloc that’s almost identical to the existing one, with two differences:
/
) but the /051-movie-explorer
directory.Here is the new location
bloc that you add below the existing one:
Why adding a location?
We could add only the last two lines to the previous location
bloc:
But if you do that, ALL your Shiny apps will be protected by the same passwords.
That’s not what we want.
We want to have some apps that are protected, and others that stay accessible by anyone.
With my solution and a second location
bloc, you can choose.
Save the config file.
Now, we fill the /etc/nginx/.htpasswd
with the credentials.
It’s super simple.
First, run the following instruction to create the login:
Of course, you can replace charles
by any other login.
Then, the following instruction will enable you to type a password but store only a hashed version of it (for security):
That’s it!
You can repeat the operation as much as you want to create other logins.
Now, restart the nginx
server:
And your app is protected.
Try for example my app at https://shiny.datachamp.fr/movie-explorer-secure/ and notice the authentication popup.
No credentials? No access.
Try:
And now you can access.
This method is secured because you force visitors to walk through nginx
.
And by the way: Don’t forget to block the 3838 port in your firewall.
You can do so in AWS (reverse what we did in part 4)or by using ufw
.
Otherwise people could walk around nginx
.
Another important point: Use a strong password.
This method is not resistant against bruteforce.
To protect yourself against it, use fail2ban
.
Now…
This method works well.
It’s pretty secured.
But.
It’s not flexible at all!
It’s complicated to create new accounts.
No interface.
You have to use the console.
And only you or someone technical enough can do it. It’s a bit complicated.
You can’t automate it.
It’s not user-friendly.
Could you imagine a pop-up like that to log in to an e-commerce website?
Ha ha.
No.
It doesn’t meet all possible needs.
Either you get in.
Or not.
No account management.
No user right.
You don’t even know who the user is in the Shiny app.
So let’s try an other solution.
This second solution is a bit more complicated to set up, but has interesting features:
Auth0 is an authentication and authorization service provider.
Rather than creating our own authentication portal, Auth0 will deal with this itself.
How to set up Auth0
Create an Auth0 account.
Go to https://auth0.com/signup and create an account.
The service is free up to 7000 users.
Once the account is created, log in and click on the big CREATE APPLICATION button:
Give it a name and choose Regular Web Applications:
You won’t find Shiny in the default app, so let’s configure the portal manually.
Click on Settings.
Here are the important information to note:
You will need it later.
A bit below, in the Allowed Callback URLs cell, you have to fill in your URL followed by callback.
For me, it’s https://shiny.datachamp.fr/callback
.
Warning: No trailing slash /
Save the modifications.
Install the portal on your server.
SSH into it and follow these steps:
1. Start by installing NodeJS.
2. Then, install the Auth0 application:
3. Finally, configure Auth0 with the information you got earlier.
Create a new file named .env
(by typing nano .env
for example) and enter the following:
The first four fields are the information you got earlier in your Auth0 account.
The COOKIE_SECRET must be a random string. I used a password generator to get mine.
You can leave the rest as it is.
Alright!
Is our app secured?
Not yet.
Shiny listens on port 3838.
But the authentication portal listens on port 3000.
And we configured nginx
to redirects to port 3838.
We have to change that.
Open your nginx
config file (at /etc/nginx/sites-available/shiny.conf
) and change the instructions for proxy_pass
and proxy_redirect
:
I changed the port number 3838
for 3000
.
Save the file and restart nginx
:
Is our app secured?
Not yet!
We broke the app.
It’s not even accessible anymore!
Indeed, we’re redirecting to the Auth0 portal now, but we haven’t started it!
Go back to the shiny-auth0
directory and run the following:
The console shouldn’t display anything. And the prompt is gone as well.
That’s because this instruction manually starts the authentication portal server.
It stays this way and will display the access logs.
At least now we can test the portal.
Go to your Shiny app and you should see this:
You can create an account. Log in and then access to the app.
Cool!
If you want to get back your console prompt, you have to stop the server with Ctrl + C.
Something is weird though.
We have two issues:
Let’s take care of this.
Create a service for Auth0
We have a few web servers on our machine.
The Shiny Server: On port 3838.
The nginx
Server: On port 80.
And now, the Auth0 Server. On port 3000.
Why do we have to start the Auth0 server manually and not the others?
The others are services. They start automatically as soon as the machine boots. And the service configuration has been done during the installation of these web servers.
For Auth0, the service configuration hasn’t been done.
We have to do it. Then, everything will be automatic.
It’s not complicated.
Create a new file:
And fill it with this service configuration:
Make sure you replace charles
with your username.
In general, it’s better to create a new username for each service. Like the shiny
username that was created for the Shiny Server.
If the service is compromised, the rest of the server stays safe.
Once the configuration is done, let’s activate and start the service:
Now, your authentication portal is online, and there’s no need for a manual start anymore!
Let’s tackle the other issue: How to restrict the access?
Create access rules
Go back to the Auth0 account management.
There is a Rules section that will enable you to create Javascript snippets.
Click on CREATE RULE and you will see a ton of possibilities:
For example, you can:
I must admit this part is a bit intimidating to me.
I don’t know Javascript that well, so I prefer to use only the pre-configured rules.
For example, for a client who wants the app accessible only to employees, it’s easy to limit the access only to emails that look like xxx@companyname.com
.
It’s worth noting that being able to use Javascript allows for a lot of flexibility.
You could imagine having a SaaS product with your Shiny app. The buyer is added to your Mailchimp account on a specific list. Then, the rule calls Mailchimp API to check if the email is authorized.
Or the other way around. Your Shiny app is free, but users must create an account and their contact information is sent to Mailchimp, so that you can talk with your users or notify them of new versions.
For us Shiny developers, learning Javascript can’t hurt anyway.
So. This Auth0 service is quite good.
Except I don’t use it much except in very specific cases such as the exemples I just mentionned.
Hence the third and last solution: Create your own authentication portal.
Here is the package you’ve been looking for: shinymanager
It’s available on CRAN:
You will find some documentation on their Github repo.
But I find it a bit light.
Let’s start with their minimalist example to add the authentication portal.
It works!
So let’s try to adapt it to our movie explorer app.
As a reminder, the code of the app is available here.
After reading the documentation, we understand we need to:
shinymanager
packagesecured_app
server
partLet’s do it!
Create credentials
The easiest part.
I use the piece of code from their example and put it in the global.R
file:
Yup.
Credentials are stored like that.
Not secured!
We’ll come back to it later :)
Load shinymanager
Well, this is the easiest part.
I load it in the global.R
file as well:
Wrap the UI with secure_app
In the ui.R
file, find the fluidPage
function.
That’s where the UI starts.
I add the secure_app
function around fluidPage
:
And don’t forget to close the ending bracket.
Add the authentication module
Finally, I’ll copy/paste another piece of code at the beginning of the server
function:
Boom!
Done.
Except… We created a bug.
Because of ggvis
.
It doesn’t like empty data.frames
.
And because of the authentication part, that’s what we have.
I tell it to plot something empty as long as we’re not logged in:
Now it works!
I’ve put my code here: https://gitlab.datachamp.fr/charles/movie-explorer-login.
And hosted it here: https://shiny.datachamp.fr/movie-explorer-login/.
You can try to log in with the shiny/azerty
credentials.
More options
shinymanager
doesn’t stop there.
You can also:
But…
If you want to offer the possibility to create accounts, you have to do it yourself.
There are still a lot of things you have to do by yourself.
shinymanager
is a good starting point.
Not a full solution.
Whenever I create authentication portals with Shiny, I do the following:
shinymanager
and add the code to my projectThe first time was time-consuming as I had to go through the intricacies of the code.
But now I’m good.
I recommend you do the same.
Plus, you will better understand the strengths and weaknesses of the portal in terms of security.
I add these features:
In the end…
I like this solution.
Because of the flexibility.
The only drawback is security.
I don’t know what I don’t know.
So be careful.
If you deal with confidential data, ask a professional.
Here is a short recap of all the pros & cons of each solution:
Solution | Price | Setup | Dependency | Security | Administration |
---|---|---|---|---|---|
Shiny Server Pro | Expensive | Easy | Independent | Good | No |
nginx |
Free | Easy | Independent | Good | No |
Auth0 | Free up until 7k users | Medium | Dependent | Good | Yes |
Shiny portal | Free | Hard | Independent | Medium | Yes |
Which one will you choose?
This chapter ends my guide on How to deploy a Shiny app on AWS.
I hope you found it useful!
I know there are still plenty of topics to cover.
Docker, shinyproxy.io, scalability, …
I will write about these.
:)
To get updates, you can subscribe below.
]]>Plutôt la base pour une appli Shiny.
Étant statisticien à l’origine, j’ai appris au fil des années à créer des dashboards plutôt cools et mes clients sont contents !
Sauf que vient toujours le moment où il faut déployer l’application sur les systèmes du client.
Et là… ça se complique.
Je maîtrise plutôt bien le langage R et il m’arrive régulièrement de créer des dashboards en Shiny pour faire des reportings automatiques.
Sauf que… créer un serveur où je peux héberger l’application, la configurer, et ensuite maintenir le serveur à jour…
C’est pas tout à fait mon boulot à la base.
Je vous avoue que la toute première fois où j’ai été confronté au problème, mon équipe et moi avons opté pour une solution un peu… disons… artisanale !
J’ai presque honte d’admettre ce qu’on a fait…
On a…
On a installé R et le code de l’appli Shiny sur chaque poste de chaque utilisateur.
…
Voilà.
Oui oui.
Il a fallu aller dans chaque bureau, installer R, les packages, aller chercher les données, ajouter un raccourci sur le bureau, … Bref.
Avec le recul, on avait été assez créatif sur le coup.
Mais en vrai c’était horrible !
Mais bon.
On n’avait ni les compétences, ni les moyens d’accéder à un serveur.
On s’est débrouillé comme on pouvait.
Mais il faut savoir qu’il existe une bien meilleure méthode.
Il s’agit tout simplement de :
Et c’est tout.
Bon, bon, ok, je dis tout simplement, mais c’est pas SI simple (sinon ce guide ne ferait pas 13.000 mots).
Au début en tout cas.
Aujourd’hui, dans ce guide sur le déploiement d’application Shiny sur un serveur AWS, on va voir exactement comment procéder, étape par étape.
Comment ?
D’abord, on va utiliser AWS, c’est le petit nom de Amazon Web Services, c’est-à-dire tous les services Cloud proposés par Amazon.
Vous pourriez utiliser des services concurrents, comme Azure chez Microsoft, Digital Ocean, Rackspace, etc.
Peu importe en fait.
Au début, j’utilisais AWS, maintenant j’utilise mon propre serveur dédié. Le fonctionnement est exactement le même et ce guide vous servira quelque soit le fournisseur que vous utilisez.
Mais on va utiliser AWS parce que c’est ce qui est le plus utilisé.
En plus, quand on crée un nouveau compte, on a accès à certains services gratuitement pendant un an.
Tout ce qu’on va faire dans ce tutorial est faisable sans débourser un sou.
Les avantages de cette méthode :
Mon but est de créer un guide de référence auquel vous pourrez toujours revenir dès que vous serez confronté à la problématique Comment déployer mon appli sur un serveur ?
Jusqu’à ce que vous reteniez comment faire.
Mais je sais d’expérience que c’est le genre de chose qu’on oublie vite.
Je vais donc expliquer chaque étape avec moults détails, plein de screenshots, et des dessins rigolos.
Quelles étapes ? Les voici :
Dans la Partie 1, on va aller se trouver une application Shiny basique. Bon, c’est pas le but du guide d’apprendre à créer des applis, donc on va en prendre une qui existe déjà.
Dans la Partie 2, je vous montrerai comment créer un serveur sur AWS. C’est la seule partie qui est vraiment spécifique à AWS, et en vrai, elle est nécessaire seulement parce que AWS est assez difficile à prendre en main tout au début.
Dans la Partie 3, on verra comment installer R et R Shiny sur votre tout nouveau serveur. Comment ça marche ? Comment on fait ? À l’issue de cette partie, vous aurez déjà un serveur fonctionnel et même une appli Shiny disponible sur les internets !
Dans la Partie 4, on va finalement déployer l’application sur le serveur, pour qu’elle soit disponible à tous !
J’aurais pu m’arrêter là. Sauf que.. vous n’aurez pas un résultat parfait, et probablement encore d’autres questions. C’est quoi j’ai rajouté trois autres parties.
Dans la Partie 5, on va aller chercher un nom de domaine pour avoir une belle URL. Oui parce que jusqu’ici, il fallait taper une adresse IP pour accéder à l’application.
Dans la Partie 6, ça va se compliquer. On va se prendre un serveur web qui s’appelle nginx, on va générer des certificats TLS, tout ça afin de sécuriser votre application en HTTPS. Ça paraît compliqué, mais chaque étape sera détaillée !
Dans la Partie 7, on voit finalement comment protéger l’application avec un mot de passe. Oui parce que tout le monde ne veut pas que son appli soit ouverte au public. Je vous montre notamment quelles sont les différentes possibilités qui s’offrent à vous pour protéger votre appli.
Et là, on sera paré !
D’ailleurs, je vous propose de commencer directement avec l’étape 1 : Créer une application Shiny.
C’est pas exactement le but de ce guide d’apprendre à utiliser Shiny, du coup on va prendre une appli disponible dans la galerie Shiny.
J’ai pris celle de l’explorateur de films :
Et j’ai créé un repo sur Gitlab pour que vous puissiez la cloner facilement : https://gitlab.datachamp.fr/charles/051-movie-explorer
Notez que vous aurez besoin de git et d’une console pour suivre ce tutoriel. Vous pouvez télécharger git à l’adresse suivante : https://git-scm.com/downloads
Euh.. c’est déjà fini ?
Ben oui, c’est partie là est vraiment courte :D Profitez-en !
Nous allons commencer par créer un serveur AWS afin d’héberger notre application sur les internets.
Pour ça, la première étape est de vous créer un compte sur AWS.
Rendez-vous d’abord sur https://aws.amazon.com et créez un compte. Les étapes sont simples à suivre donc je vous laisse faire.
Notez qu’Amazon va vous demander un moyen de paiement, puisqu’à chaque fois que vous utiliserez un service payant, il faudra bien le régler.
Mais rassurez-vous, lorsque vous ouvrez un nouveau compte, certains services (limités, certes) sont gratuits afin que vous puissiez vous faire la main sans soucis !
À noter aussi qu’il est possible de réserver un serveur pour très peu d’argent. À titre informatif, mon site aujourd’hui est hébergé sur AWS et il ne m’en coûte que 10$ par mois. J’y héberge aussi mes applications Shiny, un RStudio Server, un Jupyter Notebook, et encore d’autres services.
Une fois votre compte créé, identifiez-vous, et vous arriverez sur une page avec tous les services proposés par AWS :
Dans le champ de recherche en haut, tapez EC2.
EC2, ça veut dire Elastic Compute Cloud. En français, un nuage élastique qui fait des calculs.
En fait, il s’agit d’un service qui permet de louer des ressources d’un serveur afin de :
AWS dispose d’un service plus adapté pour stocker des données, comme S3, mais quand on veut héberger un site ou faire tourner un modèle, il faut bien stocker les données et les fichiers du site quelque part !
Le gros atout de ce service, c’est qu’on peut très facilement changer la configuration du serveur.
Peut-être qu’aujourd’hui vous avez besoin d’une petite machine pas trop gourmande. Mais demain, quand le monde entier ira visiter votre appli Shiny, vous devrez augmenter la RAM ou la puissance du processeur. C’est possible en seulement quelques clics.
Ou bien imaginez que vous souhaitiez faire tourner un algorithme de Deep Learning mais votre petit laptop n’est pas assez puissant.
Louez une machine bien balèze, importez vos données, faites tourner l’algorithme, exportez vos résultats, éteignez la machine, et hop! le problème est réglé.
Vraiment pratique.
Bref, je parle, je parle, en attendant on a toujours pas de serveur.
Créons le serveur !
Dans le champ de recherche, tapez EC2 et entrez dans le service.
À gauche, dans le menu, cliquez sur Instances. Une instance, c’est un serveur.
Puis ensuite, cliquez sur Launch Instance
Une AMI, c’est une Amazon Machine Image, c’est-à-dire une sorte de serveur pré-packagé avec déjà plein de choses prêtes à l’intérieur. Il y a des AMIs préparées par Amazon, on peut en créer nous-même, et on peut même en louer à des entreprises tierces (par exemple avec des logiciels propriétaires à l’intérieur).
Nous, on va prendre une AMI de base, la Ubuntu Server 18.04 LTS.
Sélectionnez-là en cliquant sur Select.
Le type d’instance correspond à la puissance de votre machine.
Est-ce que vous avez besoin d’une bête de course ou d’une petite machine peu puissante ?
Dans ce guide, inutile d’avoir une grosse machine. En fait, on va prendre le type t2.micro qui est le seul type d’instance éligible à l’offre gratuite d’AWS. Elle contient un processeur avec 1 seul cœur, seulement 1 GB de RAM, et une connexion internet pas ouf mais suffisante.
Pour en savoir plus sur les différents types d’instance qui existent, vous pouvez aller voir la page Amazon EC2 Instance Types qui répertorie toute la liste.
Ensuite, cliquez sur Next: Configure Instance Details.
Dans l’écran suivant, on ne va rien modifier, donc cliquez directement sur Next: Add Storage
Par défaut, AWS choisit de vous allouer 8 GB d’espace sur votre serveur.
À vous de décider si vous avez besoin de plus ou non.
Notez que pour rester dans l’offre gratuite d’AWS, vous devez allouer un maximum de 30 GB.
À présent, la configuration est terminée !
Vous pouvez cliquer sur Review and Launch.
Un écran va vous résumer les différentes options que vous avez choisi. Vous pouvez y jeter un œil puis valider le lancement de votre instance en cliquant sur Launch !
Après avoir cliqué sur Launch, AWS va vous demander de créer une paire de clés. Qu’est-ce que c’est que cette histoire de clés ?
Eh bien il s’agit tout simplement de protéger votre serveur à présent ! Comme il est connecté aux internets, s’il n’y avait pas de clé, tout le monde pourrait y accéder et mettre le bazar !
Pas cool.
Du coup, vous allez créer une clé d’accès sous la forme d’un fichier que vous allez garder sur votre ordinateur. À chaque fois que vous voudrez vous connectez sur votre instance, il faudra spécifier ce fichier et hop vous aurez accès !
Important : Ne perdez pas ce fichier. Sinon, vous risquez de vous voir refuser l’accès à votre propre serveur et vous devrez tout recommencer !
Choisissez un nom pour votre clé, et téléchargez-la.
Félicitations, vous avez créé votre premier serveur !
Après quelques minutes, votre instance sera prête et apparaîtra dans la liste de vos instances.
Prochaine étape : On va voir comment s’y connecter en SSH puis on installer R et R Shiny dessus !
Nous nous sommes donc arrêtés à la création du serveur sur AWS.
Dans cette nouvelle partie, nous allons apprendre à :
Une fois votre instance créée, AWS vous propose un bouton Connect. Bouton qui fait apparaître la fenêtre suivante :
Ce sont les instructions pour se connecter à votre serveur. À présent, on va séparer les instructions selon votre système d’exploitation.
Pour les utilisateurs de Windows
Si vous êtes sous Windows, vous aurez besoin de télécharger un client SSH, puisque l’OS n’en propose pas par défaut. Le plus couramment utilisé est PuTTY que vous pouvez télécharger directement à partir de https://www.putty.org/.
Ensuite, il va vous falloir transformer la clé que vous avez téléchargée dans le chapitre précédent (vous savez, le fichier avec l’extension pem) :
Ensuite, lancez PuTTY puis configurez-le avec les informations données par AWS :
Cliquez sur “Open”, entrez “ubuntu” quand on vous demande login as, puis dites “Oui”, et une fenêtre console va s’ouvrir !
Bravo, vous êtes à présent connecté à votre serveur !
Pour les utilisateurs de Ubuntu ou Mac
Si vous n’êtes pas sous Windows, c’est beaucoup plus simple !
Il vous suffit d’ouvrir une console, de vous déplacer jusqu’au répertoire qui contient la clé pem puis de taper l’instruction donnée par AWS.
Par exemple pour moi, c’est :
Dites “yes” et vous serez connecté.
Si vous obtenez un message d’erreur “WARNING: UNPROTECTED PRIVATE KEY FILE!”, alors il vous faudra changer les droits du fichier avec la commande suivante :
La prochaine étape, c’est d’installer Shiny Server.
Et un premier pré-requis, c’est évidemment d’installer R !
Généralement, les repos d’Ubuntu ne contiennent pas la dernière version de R. Du coup, on va directement ajouter le repo du CRAN de cette manière :
Une fois ces commandes lancées, le repo du CRAN est ajouté. On peut alors installer R :
Une fois R installé, et en préambule à l’installation de Shiny Server, il va falloir installer le package shiny.
Pour ça, lancez R en mode administrateur et installez le package :
Vous pouvez quitter R en tapant q() puis choisissez n.
Ok, on progresse. Il ne reste plus qu’à installer Shiny Server !
Il va falloir aller télécharger la dernière version d’abord. Rendez-vous sur le site de RStudio à l’adresse suivante : https://www.rstudio.com/products/shiny/download-server/ et on va vous donner les instructions tout en bas de la page.
Pour moi, il s’agit de :
A priori les deux dernières lignes seront différentes pour vous si une nouvelle version de Shiny Server sort entre-temps.
Une fois les commandes lancées, dites “oui”, et c’est parti !
Ça y est !
Vous ne le savez pas encore, mais vous avez une application Shiny qui tourne sur votre serveur.
Comment y accéder ?
C’est ce qu’on voit dans la partie suivante…
Rappelons les étapes déjà réalisées.
D’abord, on a créé une application Shiny et on l’a envoyée sur Gitlab. Ensuite, on a créé un compte sur AWS pour ouvrir un serveur, sur lequel on a préparé le terrain en installant R et R Shiny.
À la fin du précédent chapitre, je vous disais que vous aviez déjà une appli Shiny qui tourne sur votre serveur !
En effet, elle est apparue en installant Shiny Server.
Pour y accéder, deux étapes :
Trouver l’adresse IP de votre serveur
Dès que vous activez votre serveur sur AWS, une adresse IP y est affectée automatiquement.
Attention: L’adresse IP change dès que vous éteignez/rallumez le serveur ! Si vous voulez avoir une IP fixe, il faudra aller voir dans Elastic IP et allouer une adresse IP directement à votre serveur.
Dans votre dashboard sur EC2, il y a une colonne IPv4 Public IP.
Repérez-la et notez l’adresse IP.
Chez moi, c’est 3.121.42.9
Pour accéder à l’application Shiny, il suffit alors simplement de taper l’adresse IP dans votre navigateur préférée en spécifiant le port réservé à Shiny : 3838
Sauf que… si je fais ça, la page ne se charge pas.
Ouvrir le port 3838 sur le pare-feu
En effet : Par défaut, AWS n’autorise que le port 22, c’est-à-dire le port réservé à SSH, le protocole qu’on a utilisé dans le chapitre précédent pour se connecter au serveur via la console.
C’est une mesure de sécurité.
Pour changer ça, à partir du dashboard EC2 :
Type | Protocol | Port Range | Source | Description |
---|---|---|---|---|
SSH | TCP | 22 | Custom: 0.0.0.0/0 | |
Custom TCP Rule | TCP | 3838 | Custom: 0.0.0.0/0 |
Voici quelques screenshots si jamais vous êtes perdu :
.
Une fois que vous avez ouvert le port 3838, l’application devient accessible !
Ré-essayons d’y accéder :
Cette fois-ci…
Boom !
Ça marche ! Top cool !
Note : L’adresse est un peu moche pour l’instant. D’abord il faut taper une adresse IP, et ensuite un numéro de port. Dans le chapitre suivant, on verra comment avoir une belle adresse internet et se débarrasser du numéro de port.
C’est une page html avec des widgets shiny sur le côté droit de la page.
D’ailleurs, vous noterez peut-être un message d’erreur dans un des widgets : “An error has occurred”. C’est tout simplement un problème de package qui n’est pas installé.
Ça c’est chouette mais.. le but c’est de mettre notre application maintenant !
2. Comment ajouter votre application sur votre serveur ?
Pour ça, on va devoir retourner dans la console sur notre serveur.
Si vous avez déjà oublié comment on faisait, retournez voir le chapitre précédent Installer R et R Shiny sur le serveur.
Dans un premier temps, on va explorer un peu notre serveur.
Explorer le serveur
On vient tout juste de voir que quand on installe Shiny Server pour la première fois, une application par défaut s’installe et nous souhaite la bienvenue !
Où se trouve cette application ?
Elle se trouve tout simplement dans le dossier /srv/shiny-server/
, et c’est là que seront stockées toutes vos applications.
Tapez les commandes suivantes dans votre console :
Vous allez voir juste un seul dossier : sample-apps
, et un fichier : index.html
.
Le fichier index.html
correspond à ce que nous avons vu lorsque nous sommes allés nous connecter sur la page web de notre serveur, et il fait appel à l’application qui se trouve dans sample-apps
. Si on va voir dedans :
On retrouve nos fidèles server.R
et ui.R
qui sont la base de toute application Shiny !
Par défaut, Shiny Server va aller chercher les applications qui se trouvent dans ce dossier. Il est possible de le changer à partir du fichier de config qui se trouve dans /etc/shiny-server/shiny-server.conf
mais je vous le déconseille.
Ce qu’on va plutôt faire, c’est :
/srv/shiny-server/
.C’est généralement la pratique qui est recommandée.
Alors au travail !
D’abord, on retourne dans le dossier de base :
Ensuite, on clone notre application qui se trouve sur Gitlab :
Parfait ! Cette simple commande nous a permis de télécharger l’appli sur le serveur.
Sauf que Shiny s’attend à ce que l’appli se trouve dans le dossier bien spécial /srv/shiny-server/
. Donc on va créer un raccourci là-bas :
/srv/shiny-server/
À présent, si on affiche la liste des fichiers, on va voir que notre raccourci a bien été créé :
D’ailleurs, au passage, je vous propose de supprimer l’appli installée par défaut :
Eh bien ça y est !
On a ajouté notre application dans le bon dossier, à présent elle devrait être accessible sur les internets.
Testons !
Si je tape 3.121.42.9:3838
dans la barre de mon navigateur, je tombe sur…
Oops, pas très joli ça. On va voir après comment le changer.
Cliquons sur le lien, et…
ERROR: An error has occurred. Check your logs or contact the app author for clarification.
Eh :(
…
Bon.
Il semblerait que le serveur reconnaisse bien qu’on a ajouté notre application, mais elle ne marche pas !
Pourquoi ?
3. Comment configurer Shiny Server ?
Il va falloir changer un petit peu la config.
Le fichier de configuration de Shiny Server se trouve dans /etc/shiny-server/shiny-server.conf
.
On va l’ouvrir et essayer de comprendre :
Le fichier ressemble à ça :
# Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;
# Define a server that listens on port 3838
server {
listen 3838;
# Define a location at the base URL
location / {
# Host the directory of Shiny Apps stored in this directory
site_dir /srv/shiny-server;
# Log all Shiny output to files in this directory
log_dir /var/log/shiny-server;
# When a user visits the base URL rather than a particular application,
# an index of the applications available in this directory will be shown.
directory_index on;
}
}
Je vais vous expliquer ligne par ligne comment le comprendre.
run_as shiny
indique l’utilisateur qui est derrière le Shiny Server. Quand vous vous connectez au serveur via SSH, vous dirigez l’utilisateur ubuntu
, celui par défaut. Quand on a installé Shiny Server, il a créé un nouvel utilisateur, qui s’appelle shiny
, qui est celui qui va faire tourner le serveur. C’est important pour la suite.listen 3838
, ça veut dire qu’il faut spécifier le port 3838 pour accéder à l’appli. C’est justement ce qu’on a fait quand on a tapé l’adresse dans notre navigateur.site_dir /srv/shiny-server
indique l’endroit où se trouvent les applis Shiny. Et effectivement, c’est là qu’on a placé la notre. En tout cas, on y a mis un raccourci.log_dir /var/log/shiny-server
spécifie l’endroit où se trouve les logs. Ça c’est super utile, puisque ça va nous permettre de comprendre pourquoi notre appli a planté. On va aller les voir juste après.directory_index on
, comme le commentaire l’indique, permettre de montrer l’arborescence des dossiers à la racine. C’est ce qu’on voit quand je vais directement sur 3.121.42.9:3838
. Moi je trouve ça un peu moche, du coup j’ai tendance à le désactiver : directory_index off
. En plus, c’est mieux si on préfère ne pas montrer toutes les applis dont on dispose.Outre cette modification optionnelle pour la dernière ligne, je vous invite à rajouter une ligne tout en haut du fichier de config :
Cette ligne permet de garder les logs en toute circonstance.
En effet, Shiny a la mauvaise habitude de supprimer les logs quand il considère qu’il ne s’est rien passé d’intéressant. Et parfois, notre appli bug, on veut voir les logs… et ils ne sont pas là ! Cette ligne permet d’éviter ce problème.
Important : Lorsque vous changez la config, il faut recharger le Shiny Server avec la commande suivante afin que les changements soient pris en compte :
D’ailleurs, en parlant de logs… allons voir pourquoi notre appli plante !
Pour aller voir les logs :
Je vois deux fichiers dans la liste.
Celui nommé movie-explorer
correspond à notre application. L’autre est l’application de base par défaut.
Les chiffres dans les noms des fichiers correspondent à la date. Donc le premier fichier a été créé le 2018-12-10 à 08:05:34. Quand les fichiers de logs commencent à s’accumuler, c’est pratique pour s’y retrouver.
Si jamais vous ne voyez pas de fichier, essayez de relancer l’application. C’est le problème des logs qui disparaissent que je mentionnais juste avant. Mais il sera résolu avec la nouvelle config.
J’affiche les dernières lignes du fichier de log et je vois :
Mais oui !
On n’a pas installé les libraries nécessaires pour le fonctionnement de l’appli.
C’est évident maintenant qu’on le voit.
Mais dites-vous que c’est une source d’erreur très fréquente. On rajoute une librairie dans une appli, tout roule bien en local, on met à jour, et là… ça plante, on comprend plus pourquoi. Juste parce qu’on n’a pas pensé à installer le package sur le serveur.
Attention : Comme le Shiny Server est géré par l’utilisateur shiny
, il faut installer les packages avec cet utilisateur. On peut changer d’utilisateur en tapant les commandes suivantes :
Il va vous demander si vous voulez utiliser une bibliothèque locale, répondez que oui.
Une fois le package installé, ré-essayez de charger l’application.
Cette fois-ci, vous allez voir l’appli apparaître, et très rapidement elle crash (elle devient grisée).
Encore raté !
Si on retourne rapidement dans les logs, on s’aperçoit qu’un nouveau fichier s’est créé, et il nous indique :
Il manque un autre package ! dbplyr
.
Note: Après avoir installé le package pour l’utilisateur shiny
, n’oubliez pas de revenir à l’utilisateur principal ubuntu
en tapant exit
dans la console.
Je vous laisse installer le package, vous savez comment faire, à présent. Bon, et puis autant vous le dire tout de suite, il faut aussi installer RSQLite
.
Une fois installés…
Finalement !
On y arrive.
Note : J'en profite pour vous dire que si vous essayez d'installer dplyr
, il faudra un serveur avec au moins 2 Go de ram.
C'est seulement pour l'installation, donc avec AWS vous pouvez très bien upgrader votre serveur puis le downgrader une fois l'installation terminée.
Après avoir :
Enfin on peut accéder à notre appli depuis n’importe où dans le monde ! Et surtout, on peut la partager à d’autres personnes.
Par contre, vous avez peut-être encore quelques questions, comme…
C’est ce que nous voyons dans les prochains chapitres.
Dans la partie 4 de ce guide, nous avons déployé l’appli Shiny sur le serveur AWS.
Rappelez-vous, on pouvait taper 3.121.42.9:3838
dans le navigateur et boum notre appli apparaissait !
Plutôt cool !
Je vous mets un petit rappel des choses importantes qu’on a vues jusqu’ici pour manipuler le serveur :
/srv/shiny-server
./var/log/shiny-server
./etc/shiny-server/shiny-server.conf
.Ce sont les trois importants dossier à se rappeler.
Au bout d’un moment ça rentre, mais d’ici là, n’hésitez pas à repasser ici quand vous en avez besoin.
Alors..
Comme je disais juste au-dessus, on peut maintenant visiter notre appli juste en tapant 3.121.42.9:3838
.
Oui alors ok, sauf que qui va s’amuser à se rappeler tous ces numéros ?
Votre client ?
Sans doute pas.
Non en fait, dans le monde réel, personne ne visite jamais un site web en tapant l’adresse IP.
Par exemple, moi je mets toutes mes applis Shiny sur shiny.datachamp.fr.
C’est facile à se rappeler et ça fait du sens.
D’ailleurs j’ai hébergé notre appli à l’adresse suivante : https://shiny.datachamp.fr/051-movie-explorer/
Alors comment on fait ?
Trois étapes :
Si le vocabulaire utilisé vous fait un peu peur.. pas d’inquiétude !
Comme d’habitude, je vais vous guider étape par étape, avec des screenshots et tout et tout!
La toute première étape, pour utiliser un nom de domaine, c’est.. d’en avoir un !
Ça veut aussi dire en acheter un.
Alors oui.
Ça coûte des sous en général.
Par exemple, le mien (charlesbordet.com) me coûte $14.99 par an.
Les .fr
coûtent moins chers que les .com
en général. J’en ai un qui me coûte 6.99€ par an par exemple.
Vous pouvez aussi trouver des noms de domaine un peu exotiques vraiment pas chers à moins de 2€ par an.
J’achète les miens sur Hover.com. Ils sont spécialisés exclusivement sur les noms de domaine.
Ça fait des années que j’en ai chez eux et ça marche toujours bien.
Vous pouvez aussi utiliser OVH qui vend les .fr
.
Du coup au niveau des screenshots je vais utiliser Hover, mais que ce soit OVH ou les autres, vous aurez à peu près la même interface.
Bon.
Alors supposons que vous soyez allé acheter votre nom de domaine.
Maintenant il faut faire le lien entre:
Comment on fait ça ?
Avec les DNS !
DNS, ça veut dire Domain Name System.
C’est un des services fondamentaux des internets.
Et probablement aussi un des plus ignorés.
C’est ce qui vous permet de taper https://duckduckgo.com dans votre navigateur au lieu de l’adresse IP 23.21.193.169.
En gros, c’est ce qui rend l’internet un peu plus humain.
Et c’est exactement ce qu’on veut faire avec notre appli !
On veut que les gens tapent http://monapplishinysupercool.fr plutôt que 3.121.42.9:3838
.
Votre vendeur de nom de domaine va vous donner accès à une interface où vous pouvez justement paramétrer ces DNS.
Sur Hover, il suffit de cliquer sur DNS :
Il y a déjà des valeurs par défaut qui sont entrées (peut-être différentes des vôtres, peu importe).
Si vous voulez comprendre comment ça marche, je vous invite à lire la boîte bleue ci-dessous. Sinon, passez directement à la suite.
Chaque entrée DNS indique comment rediriger les utilisateurs quand ils tapent votre nom de domaine.
Alors pourquoi avoir plusieurs entrées ?
Parce que vous pourriez avoir datachamp.fr
sur un premier serveur, puis shiny.datachamp.fr
sur un autre serveur, et ainsi de suite. Pour chaque cas d'usage, il faut ajouter une nouvelle ligne.
La colonne TYPE peut prendre les valeurs suivantes : A pour rediriger le nom de domaine vers une adresse IP. Ou CNAME pour rediriger le nom de domaine vers un autre nom de domaine. Ou MX pour gérer les emails.
La colonne HOST correspond au sous-domaine. Un sous-domaine, c'est la partie avant le domaine. Par exemple, quand j'écris shiny.datachamp.fr
, c'est un sous-domaine de datachamp.fr
. On peut mettre une étoile * pour représenter tous les sous-domaines. Et on peut mettre un @ pour le domaine principal (donc datachamp.fr
dans mon cas).
La colonne VALUE dépend du TYPE qu'on a choisit. Si on a choisit le TYPE A, alors il faut entrer une adresse IP.
Le reste n'a pas vraiment d'importance pour nous aujourd'hui.
Les valeurs par défaut de Hover indiquent ici que le domaine principal datachamp.fr
et tous les sous-domaines *.datachamp.fr
vont être redirigés vers l’adresse IP 64.98.145.30.
C’est pas tout à fait ce qu’on veut.
Moi j’aimerais avoir seulement le sous-domaine shiny.datachamp.fr
redirigé vers l’adresse IP de mon serveur AWS.
Pour faire ça je retire les deux lignes de TYPE A, et j’en ajoute une nouvelle :
Si vous ne voulez pas utiliser un sous-domaine mais directement votre domaine principal, alors écrivez @
au lieu de shiny
dans la case hostname.
Du coup à la fin j’ai un tableau qui ressemble à ça :
À présent, à chaque fois que quelqu’un tape shiny.datachamp.fr
dans son navigateur, il sera redirigé automatiquement vers mon serveur AWS !
Top !
Mais c’est pas fini.
Si vous avez essayé, vous vous êtes aperçu que ça ne marchait pas.
Pourquoi ?
Parce qu’on n’a pas spécifié le port 3838 !
En fait, on a toujours besoin d’écrire shiny.datachamp.fr:3838
pour accéder à l’appli.
Uh…
C’est pas terrible ça.
On n’a jamais vu une URL avec des chiffres au bout comme ça. Ça fait pas sérieux.
Bon.
On s’approche quand même.
Il y a deux méthodes pour résoudre ce problème :
La méthode rapide et sale
Si vous voulez juste expérimenter et vous avez besoin d’une solution rapide, c’est facile :
sudo nano /etc/shiny-server/shiny-server.conf
.listen 3838
en listen 80
.sudo systemctl restart shiny-server
.Et voilà !
Note: N’oubliez pas d’ouvrir le port 80 au niveau du pare-feu de AWS. Voir le chapitre Ouvrir le port sur le pare-feu.
Le changement, c’est que le serveur Shiny écoute sur le port 80 au lieu de 3838.
Pourquoi 80 ?
80 c’est le port par défaut pour le protocole HTTP (celui que tous les sites web utilisent).
En fait, quand on tape shiny.datachamp.fr
dans le navigateur, on utilise implicitement le port 80 sans avoir besoin de le spécifier.
Et si on veut spécifier un autre port, comme 3838, il faut l’écrire explicitement.
Du coup, ça marche bien..
Alors pourquoi “rapide et sale” ?
En fait, si demain vous avez besoin d’installer un autre service sur votre serveur, comme un serveur RStudio, un Jupyter notebook, ou même un site web, vous aurez le même problème.
Et le problème, c’est que seulement un seul service à la fois peut utiliser le port 80.
Du coup, si vous avez à la fois un Shiny Server et un RStudio Server, ils doivent forcément utiliser deux ports différents.
On ne peut pas tous les deux les mettre sur le port 80.
Alors que vous aimeriez bien avoir deux URL bien clean : shiny.datachamp.fr
et rstudio.datachamp.fr
.
La solution, c’est d’utiliser un reverse proxy qui s’appelle Nginx.
Bon.
On entre dans des considérations un peu plus compliquées.
Je pourrais juste vous donner les commandes à écrire, et puis youplaboum article terminé.
Sauf que c’est pas tout à fait ma méthode d’enseignement.
Donc je vais tout vous expliquer.
En plus comme ça si demain vous avez besoin de changer quelque chose, vous comprendrez ce que vous faites.
Commençons par..
C’est quoi le rivet sporxi ?
Reverse proxy.
Voyez votre serveur comme une grande maison qui a plein de portes.
65535 portes exactement.
Et au lieu de dire portes, on va dire ports, ok ?
Si vous voulez parler à quelqu’un dans cette maison, il faut passer par la bonne porte.
Par exemple, M. Shiny Server est dans la maison, et il habite à la porte 3838.
Et puis M. RStudio Server est à la porte 8080.
Mais si vous allez à la porte 3322, vous ne trouverez personne, parce que personne n’y habite.
Ça ça marche.
Mais c’est pas pratique parce qu’il faut à chaque fois indiquer la bonne porte.
Comme on disait avant, il faut spécifier le numéro du port dans l’URL, comme dans shiny.datachamp.fr:3838
.
Ce qui serait pratique, c’est si on avait Nestor à l’entrée principale qui pourrait nous guider directement à la bonne personne.
Du coup, pas besoin de spécifier une porte particulière, on va juste à l’entrée principale, on demande M. Shiny Server, et il vient à nous !
Bien mieux.
Et ce Nestor, en fait il s’appelle Nginx, et c’est ce qu’on appelle un reverse proxy.
Au lieu de taper le numéro de port, on tape seulement shiny.datachamp.fr
, et le premier interlocuteur au niveau du serveur sera Nginx.
Nginx va comprendre qu’on veut accéder au Shiny Server, et il va nous guider au bon endroit.
Techniquement, il nous reste donc seulement à :
Installer Nginx
Ça c’est l’étape facile.
Connectez-vous en SSH sur votre instance AWS (de nouveau, voyez la partie 3 si vous avez oublié), puis tapez :
Et Nginx est installé.
Facile !
Configurer Nginx
Pour la partie un peu plus délicate..
D’abord, changez de répertoire pour aller dans les dossiers de Nginx :
La commande ls
permet de faire la liste de tous les fichiers et dossiers dans ce répertoire.
Ce qui nous intéresse, c’est le fichier nginx.conf
, mais on ne va pas y toucher.
À la place, on va rajouter un nouveau fichier séparé dans sites-available
:
Et c’est là qu’on va écrire la configuration :
C’est pas très long, mais ça peut faire un peu peur.
listen
Au début, on dit que le serveur Nginx écoute sur le port 80.
Comme il n’y a qu’un seul service qui peut être sur ce port, on va mettre Nginx, et c’est lui qui va nous rediriger vers tous les autres services.
En gros, c’est notre Nestor à l’entrée principale de la maison.
server_name
La partie server_name
indique ce que l’utilisateur tape dans son navigateur.
Ici je veux que l’utilisateur accède à l’appli Shiny sur le sous-domaine shiny.datachamp.fr
.
Évidemment, remplacez la valeur avec votre nom de domaine.
location
Et enfin, tout ce qui est dans location
correspond au reverse proxy.
On donne les instructions à Nestor :
Si l’utilisateur arrive et demande shiny.datachamp.fr
, alors guide-le dans la maison à la porte 3838.
Et c’est tout !
Vous pouvez même réutiliser ce fichier de config pour un RStudio Server.
Ajoutez un deuxième fichier nommé rstudio.conf
et copiez/collez tout. Il faut remplacer deux choses :
server_name
..
C’est presque fini mais il reste un petit détail.
Enregistrez le fichier et retournez dans le terminal.
Il faut qu’on crée un raccourci dans le dossier sites-enabled
:
Pourquoi on fait ça ?
En fait, Nginx ne regarde pas dans le dossier sites-available
.
Il regarde seulement le dossier sites-enabled
.
Donc on crée le fichier de config dans sites-available
, et ensuite on active la config en créant un raccourci dans sites-enabled
.
L’idée c’est que si demain vous souhaitez temporairement désactiver l’accès à Shiny, il suffit seulement de supprimer le raccourci.
Alors que sinon, il faudrait supprimer tout le fichier de config, et après c’est pas vraiment pratique pour le remettre.
Ok.
Il ne reste plus qu’à redémarrer Nginx pour que les changements soient pris en compte.
D’abord :
Ça permet de vérifier si la syntaxe du fichier de config est correcte.
Si jamais vous avez une erreur du type nginx: [emerg] unknown "connection_upgrade" variable
, c’est que les variables $http_upgrade
et $connection_upgrade
n’ont pas été déclarées.
Dans ce cas, ouvrez le fichier /etc/nginx/nginx.conf
puis ajoutez les lignes suivantes à l’intérieur du bloc http :
Ensuite réessayez de valider la syntaxe avec sudo nginx -t
.
Puis :
Bravo !
Votre appli Shiny est à présent accessible à un vrai nom de domaine du type https://shiny.datachamp.fr/051-movie-explorer/ !
C’était pas très facile à la fin, mais c’est vraiment la meilleure manière de procéder.
En plus, il y a d’autres avantages..
Comme par exemple la possibilité de sécuriser l’appli en HTTPS !
On a maintenant changé l’adresse IP un peu moche de notre appli Shiny en bon nom de domaine et on est capable d’accéder à l’application en tapant une URL du type http://shiny.datachamp.fr/051-movie-explorer.
Très cool.
Sauf qu’il reste un vrai problème si vous voulez mettre votre application en production pour un client.
L’appli n’est pas sécurisée.
Vous avez peut-être remarqué le message sympa que vous laisse votre navigateur :
Votre application utilise en effet le HTTP, un protocole de communication non chiffré, alors qu’aujourd’hui, tout le monde utilise HTTPS, l’équivalent chiffré et sécurisé.
Dans ce chapitre, on va voir :
Vous en avez sans doute entendu parler sans savoir exactement comment le https fonctionnait ni pourquoi il était autant nécessaire.
Imaginez qu’à chaque fois que vous vous connectez à un site web, vous envoyez une requête de chez vous jusqu’au data center où le serveur est hébergé, puis une réponse revient chez vous.
Ces messages font parfois le tour du monde et passent de main en main, de routeur en routeur, avant d’arriver à destination.
Quand on utilise le HTTP, ces messages ne sont pas chiffrés.
Donc n’importe qui peut à tout moment intercepter le message, l’ouvrir, le lire, et même le modifier avant de le renvoyer sans que personne ne le sache.
On appelle ça une Man-in-the-Middle attack .
Pas très cool, d’autant plus quand le message contient des mots de passe, des data confidentielles ou votre code de carte bleue.
C’est pour éviter ça que le HTTPS est apparu.
Le HTTPS garantit que les messages seront chiffrés. Si une personne intercepte le message, elle ne pourra pas le lire.
Au départ, le HTTPS était surtout utilisé par les commerces en ligne pour sécuriser vos informations bancaires lors de transactions, puis il a été généralisé à l’Internet entier.
C’est pour ça que votre navigateur vous met un gros panneau “ATTENTION DANGER !!!”.
Bref, si vous voulez :
On va devoir utiliser le HTTPS.
Comment ça se passe ?
Il faut d’abord obtenir des certificats (SSL/TLS) pour le serveur et des clés.
Je vous passe les détails techniques de cryptographie, mais en gros le certificat permet de dire « Oui c’est bien moi le vrai serveur ! » et il contient une clé de chiffrement.
Les certificats s’obtiennent auprès d’institutions de confiance.
La plupart du temps, c’est payant.
Si on utilise Let’s Encrypt, c’est gratuit et automatique.
Donc je vous propose d’utiliser Let’s Encrypt.
Il existe même un programme qui permet de renouveler les certificats automatiquement (parce qu’ils ont une date de péremption, oui).
Donc on le fait une fois, et après on est tranquille !
Pour commencer, allons sur le site de Cerbot:.
Dans les cases en haut, on choisit :
Ensuite descendez dans la page et copiez/collez les instructions dans la console de votre serveur. Vous pouvez jeter un œil sur le chapitre Installer R et R Shiny sur le serveur) si vous avez déjà oublié comment vous connecter à votre serveur.
Donc :
L’obtention du certificat est ensuite automatique en tapant la commande suivante :
Évidemment, modifiez le nom de domaine pour renseigner le vôtre.
Quelques lignes vont s’afficher en parlant de challenges :
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for shiny.datachamp.fr
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/shiny.datachamp.fr/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/shiny.datachamp.fr/privkey.pem
Your cert will expire on 2020-02-04. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
Les lignes les plus importantes sont celles qui vous informent que les certificats ont été enregistrés dans le dossier /etc/letsencrypt/live/shiny.datachamp.fr
.
On va pouvoir s’en servir pour passer notre appli en HTTPS !
Pour mettre en place ce qui suit, il faut que vous ayez suivi les recommandations du précédent chapitre de ce guide sur la création d’un nom de domaine, notamment la dernière section : Mettre en place un reverse proxy.
Si vous ne l’avez pas encore mis en place, je vous invite à le faire et revenir ensuite.
Dans cette section, on avait installé le logiciel nginx
.
Ce logiciel, c’est tout simplement ce qu’on appelle un serveur web. L’une de ses fonctionnalités, c’est le reverse proxy (ce qu’on avait donc vu la dernière fois).
Rappelez-vous, on avait dit que nginx
agissait comme le gardien du serveur : il écoute sur le port 80, et il redirige les visiteurs vers le bon port (3838 pour Shiny par exemple).
Le port 80, c’était pour le protocole HTTP.
Comme on va passer en HTTPS, ce port va changer : à la place, on va utiliser le port 443.
Chaque protocole a son port dédié, ce n’est pas nous qui pouvons décider. Par exemple, SSH est sur le port 22, FTP sur le port 21, etc.
Vous pouvez trouver une liste de ces ports logiciels sur Wikipédia.
Ils sont pris jusqu’au port 1024, et tout ceux qui sont au-delà sont libres. C’est pour cette raison que Shiny s’octroie le port 3838 par défaut (mais on pourrait le changer).
Bref.
Reprenons la configuration de nginx
. On va changer le port 80 en 443.
Pour rappel, le fichier de configuration se trouve au chemin suivant :
Vous devriez retrouver le fichier qu’on avait écrit la dernière fois.
On va rajouter et modifier quelques points.
1. Changement de port
D’abord, on change le port.
Les deux lignes qui commencent par listen
vont être remplacées par celles-ci :
2. Configuration du SSL
On va ensuite ajouter les paramètres de SSL.
SSL, c’est le protocole qui permet de chiffrer vos données. Dites-vous que HTTP + SSL = HTTPS.
Normalement, depuis 20 ans, on dit TLS, mais tout le monde continue de dire SSL.
Je vous le dis tout de suite : je ne suis pas un expert de cryptographie ni de SSL.
Donc quand il s’agit de configurer de manière optimale, je vais chercher l’information d’une source sûre.
Ici, j’utilise les recommandations de Mozilla. Ils ont créé un SSL Configuration Generator.
On va donc rajouter les lignes suivantes dans notre fichier de config :
Dans cette config, il va falloir modifier certaines choses.
On commence avec les deux premières lignes ssl_certificate
et ssl_certificate_key
. Il faut leur donner les chemins des certificats générés plus tôt.
Je les modifie ainsi :
(Bien entendu, remplacez avec votre nom de domaine)
3. Télécharger les paramètres DH
On va ensuite s’attaquer aux paramètres DH. Le commentaire nous dit de les télécharger à partir d’une URL. C’est donc ce qu’on va faire avec l’instruction suivante dans la console :
Et il faut ensuite modifier le chemin spécifié dans ssl_dhparam
:
Je vous conseille de désactiver la partie HSTS pour l’instant.
Pourquoi ? Car le HSTS va permettre de dire au navigateur : « À partir d’aujourd’hui, et pendant deux ans, tu utilises toujours le HTTPS et jamais le HTTP. ».
C’est une bonne pratique de sécurité.
Sauf que si vous avez des problèmes pour mettre en place le HTTPS et que vous souhaitez revenir au HTTP, vous allez être coincé.
Donc désactivons-le pour le moment, et lorsqu’on sera sûr que la config fonctionne bien, on le remettra. Je rajoute simplement un #
devant :
4. Utiliser le certificat Let’s Encrypt
Il ne nous reste plus qu’à modifier la dernière ligne avec un certificat de Let’s Encrypt :
Et voili voilou !
À présent, enregistrez la configuration, on va tester si tout fonctionne.
Pour vérifier que vous n’avez pas fait d’erreur, tapez dans la console :
Vous devriez recevoir le message suivant :
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Si ce n’est pas le cas, c’est que vous avez fait une erreur quelque part.
Une fois la syntaxe validée, on redémarre le serveur nginx :
À présent, il ne reste plus qu’à tester.
Rendez-vous sur votre nom de domaine en tapant bien https au début, par exemple : https://shiny.datachamp.fr/051-movie-explorer.
Note: N’oubliez pas d’ouvrir le port 443 au niveau du pare-feu de AWS. Voir le chapitre Ouvrir le port sur le pare-feu.
Votre navigateur va vous afficher un beau cadenas avec un message rassurant :
Bravo !
Mais ce n’est pas terminé. Il nous reste encore une dernière étape : interdire le HTTP et forcer l’utilisation du HTTPS.
Un peu plus haut, je vous ai recommandé de désactiver le HSTS (l’utilisation automatique du HTTPS).
On va à présent réactiver cette option.
Dans la config nginx, on retire le #
ajouté plus tôt :
Et on va faire une deuxième modification.
Au niveau du générateur de configuration SSL de Mozilla, vous avez peut-être remarqué un autre bloc server
ajouté en haut de leur fichier.
Ce bloc serveur va écouter sur le port 80, c’est-à-dire HTTP, et automatiquement rediriger vers le port 443.
Il permet aux utilisateurs qui viennent sur votre appli en HTTP de ne pas tomber sur une page 404 (ben oui, parce que plus personne n’écoute sur le port HTTP à présent).
À la place, on leur dit gentiment d’aller voir en 443 si on y est.
Donc tout en haut du fichier de config nginx, on va rajouter les lignes suivantes :
Et là, c’est bon. On a activé le HTTPS, notre appli est sécurisée, et les données qu’on envoie sont chiffrées.
N’oubliez pas de redémarrer nginx
à chaque fois que vous faites un changement :
Vous aurez remarqué que plus on avance dans ce guide, plus on s’éloigne de R et plus on rentre dans le domaine de l’administration système.
Perso, je trouve que c’est un domaine super intéressant.
Grâce à Shiny, j’ai appris énormément sur les serveurs et même sur le fonctionnement des internets en général. Et ce n’est pas fini !
Cette fois-ci, on va apprendre à protéger notre application derrière un portail d’authentification.
La plupart des applications Shiny que j’ai eu l’occasion de créer pour mes clients étaient des applications internes.
Ça veut dire qu’on utilise des données de l’entreprise, confidentielles, et le dashboard est utile à une équipe en particulier. Mais ce n’est pas fait pour être ouvert au grand public.
Dans certaines entreprises, on dispose d’un réseau interne, parfois d’un VPN, et alors c’est facile d’isoler le dashboard de l’internet public.
Mais parfois, ce n’est pas possible.
Ou alors, on veut donner l’accès à un public plus large, et on veut pouvoir gérer des utilisateurs et des droits d’accès.
Les besoins sont multiples.
Dans ce chapitre, on va voir comment répondre à ces besoins, du plus simple au plus complexe :
nginx
Avant de commencer, j’aimerais ajouter un petit avertissement.
Dans ce chapitre, on va parler sécurité. Je ne suis pas un expert en sécurité informatique, c’est un vrai métier et ce n’est pas le mien.
Vous restez responsable de la sécurité de vos applications.
Ce chapitre est purement informatif!
À présent que ceci est précisé, allons-y !
Dans un premier temps, je vais vous présenter succinctement les choix à votre disposition. Je vais ensuite décrire comment mettre en place chacun d’eux.
En gros, vous avez le choix entre :
Utiliser Shiny Server Pro
Haha.
Je plaisante.
Utiliser votre serveur web nginx
Dans les précédents chapitres, on a utilisé nginx
comme reverse proxy.
nginx
agit comme un gardien à l’entrée du serveur et redirige les visiteurs vers le bon service, comme notre Shiny Server en l’occurrence.
Une étape supplémentaire logique serait d’utiliser ce gardien pour autoriser ou refuser certains visiteurs.
Plutôt que de laisser passer tout le monde, il pourrait demander un mot de passe aux visiteurs et ne donner l’accès qu’à ceux disposant d’identifiants valides.
C’est une solution qui marche très bien, qui est très facile à mettre en place, et qui offre en plus une bonne sécurité puisqu’une partie du code de l’application n’est pas chargée tant qu’on n’est pas authentifié.
Utiliser un service tiers comme auth0
auth0
, c’est une surcouche d’authentification que vous pouvez intégrer dans n’importe quelle application.
Vous le mettez en place, et c’est lui qui se chargera de gérer les utilisateurs et les droits d’accès.
C’est gratuit jusqu’à 7000 utilisateurs (pas mal !).
Et l’outil s’intègre automatiquement avec Shiny.
Le gros avantage, c’est que vous n’avez pas à vous soucier de la sécurité.
Ce sont eux qui s’en occupent, c’est leur spécialité, donc on peut imaginer qu’ils font ça mieux que nous.
Maintenant, ça reste un service tiers, ce qui crée une dépendance pas toujours désirable.
Utiliser une solution maison
Finalement, la dernière alternative que je vous propose : le faire vous-même.
Si vous êtes arrivé jusqu’ici dans ce guide, c’est que vous aimez quand même un peu vous faire du mal faire les choses par vous-même.
Il est important de noter toutefois que cette solution est celle qui apporte le moins de sécurité, puisque vous n’êtes pas un expert en sécurité, et moi non plus.
En plus, on ne sait pas trop si Shiny sécurise correctement les données ou non.
Imaginez par exemple que vous ajoutiez un portail d’authentification au début de votre appli Shiny.
Et ensuite, vous écrivez dans le code quelque chose du genre : « Si l’utilisateur se login, on montre le reste de l’application. »
Du coup, ça implique d’accéder au Shiny Server pour pouvoir se logger.
Mais est-ce que le reste du code est caché tant qu’on n’est pas loggé ?
Est-ce qu’il n’y a pas moyen de manipuler la console Javascript ou une autre backdoor pour accéder aux données ?
Est-ce que votre formulaire d’authentification est résistant à l’injection SQL ?
Les réponses à ces questions restent un peu floues, et même si on s’assure de pouvoir répondre à certaines, on ne sera jamais sûr à 100% d’avoir tout sécurisé correctement.
De mon point de vue, le gros problème, c’est qu’on a accès au Shiny Server pour pouvoir se logger. On charge déjà du code Shiny.
Dans la solution du serveur nginx
, par exemple, le Shiny Server n’est pas accessible tant qu’on n’a pas passé l’étape d’authentification.
Et c’est généralement une bonne pratique de séparer le serveur d’authentification (le rôle que joue nginx
par exemple) et le serveur applicatif.
Mélanger les deux, c’est chercher des ennuis.
Donc cette solution est très bien si vous voulez restreindre l’accès sans avoir une sécurité au top level.
Elle est relativement simple à mettre en place, d’autant plus que vous connaissez déjà Shiny, donc c’est très flexible si vous voulez créer des comptes admin, gérer des droits utilisateurs, etc. Ce que ne permet pas nginx
.
Mais gardez en tête que faire appel à un vrai professionnel de la sécurité peut être une bonne idée aussi.
À présent, je vais détailler chacune de ces solutions.
nginx
comme serveur d’authentificationCette solution est la plus simple à mettre en place, mais est aussi la moins flexible.
En gros, vous allez d’abord choisir et enregistrer les logins et mots de passe au niveau du serveur.
Ensuite, vous aurez un popup pas très accueillant à chaque connexion.
C’est un peu la solution rapide et pas chère.
Mais c’est très efficace si vous avez une appli que vous voulez montrer seulement à certaines personnes.
Par exemple, quand vous développez une application pour un client et que vous voulez lui montrer sans la rendre accessible au grand public. Généralement, le plus simple est de la mettre sur votre serveur, de paramétrer un mot de passe et de la partager à votre client.
De nouveau, on va devoir mettre les mains dans notre serveur et sa console.
D’abord, les prérequis : vous devez avoir suivi les deux chapitres précédents :
Vous devriez avoir un fichier de config (celui qui se trouve à /etc/nginx/sites-available/shiny.conf
) qui ressemble à ça (à quelques détails près) :
C’est un gros fichier qui fait un peu peur, mais comme on y est allé petit à petit, il ne fait pas si peur que ça.
Bon maintenant, on va devoir rajouter un nouveau bloc location
qui est quasiment identique à celui qui existe déjà mais avec deux petites différences :
/
) mais sur le dossier /051-movie-explorer
.Voici à quoi ressemble le nouveau bloc que vous pouvez ajouter juste en dessous de l’autre :
Pourquoi rajouter une location ?
En fait, on pourrait seulement rajouter les deux lignes suivantes au précédent bloc location
:
Sauf que dans ce cas, toutes vos applications Shiny seront protégées par les mêmes mots de passe.
Ce n’est pas forcément ce qu’on veut.
On peut vouloir avoir certaines applications protégées, et d’autres accessibles.
Là, ça permet de choisir.
Enregistrez le fichier.
Maintenant, on va devoir remplir le fichier /etc/nginx/.htpasswd
avec les identifiants.
C’est très simple. D’abord, lancez cette instruction pour créer l’identifiant :
Vous pouvez remplacer charles
par le login que vous voulez.
Ensuite, la commande suivante vous permet d’entrer un mot de passe et de le stocker de manière chiffrée. On ne stocke jamais un mot de passe en clair ! :
Et voilà !
Vous pouvez répéter l’opération autant de fois que nécessaire pour créer davantage de combinaisons login & mot de passe.
À présent, on redémarre le serveur nginx
:
Et maintenant, votre application va être protégée.
Vous pouvez essayer mon application à l’adresse https://shiny.datachamp.fr/movie-explorer-secure/ et vous remarquerez qu’une fenêtre d’authentification s’affiche.
Pas d’identifiant ? Pas d’accès.
Essayez avec :
Et vous devriez pouvoir accéder à l’application !
Notez que bien que cette technique soit plutôt bien sécurisée, il faut absolument forcer le passage par nginx
pour que ça marche.
Donc bloquez le port 3838 avec votre pare-feu (celui de AWS suffit, ou bien utilisez ufw
).
Notez aussi que vous n’êtes pas protégé face à une attaque de type bruteforce.
Un mot de passe de seulement 8 caractères se casse en quelques secondes.
Donc soit vous utilisez des mots de passe plus longs, soit vous passez par l’outil fail2ban
pour bannir les adresses IP qui font plusieurs tentatives de login infructueuses.
Maintenant, bien que cette technique soit très facile à mettre en place et offre une bonne sécurité, tout n’est pas parfait.
D’abord : C’est compliqué de créer de nouveaux comptes.
On n’a pas d’interface, il faut passer par le serveur, etc. On a vu plus simple.
Et puis, ça ne s’automatise pas facilement.
Ensuite : Ce n’est pas franchement convivial.
Franchement vous verriez un site e-commerce utiliser une fenêtre moche comme ça pour donner accès à un compte client ?
Non.
Et enfin : Ça ne répond pas à tous les besoins.
C’est soit on accède, soit on n’accède pas. Pas de gestion de compte ni de droits d’accès. On ne peut même pas savoir quel login a été utilisé pour accéder à l’application Shiny.
Du coup…
On peut essayer autrement.
auth0
comme serveur d’authentificationCette deuxième solution est un peu plus compliquée à mettre en place, mais elle a de gros avantages :
Auth0 est un fournisseur de services d’authentification et d’autorisation.
Plutôt que de créer nous-même une application d’authentification, Auth0 va s’en charger à notre place via un SDK (ou un appel à leur API).
Mettre en place Auth0
Première étape : Créer un compte Auth0.
Naviguez sur https://auth0.com/signup et créez un compte.
Vous pourrez utiliser le service gratuitement jusqu’à un maximum de 7000 utilisateurs.
Une fois le compte créé, connectez-vous et cliquez sur le gros bouton CREATE APPLICATION :
Donnez-lui un nom puis choisissez Regular Web Applications :
Vous ne trouverez pas Shiny dans les applications proposées par défaut, donc on va devoir configurer notre portail manuellement. Cliquez sur Settings
Les informations importantes à noter sont :
On en aura besoin un peu plus tard.
Un peu plus bas, dans la case Allowed Callback URLs, vous devez remplir votre URL suivi de callback. Donc pour moi, j’ai rempli https://shiny.datachamp.fr/callback
.
Attention : Pas de slash /
à la fin !
À présent, vous pouvez enregistrer les changements.
Maintenant, il va falloir installer le portail d’authentification sur votre serveur. Connectez-y vous via SSH, comme d’habitude, puis effectuez les étapes suivantes :
1. Installez NodeJS
2. Installez l’application de Auth0
3. Paramétrez
Il ne reste plus qu’à paramétrer l’application Auth0 pour que le service utilise les paramètres que vous avez créé tout à l’heure dans votre compte !
Créez un fichier nommé .env
(en tapant par exemple nano .env
) et remplissez-le des informations suivantes :
Pour les quatre premiers, ce sont les informations qu’on a récupérées tout à l’heure dans votre compte Auth0.
Le COOKIE_SECRET doit être une longue chaîne de caractères aléatoires. J’ai utilisé un générateur de mots de passe pour ça.
Ensuite, laissez ce qui reste tel quel.
Pour l’instant, notre application Shiny n’est toujours pas sécurisée.
En effet, Shiny écoute sur le port 3838, tandis que le portail d’authentification est une autre application qui écoute sur le port 3000.
Or, avec nginx
, on dit de rediriger https://shiny.datachamp.fr vers le port 3838. Donc on court-circuite le portail d’authentification !
Comment résoudre ce problème ?
Eh bien tout simplement en disant à nginx
de rediriger directement vers le port 3000 et de ne pas donner l’accès direct au port 3838.
Pour ça, il suffit de changer les instructions proxy_pass
et proxy_redirect
dans votre fichier de configuration nginx
:
Puis on enregistre la configuration :
Si vous essayez à présent d’accéder à votre application, ça ne va plus fonctionner. C’est parce que nous n’avons pas démarré le portail d’authentification.
Pour ça, rendez-vous dans le dossier shiny-auth0
et lancez la commande suivante :
Vous remarquerez que la console n’affiche rien et vous n’avez plus la main.
En effet, la commande qu’on a écrite permet de démarrer l’application du portail d’authentification mais c’est un démarrage manuel. C’est pourquoi on n’a plus la main.
On peut néanmoins tester si le portail fonctionne.
Connectez-vous sur votre application Shiny. La fenêtre suivante devrait s’afficher :
Vous pouvez créer un compte, vous logger, puis accéder à l’application.
Pour reprendre la main dans la console, vous pouvez arrêter Auth0 avec Ctrl + C.
Super mais bon… Il reste quand même deux problèmes :
On va s’en occuper.
Créer un service pour Auth0
On a déjà des “serveurs web” sur notre machine :
Shiny Server, qui est un serveur web qui écoute sur le port 3838.
nginx
, qui est aussi un serveur web qui écoute sur le port 80.
Et là, Auth0 est un autre serveur web qui écoute sur le port 3000.
Alors pourquoi doit-on lancer Auth0 manuellement, et pas les autres ?
La raison : les autres sont des services. C’est-à-dire qu’ils sont lancés automatiquement dès le démarrage de la machine. Et la configuration du service a été automatique dès l’installation de ces serveurs web.
Pour Auth0, on va devoir créer manuellement le service, et tout sera ensuite automatique.
Pour ça, ce n’est pas très compliqué, vous allez simplement créer un nouveau fichier :
Et ce fichier va contenir les informations suivantes :
Assurez-vous bien de remplacer le nom d’utilisateur charles
par le vôtre !
En général, la bonne pratique est de créer un nouveau nom d’utilisateur spécifiquement pour chaque service (comme l’utilisateur shiny
qui a été créé automatiquement pour le Shiny Server).
Grâce à ça, si le service est compromis, le reste du serveur n’est pas atteint.
Une fois cette configuration faite, il ne reste plus qu’à activer le service :
À présent, votre portail d’authentification est en ligne, plus besoin de démarrage manuel !
Maintenant, l’autre problème : comment restreindre l’accès ?
Créer des règles de restriction
Pour cette partie-là, tout se passe dans l’interface de votre compte Auth0.
Vous aurez peut-être remarqué la section “Rules”, qui permet de créer des règles à l’aide de petits snippets Javascript. Après avoir cliqué sur CREATE RULE, vous accédez à plein de possibilités :
Vous pouvez par exemple :
Je vous avoue que cette partie est un peu intimidante pour moi. Ma connaissance en Javascript reste très limitée, donc je préfère en rester aux règles pré-configurées.
Par exemple, pour un client qui veut que son appli ne soit accessible qu’à ses employées, limiter l’inscription aux emails du type xxx@nomdelentreprise.com
est déjà très pratique !
D’un autre côté, le fait de pouvoir directement écrire les règles en Javascript rend le système de règles super flexible.
On peut très bien imaginer par exemple que vous vendiez votre application Shiny comme un produit SaaS, l’acheteur est ajouté à votre Mailchimp sur une liste spécifique, et votre règle utilise l’API de Mailchimp pour autoriser l’accès seulement aux acheteurs de cette liste.
D’ailleurs, l’inverse est aussi possible. Par exemple, l’accès à votre application Shiny est gratuite mais nécessite la création d’un compte. Puis les informations de ce compte sont automatiquement exportées vers Mailchimp pour que vous puissiez communiquer avec les utilisateurs.
Ça demande simplement un peu de Javascript.
Pour nous autres développeurs de Shiny, connaître un peu de Javascript ne peut pas faire de mal !
En tout cas, ce service Auth0 fait plutôt bien le job.
Mais pour ma part, je ne me vois pas trop l’utiliser, sauf dans des cas très spécifiques (comme les exemples que je viens de citer).
En fait :
C’est pour ça que j’en arrive à la troisième et dernière solution : Créer votre propre portail d’authentification.
Afin d’implémenter cette dernière solution, je vous propose un package appelé shinymanager
.
Disponible sur le CRAN, vous pouvez l’installer (en local et sur votre serveur) facilement :
La page Github explique sommairement comment l’utiliser, la documentation reste encore très limitée : https://github.com/datastorm-open/shinymanager.
En descendant un peu sur la page, vous trouverez un exemple minimaliste pour ajouter le portail d’authentification.
Et ça marche :
Essayons de l’adapter à notre application sur l’explorateur de films (pour rappel, le code est disponible ici).
En lisant la page Github, on comprend qu’il faut faire les étapes suivantes :
shinymanager
,secure_app
autour de l’UI,server
.C’est parti !
Créer des identifiants
C’est la partie la plus facile.
Je vais utiliser le même bout de code qu’eux pour rester simple pour le moment et je le mets dans le global.R
:
Alors oui, les identifiants sont écrits comme ça, en clair, pas chiffrés ni rien.
Vous vous doutez bien que ce n’est ni pratique ni sécurisé.
On verra un peu plus tard comment changer ça.
Charger le package shinymanager
Je le charge au début du fichier global.R
, afin que les fonctions soient accessibles dans la partie serveur et dans la partie UI :
Ajouter la fonction secure_app
Dans l’exemple de Github, l’appli minimaliste tient en un seul fichier.
Nous, on en a trois : global.R
, server.R
, et ui.R
.
Les deux méthodes fonctionnent pour créer des applis Shiny.
Pas de souci ici.
Dans le fichier ui.R
, repérez la partie UI. C’est le bout de code qui commence par fluidPage(
, à la ligne 11.
Je rajoute la fonction secure_app
:
Et je n’oublie pas de refermer la parenthèse à la fin du fichier.
Ajouter le module d’authentification
Enfin, je vais juste copier/coller un bout de code au début de la fonction server
, autour de la ligne 23 du fichier server.R
:
Et voilà !
Alors si on s’arrête là… ça ne va pas marcher.
En fait, l’appli va bugger à cause de ggvis
.
Pour résoudre le problème, je lui dis juste de faire un graphe vide tant qu’on ne s’est pas connecté.
Je rajoute donc vis
autour de la ligne 99, tout au début de la déclaration de la variable réactive :
Maintenant, c’est bon !
Vous pouvez voir le code mis à jour à l’adresse suivante : https://gitlab.datachamp.fr/charles/movie-explorer-login.
J’ai hébergé l’application sur mon serveur Shiny.
Et voilà le résultat : https://shiny.datachamp.fr/movie-explorer-login/.
Vous pouvez vous logger avec les identifants shiny/azerty par exemple.
Options supplémentaires
shinymanager
ne s’arrête pas là heureusement.
Vous pouvez aussi :
Toutefois, tout n’est pas possible.
Par exemple, si vous voulez offrir une interface de création de compte pour vos utilisateurs… vous devrez le faire vous-même.
En fait, je trouve personnellement que shinymanager
est un très bon point de départ.
Lorsque je crée des portails d’authentification pour mes clients, je suis les étapes suivantes :
shinymanager
et l’ajoute à mon projet,La première fois, ça m’a demandé de vraiment comprendre comment le package fonctionnait, notamment via l’utilisation de tokens d’authentification et une classe R6.
Mais c’était super intéressant et j’ai beaucoup appris !
Donc je recommande vraiment de passer du temps à comprendre ce package.
D’autant plus que comme la sécurité est un argument essentiel du portail d’authentification, le fait de comprendre les rouages de shinymanager
vous permettra de comprendre aussi ses points forts et ses points faibles.
Par exemple le fait que le package ne hash pas les mots de passe avant de les stocker.. Pas bien !
Vous pouvez donc repartir de shinymanager
et rajouter par exemple :
Au final, c’est une solution que j’aime beaucoup parce qu’elle est entièrement flexible.
Le seul point qui me dérange est la question de la sécurité.
Ce n’est pas mon expertise, je ne sais pas ce que je ne sais pas, et il faut se méfier très fortement de l’effet Dunning-Kruger (surestimer vos capacités en sécurité parce que vous avez lu 3 articles, en gros).
Donc si votre application brasse des données confidentielles, faites-vous accompagner par une personne compétente !
Je vous ai récapitulé tous les avantages et inconvénients de chaque solution dans le tableau suivant :
Solution | Prix | Facilité de mise en œuvre | Dépendance | Sécurité | Administration |
---|---|---|---|---|---|
Shiny Server Pro | Cher | Facile | Indépendant | Bonne | Non |
nginx |
Gratuit | Facile | Indépendant | Bonne | Non |
Auth0 | Gratuit jusqu’à 7000 utilisateurs | Moyen | Dépendant | Bonne | Oui |
Solution maison | Gratuit | Difficile | Indépendant | Moyenne | Oui |
À présent, à vous de voir la solution la plus adaptée à votre situation !
Ce chapitre conclut le guide sur Comment déployer une application Shiny sur AWS.
J’espère que vous aurez trouvé ce guide utile ! Merci à tous ceux qui m’ont fait des retours et qui m’ont permis d’améliorer continuellement ce travail. Je compte le garder mis à jour régulièrement, donc n’hésitez pas à me faire part de tout changement que vous aurez remarqué !
Je sais qu’il y a encore plein d’autres sujets à traiter, comme le déploiement avec Docker, l’utilisation de shinyproxy.io, la scalabilité du déploiement, etc.
Ce sont des sujets que j’aborderai un peu plus tard, probablement dans un format différent.
Pour être tenu au courant de la publication de nouveaux articles, n’hésitez pas à vous inscrire ci-dessous !
]]>Dommage pour vous…
C’était une conférence géniale !
Des tutoriels, des conférences, (repas), à la rencontre d’autres utilisateurs de R, j’ai juste eu une première expérience formidable (:
Dans cet article, je vais faire un résumé de mes longues notes.
Je ne sais pas si ça vous sera utile, mais au moins ça vous montrera ce que vous avez manqué !
La première journée est consacrée aux tutoriels..
..que j’ai failli manquer !
Easyjet a décidé d’annuler mon vol la veille. Il n’y avait pas de train et pas d’autre vol avant deux jours plus tard !!
Alors j’ai fait ce que j’avais à faire.
J’ai loué une voiture et j’ai conduit toute la nuit pour NE PAS MANQUER les tutoriels !
Donc, je suis arrivé un peu fatigué. Et un peu stressé.
Mais ça s’est bien passé.
J’ai commencé avec :
Je voulais participer à ce tutoriel parce que j’avais entendu parler de H2O et d’AutoML, mais je ne les avais jamais essayés.
J’ai entendu dire que les meilleurs Kagglers utilisaient ces deux programmes pendant les compétitions.
Le tutoriel était top, car la progression était lente et régulière, et nous avons eu assez de temps pour nous mettre à la pratique.
À la fin du tutoriel, j’ai pu essayer h2o sur un ancien projet.
Maintenant, je l’utilise dans un autre projet pour un client, et il est très performant !
La plus grande force de h2o pour moi, c’est qu’il permet l’utilisation d’un tas de modèles pour obtenir d’excellents résultats rapidement.
Contrairement à caret
dans R, h2o
utilise Java pour effectuer tous les calculs et tout est parallélisé. Ça permet un calcul beaucoup plus rapide.
De plus, en utilisant AutoML
, je suis capable de trouver un modèle super performant en un rien de temps (enfin, je dois simplement le laisser travailler tout seul.)
Évidemment, c’est loin d’être parfait.
Premièrement, les modèles ne sont pas facilement interprétables. C’est pourquoi des outils sont fournis pour utiliser LIME, dont je n’avais jamais entendu parler auparavant.
Mais vous pouvez toujours exécuter des modèles interprétables tels que GLM ou CART avec h2o.
Deuxièmement, ça fonctionne bien si votre algorithme est simple, comme la régression ou la classification.
D’après mon expérience, c’est rarement aussi simple que ça. La plupart du temps, on doit faire preuve de créativité et développer des modèles plus complexes, parfois plusieurs couches de modèles. H2o ne vous aidera pas à être plus créatif.
Si vous êtes curieux, vous pouvez trouver des tutoriels sur ce repo Github.
Je n’ai pas appris grand-chose dans ce tutoriel.
J’aurais aimé avoir la possibilité d’installer Shinyproxy pendant le tutoriel (c’était mon souhait), mais il était plus axé sur l’information que sur la pratique.
Bon, j’en ai quand même appris un peu plus sur Shinyproxy. Et un petit peu sur Docker.
J’ai quand même utilisé les 3 heures passées dans la salle pour pratiquer, installer et tester Shinyproxy avec Docker.
À la fin du tutoriel, j’y étais presque, et quelques efforts supplémentaires m’ont amené à mettre en place Shinyproxy avec succès quelques jours plus tard. Et plus important encore, comprendre chaque étape du processus.
De plus, j’ai appris à développer des applications Shiny avec un simple container Docker (sans Shinyproxy), qui s’avère pratique avec golem
, un package récemment développé pour aider à concevoir des applications Shiny.
Si vous voulez tester Shinyproxy, essayez : Premiers pas avec ShinyProxy.
Une chose que j’ai trouvée très intéressante pendant la conférence est le nombre de présentations faites par les agences R.
On ne les appelle pas nécessairement comme ça. Ce sont des équipes de plusieurs développeurs de R qui proposent des activités de consulting pour d’autres entreprises.
Un peu comme moi ! Sauf qu’eux, ils sont plusieurs.
Ils viennent donc à cette conférence, et l’une des façons avec lesquelles ils peuvent se faire connaître, c’est de faire des présentations.
Ils nous ont présenté les outils open-source qu’ils ont développés, les succès avec les clients, etc.
C’est très intéressant d’entendre parler d’autres acteurs dans le domaine et d’être inspiré par eux !
Alors j’ai découvert :
Selon leur site Internet, ils fournissent 3 services majeurs :
Ils ne sont encore que 2 personnes. Cette société est fondée récemment, au début de 2018.
Leur site web a l’air soigné et ils ont de nombreux projets open-source : cynkra
MiraiSolutions est également basée à Zurich, sauf qu’elle est bien plus grande.
J’ai pu voir sur leur site qu’ils étaient 14, et ils semblent couvrir plus de sujets data.
Ils se spécialisent dans R, Python, Julia, et même Matlab !
Je viens d’apprendre d’ailleurs qu’ils sont les développeurs de XLConnect
. Je ne sais pas si vous avez déjà essayé de travailler avec des fichiers Excel, mais c’est l’un des meilleurs packages disponibles pour ça.
ils ont publié leur post useR avant le mien, voici donc un lien vers leur article si vous voulez avoir un autre extrait de la conférence : Rapport sur useR! 2019
Je connaissais déjà Appsilon après avoir échangé quelques tweets avec eux.
Ils sont basés en Pologne et sont très actifs dans l’univers de l’open-source.
Vous ne connaissez peut-être pas leurs packages, même si vous êtes un développeur Shiny, mais les voilà :
shiny.semantic
pour améliorer l’aspect visuel de votre application Shinyshiny.dashboard
pour créer un dashboard (n’en avez-vous pas marre de voir toujours le même shinydashboard
?)shiny.router
pour faire du routage d’URL (aucune idée sur le sujet)shiny.i18n
pour créer une application multilingueshiny.info
pour afficher les informations de diagnostic pendant le développement de l’applicationMa diapo préférée de la conférence de Filip Stachura (PDG d’Appsilon) était celle-ci :
car elle montre en quelques mots les plus grandes potentialités de Shiny :
Il a écrit un récapitulatif d’useR!2019 et je vous invite donc à y jeter un coup d’œil également : récapitulatif useR!2019 Toulouse.
Basée en Écosse, Jumping Rivers est une équipe de 9 personnes spécialisée dans le consulting et la formation sur l’environnement R.
Elle est même un partenaire officiel de RStudio, ce qui est très rare à ma connaissance (il n’y a que 10 partenaires selon la Page Partenaires RStudio).
J’aime la présentation détaillée des études de cas de leurs travaux précédents.
Chose qui reste à faire sur mon site web…
Vous pouvez en savoir plus sur leur site : https://www.jumpingrivers.com/.
ThinkR est, à ma connaissance, la seule compagnie spécialisée dans R en France.
En tout cas la seule parmi celles de plus d’une personne.
Comme ils avaient un petit bureau sur place, ils étaient super accessibles pour discuter avec eux.
J’ai pu obtenir quelques stickers, et même interviewer leur PDG, Vincent Guyader ! Je publierai l’interview dans quelques jours.
Ils ont un blog avec d’excellents articles que je recommande vivement (vu que je tombe régulièrement sur eux lors de mes requêtes de recherche concernant le R sur Google) : R Task Blog.
L’un des éléments les plus communs de toutes les conférences useR!2019 est la présentation d’un nouveau package (pas toujours si nouveau que ça d’ailleurs).
La communauté est probablement plus active que jamais.
Il y a de plus en plus de packages sur CRAN.
Et encore plus qui restent dans les dépôts Github.
Voici la liste des packages que je veux essayer dès que j’en aurai l’occasion :
Lien | présent sur le CRAN | Vignette ou Tutoriel |
---|---|---|
shinyEventLogger | yes | Vignette |
Il n’y a pas tant d’outils que ça pour le logging dans les applications Shiny.
Vous avez trois options :
/var/log/shiny-server/
où votre application est hébergée (lire comment déployer l’application sur votre serveur).shinyEventLogger
.Vous pouvez facilement enregistrer la valeur d’une variable, les résultats de tests, etc.
Ensuite, les logs peuvent être affichés dans la console R (enregistrés dans les fichiers log de /var/log/shiny-server/
), mais aussi affichés dans la console Javascript ou même enregistrés dans une database.
Ça rend la recherche dans les logs BEAUCOUP plus facile par rapport à la méthode par défaut.
On pourrait imaginer par exemple enregistrer la durée d’un événement, comme la durée d’un calcul complexe.
Comme de plus en plus de personnes utilisent votre application, ou comme de plus en plus de données sont stockées dans votre base de données, ce calcul peut prendre de plus en plus de temps. Et on peut configurer une alerte e-mail lorsqu’un certain seuil est dépassé.
Lien | présent sur le CRAN | Vignette ou Tutoriel |
---|---|---|
promises | Yes | Vignette |
La programmation asynchrone permet d’exécuter un code R en arrière-plan sans interrompre votre session en cours.
C’est super utile lorsque vous voulez rendre une application Shiny plus légère !
Le point le plus important n’est pas vraiment d’accélérer le calcul (parce que c’est une sorte de parallélisme), mais surtout de pouvoir continuer à naviguer dans l’application Shiny, ou dans votre session R, pendant que le calcul complexe se fait en arrière-plan.
C’est d’autant plus important lorsqu’il y a plusieurs utilisateurs dans la même session Shiny, car UN SEUL calcul complexe peut bloquer PLUSIEURS utilisateurs en même temps.
Je ne l’utilise pas beaucoup, même si je pense qu’elle a un potentiel énorme.
Il y a beaucoup de vignettes donc.. Commencez par là !
Lien | présent sur le CRAN | Vignette ou Tutoriel |
---|---|---|
future | Yes | Vignette |
Avez-vous déjà essayé d’exécuter des calculs en parallèle dans R ?
C’est pas super facile.
Et c’est difficile de comprendre comment ça marche exactement.
Le but de future
est de rassembler toutes les bibliothèques sur le calcul parallèle existantes en une seule. Les autres bibliothèques restent utilisées, bien sûr, mais future
offre une API unique pour accéder à ces bibliothèques. C’est super facile à utiliser.
J’ai déjà essayé de l’utiliser une fois pour des travaux récents, mais il se trouve que la parallélisation a pris plus de temps que le calcul séquentiel.
Bon.. parfois la parallélisation n’est pas toujours plus rapide !
Ce n’est pas une raison pour ne pas l’essayer !
Lien | présent sur le CRAN | Vignette ou Tutoriel |
---|---|---|
shinymeta | No | Tutorial |
shinymeta
est encore tout nouveau et a été présenté pour la première fois par Joe Cheng à la conférence useR!2019.
Il nous a dit qu’il avait choisi ce sujet pour sa conférence plénière sans avoir de solution au préalable.. et que jusqu’à une semaine avant la conf, ils étaient toujours pas sûr de présenter quelque chose de propre.
Donc.. L’icône “lifecycle: experimental” n’est pas là pour rien, je suppose.
De quoi s’agit-il ?
Le but de shinymeta
est d’extraire la logique d’une application Shiny et de l’exécuter en dehors de Shiny. Par exemple dans un rapport RMarkdown ou même dans un script R simple.
On enregistre ce qu’on fait avec l’application, puis on extrait automatiquement le code pour reproduire les étapes sans l’application Shiny. Donc, on va :
Résultat : vous disposez déjà d’un script R autonome qui peut reproduire le même graphique ou tableau.
Génial pour rendre l’analyse Shiny reproductible !
Lien | présent sur le CRAN | Vignette ou Tutoriel |
---|---|---|
polite | No | Tutorial |
J’ai fait ma part de web scraping.
Que faites-vous lorsque vous avez besoin de données provenant d’un site web ?
Vous allumez votre RSelenium
et commencez le scraping comme un fou fou !
Eh bien.. non.
Le but de polite
est de scraper poliment en cherchant la permission, en y allant lentement, et en ne demandant jamais deux fois.
Je recommande de lire le tutoriel pour apprendre les bonnes pratiques, et de les utiliser à partir de maintenant.
Du moins, c’est ce que j’ai l’intention de faire la prochaine fois que j’aurai besoin de scraper des données !
Lien | présent sur le CRAN | Vignette ou Tutoriel |
---|---|---|
SuperLearning | Yes | Vignette |
Je n’ai pas assisté à cette conférence, mais j’ai très envie d’essayer ce package !
Je suis tellement frustré par le fait que tant de packages différents ont chacun leur propre syntaxe différente quand on fait du machine learning en R.
Donc quand un package comme SuperLearning se présente et me dit qu’il y a un autre moyen..
J’ai envie d’essayer.
Lien | présent sur le CRAN | Vignette ou Tutoriel |
---|---|---|
auth0 | Yes | Tutorial |
L’une des demandes les plus courantes que je reçois de mes clients une fois que j’ai créé une application Shiny pour eux est :
Comment protéger mon appli Shiny ?
Ils veulent qu’elle soit accessible de partout, donc ouverte au public sur Internet, mais ils ne veulent pas dévoiler leurs secrets professionnels à tout le monde.
Une solution : L’authentication.
Il existe de nombreuses façons pour configurer l’authentification d’une application Shiny de nos jours.
L’une d’entre elles est d’utiliser auth0
.
Je l’ai déjà configuré et c’est assez facile. Maintenant, je découvre qu’ils m’ont rendu la tâche encore plus simple en me proposant ce package.
Aucune excuse pour ne plus configurer une authentification dans vos applis !
C’était la première fois que je participe à une conférence useR. Je ne savais pas à quoi m’attendre, mais la conférence a largement dépassé mes attentes !
Il y a beaucoup de choses que je voulais aborder dans cet article, mais.. c’est déjà pas mal long.
En gros, j’ai beaucoup appris. Et maintenant j’ai une longue liste d’autres choses à apprendre.
C’était top de rencontrer autant d’utilisateurs de R. Maintenant j’ai une bonne idée de qui travaille dans cet monde.
J’ai hâte d’assister au prochain événement.
J’espère vous voir là-bas !
]]>Too bad for you..
It was an awesome conference!
From tutorials, talks, (food), to meeting other R users, I just had a great first time (:
In this article, I want to make a recap out of my lengthy notes.
Not sure if that’ll be of any use to you, but at least it will show you what you missed!
The first day was tutorials day..
..which I almost missed!
Easyjet decided to cancel my flight the day before. There was no train and no other flight until two days later!!
So I did what I had to do.
I rented a car and drove all night long to NOT MISS the tutorials!
So, I was a bit tired when I arrived there. And a bit stressed out.
But it went okay.
I started with:
I wanted to be at this tutorial because I had heard about H2O, and AutoML, but had never tried them.
I had heard top Kagglers were using it during competitions.
The tutorial was great, as the progression was slow and steady, and we were left with enough time to practice on our own.
At the end of the tutorial, I was able to try h2o by myself on a past project.
Now I’m using it in another project for a client, and it’s performing great!
The biggest strength of h2o, to me, is that you can run a bunch of models and get great results fast.
Contrary to caret
in R, h2o
uses Java to perform all the computations and everything is parallelized. That makes the computation much quicker.
Plus, by using AutoML
, I’m able to find a super-performing model in no time (well, I just have to let it run by itself).
Now, it’s not perfect of course.
First, the models are not easily interpretable. That’s why they gave tools to use LIME (Local Interpretable Model-Agnostic Explanations), something I had never heard of before.
But you can still run interpretable models such as GLM or CART with h2o.
Second, it works well if your algorithm is simple, such as regression or classification.
In my experience, it’s rarely as simple. Most of the time, you must think creatively and develop more complex models, sometimes multiple layers of models. h2o won’t help you get more creative.
If you’re curious, they have developed tutorials you can find on their Github repo.
It’s easy to use.
I learned less in this tutorial.
I wished I had had the opportunity to setup shinyproxy during the tutorial (what I hoped), but it was more information-oriented than practice-oriented.
Anyway, I still learned a little bit more about shinyproxy. And a little bit about Docker.
I still used the 3 hours of being in the room to practice it, set it up, and play with Docker.
At the end of the tutorial, I was almost there, and a bit of extra effort lead me to successfully set up shinyproxy a few days later. And more importantly, understand every step of the way.
Yay o/
Plus, I learned how to deploy shiny apps with a simple Docker container (without shinyproxy), which goes handy with golem
, a recently developed package to help with building Shiny apps.
If you want to experiment with shinyproxy, give it a try here: Getting Started with ShinyProxy.
Something I found quite interesting during the conference was how many of the talks were made by R Agencies.
They don’t necessarily call themselves this way. They’re a team of several R developers and do consultancy jobs for other companies.
A little bit like me, except they’re multiple people.
So they come to this conference, and one of the ways they have to put their name out there is to make talks.
They show the open-source tools they have developed, customer success stories, etc.
It’s quite interesting to hear about other actors and be inspired by them!
So I discovered:
According to their website, they provide 3 major services:
They’re still only 2 people, and it was founded recently, in early 2018.
Their website looks neat and they have multiple open-source projects: cynkra
MiraiSolutions is also based in Zurich, except they are much bigger.
I can see 14 people on their website, and they seem to cover a larger part of the field.
They do R, Python, Julia, and even Matlab!
I just learned they are the developers behind XLConnect
. Not sure if you’ve already tried to work with Excel files, but this is one of the best packages out there to do that.
They published their useR post before mine, so here is a link to their article if you want to have another excerpt of the conference: Impressions from useR! 2019
I already knew Appsilon before, as they once answer a Tweet of mine asking for help.
They’re based in Poland and are quite active in the open-source world.
You might not know about their packages, even if you’re a Shiny developer. They have five of them:
shiny.semantic
to improve the visual of your Shiny appshiny.dashboard
to build a dashboard (aren’t you bored of always seeing the same shinydashboard
?)shiny.router
to do URL routing (no idea what this is)shiny.i18n
to create a multi-language appshiny.info
to show diagnostic information during the development of the appMy favorite slide from Filip Stachura’s (CEO of Appsilon) talk was this one:
as it shows in a few words the biggest strengths of Shiny:
He wrote a recap of useR!2019 so I invite you to check it out as well: useR!2019 Toulouse recap.
Based in Scotland, Jumping Rivers is a team of 9 people doing consultancy and training in R.
They’re even a certified RStudio Partner, which, to my understanding, is a rare thing (there are only 10 partners according to the RStudio Partners page).
I like how they detailed case studies of previous work.
Something that’s still to be done on my website…
Anyway. Check them out here: https://www.jumpingrivers.com/.
ThinkR is, to my knowledge, the only company specialized in R in France.
At least the only company with more than 1 person.
I knew them before going to the conference, and they had a small desk there so it was super easy to meet and discuss with them.
I could get a few stickers and even interview their CEO, Vincent Guyader. I’ll publish the interview in a few days.
They have a blog with great articles that I highly recommend (as I regularly stumble upon them when googling my R issues): R Task Blog.
One of the most common points of all talks at useR!2019 is the presentation of a new (or not-so-new) package.
The community is probably more active than ever.
There are more and more packages on CRAN.
And even more that stay in their Github repositories.
Here is the list of packages I want to try as soon as I have the opportunity to:
shinyEventLogger
: Log events in shiny applicationsLink | is on CRAN | Vignette or Tutorial |
---|---|---|
shinyEventLogger | Yes | Vignette |
There aren’t so many tools out there to log events in Shiny.
You have three options:
/var/log/shiny-server/
where your app is hosted (read how to Deploy the app on your server).shinyEventLogger
package.You can easily log the value of a variable, the results of tests, etc.
Then, the logs can be printed to the R console (and find themselves in the log files at /var/log/shiny-server/
) but also displayed in the Javascript console or even stored in a database.
This makes searching through logs MUCH easier than the default method.
Here is a use case I have imagined: You can log the duration of an event. So, why not record the duration of a lengthy computation?
As more and more people use your app, or as more data is gathered in your database, this computation could take more and more time. Then, you could set up an alarm that sends you an email when it gets higher than a certain threshold.
promises
: Abstractions for Promise-Based Asynchronous ProgrammingLink | is on CRAN | Vignette or Tutorial |
---|---|---|
promises | Yes | Vignette |
Asynchronous programming is running some R code in the background without keeping your current session busy.
That is super useful when you want to speed up a Shiny app!
The most important point is not much about speeding up computation (because that’s some way of parallelizing) but mostly being able to keep navigating in the Shiny app, or in your R session, while the heavy computation is getting done in the background.
It’s even more important when there are multiple users on the same Shiny session since ONE heavy computation can block MANY users at the same time.
I don’t use it much, even though I think it has fantastic potential.
They have many vignettes so.. start there!
future
: Unified Parallel and Distributed Processing in R for EveryoneLink | is on CRAN | Vignette or Tutorial |
---|---|---|
future | Yes | Vignette |
Have you already tried to run computations in parallel in R?
It’s not super easy.
And it’s hard to understand how that works exactly.
The purpose of future
is to gather all existing parallelizing libraries into one. The other libraries are not discarded, of course, but future
offers a unique API to access these libraries. It’s super easy to use.
I’ve already tried to use it once for recent work, but it happened that parallelizing took a longer time than doing the computation sequentially.
Yea.. sometimes it’s not faster because of the overhead cost.
That’s not a reason to not keep trying it though!
shinymeta
: Record and Expose Shiny app Logic using MetaprogrammingLink | is on CRAN | Vignette or Tutorial |
---|---|---|
shinymeta | No | Tutorial |
shinymeta
is still brand new and was presented for the first time by Joe Cheng at useR!2019.
He told us he picked this topic for his keynote session.. without having the solution. And that they had something terrible until about one week before the conference.
So.. The “lifecycle: experimental” icon is not there for nothing, I guess.
What is this about?
The goal of shinymeta
is to extract the logic of a Shiny app and run it outside Shiny. For example in an RMarkdown report or even in a simple R script.
It will record what you do with the app, and then automatically extract the code to reproduce the steps without the Shiny app. So it will:
so that you already have an autonomous R script that can replicate the same chart or table.
Awesome for making Shiny analysis reproducible!
polite
: Be nice on the webLink | is on CRAN | Vignette or Tutorial |
---|---|---|
polite | No | Tutorial |
I have done my share of web scraping.
What do you do when you need data from a website?
You fire up your RSelenium
and start scrapping it like crazy!
Well.. no.
The goal of polite
is to scrape politely by seeking permission, taking slowly, and never asking twice.
I recommend reading through the tutorial to learn the good practice, and then use them from now on.
At least that’s what I plan to do the next time I’ll need to scrape some data!
SuperLearner
: Super Learner PredictionLink | is on CRAN | Vignette or Tutorial |
---|---|---|
SuperLearning | Yes | Vignette |
I missed this talk but want to give it a try!
I am so frustrated with the so many different packages that each have their syntax when it comes to machine learning.
So when a package such as SuperLearning comes around and tells me there is another way..
I want to try it.
I say “comes around” but.. it’s a pretty old package.
auth0
: Secure Authentication in Shiny with Auth0Link | is on CRAN | Vignette or Tutorial |
---|---|---|
auth0 | Yes | Tutorial |
One of the most common requests I get from my clients once I have set up a Shiny app for them is..
How do I keep stranger locked away from my app?
They want it accessible from anyway, so open to the public internet, but they don’t want to show their business secrets to everyone.
One solution: Authentication.
There are many ways to set up authentication for a Shiny app nowadays.
One of them is to use auth0
.
I have already set it up and it’s fairly easy. Now I discover they have made it even easier by providing this package.
No excuse to not set up authentication on your apps anymore!
It was the first time I participated in the useR conference. I had no idea what to expect, but the conference blew my non-existing expectations!
There are many things I would have liked to cover in this article but.. that’s already a ton of material.
I learned a lot. And I now have a way-too-long list of other things to learn.
It was great to meet so many R users.
I have now a much better idea of who works in the R ecosystem.
I just can’t wait for the next event.
I hope to see you there!
]]>I’m not exactly sure what to expect.
Last time I went to a similar conference, it was the SSC (Statistical Society of Canada) annual meeting of 2015.
Back then, I was still a PhD student.
I didn’t understand most talks I went to.
And, to be honest, a lot of them weren’t attractive to me.
But this time, it’s different.
This time, I chose to go to useR!2019 because I’ve been using R for many years and that’s the technology I have used with all my past employers and clients.
Now, I feel I’m part of this world.
There are a few things I expect from this conference, and I would like to prepare some goals as well.
I want to:
Let’s dive into each goal.
This list of tutorials is here: Tutorials
Pretty much every day, I encounter something I don’t know in R.
There are things I know very well, I have become an expert at them: dplyr
, data.table
, shiny
, …
There are things I know and I may even have used them, but I definitely don’t master them: plumber
, golem
, shinyproxy
, …
And finally, there are things I know nothing about: H20
, bookdown
, purrr
, …
It’s hard to know everything.
But it’s also hard to deal with this imposter syndrome because I know the huge amount of things I don’t know.
And finally, it’s also hard to take the time to sit down, open a book (or a tutorial, or even a package vignette), and LEARN.
I expect to learn during useR:2019.
I subscribed to two tutorials:
Automatic and Explainable Machine Learning with H20 in R.
I know quite a lot about machine learning.
But I know nothing about H20.
Actually, I don’t know much about AutoML in general.
I feel this is wrong.
So I hope the 4 hours tutorial on H20 will at least help me get a good grasp on the strengths of AutoML and teach me how to apply it for my clients.
Docker for Data Science: R, ShinyProxy and more.
I know some of Docker.
Mostly pulling images and setting them up on a server.
But I have struggled to setup ShinyProxy in the past and make it work perfectly.
Even though..
I created many Shiny apps for my clients and hosted them on a server (without Docker usually).
Again, I feel I would be so much efficient if only I could learn this.
And rather than spending days figuring everything out by myself, I better learn it from an expert.
This first day of tutorials will already teach me things I have been wanting to learn for MONTHS (if not years).
The list of talks with the abstracts: Talks
Talks will take place from Wednesday to Friday.
Reading through the talks subjects and descriptions reminded me of my time as a PhD student.
I would look at all the talks from the conference, trying to decrypt the titles and predict whether I would understand something after the first 3 slides.
It’s so different now!
Looking at all the talks, I felt EXCITEMENT!
I felt I wanted to go to all of them!
Ok, ok, maybe not all of them.
But at almost every timeslot, I could find something exciting to look forward to.
Wednesday Morning: Shiny 1
Shiny 1 is about:
I know it’s possible to switch between rooms to change the theme, but from experience, it’s a bit hard to do without missing a talk. I prefer to stay in the same room for the duration of the batch of talks.
In this first batch, it’s all about Shiny.
That just makes sense since many of my clients want something with Shiny (who wouldn’t?).
I am especially curious about the last one: Automated Surveys and Reports for Expert Elicitation with Shiny. Looking at the abstract, this is super close to some work I have done with a past client. I am very very curious at what they have done.
It’s also funny that both (my client and the speaker) chose Shiny for this, as it’s not especially R related. There is not a huge amount of moving data around. A little bit of statistics, okay, but not that much. It’s a web app. But R Shiny is actually very strong at just building a web app.
Wednesday Afternoon: Shiny 2
Shiny 2 is about:
Yup.
Shiny all over again.
I’m mostly curious about the Golem talk.
I only started to use it recently, after having glanced over it for months.
It felt both practical and clumsy. Probably because I need to get used to it. There are many functions in golem and the accompanying packages I don’t know, so that’s probably why I need to get used to it. Also, as they state, it is an opinionated framework.
Getting re-introduced to it by an expert will certainly help to acquire the good practices more quickly.
Thursday Morning: Text mining
Text mining is about:
I started in text mining with a client who is a researcher in political science.
In the beginning, I was mostly doing web scraping, and more and more I have learned and implemented text mining algorithms.
In this batch of talks, I am mostly interested in polite
and the one about German nouns. The former because I realize when I scrape that.. I just do it brutally. I don’t have much concern for the servers I’m scraping, and I realize there must be a better way.
The latter because I know some German and I know how hard it is to remember the genders of all the nouns. I also admire the creativity in thinking one might try to predict the gender of a German name. As a human being, I have asked myself whether there was some logic that a noun should be masculine, feminine, or neutral, and never found one. It’s not even coherent between French and German. So.. I’m curious here.
Apparently, these are very short talks, so it’s mostly about hearing about something, taking some notes, and diving into it later at home if it spikes my interest.
Thursday Morning later: Data mining
Data mining is about:
Machine Learning is also a topic of interest of mine, and something I sell to my clients. So far, I have either done it with Python or with the caret
package. Or, actually, with the so many different packages all specialized on ONE type of algorithm.
It never felt as easy as with scikit-learn in Python.
So I want to learn more about what exists.
During this session, there is also a talk about R and security. I would really like to listen to this one, as they will talk about how to hack into R. I am concerned about security whenever I host an R API or an R Shiny but don’t know much about it.
Thursday Afternoon: Operations & data products
Operations & data products is about:
These are not the most exciting talks I want to listen to, especially since I’ll be quite exhausted after 4 days of conferencing, but I see it as some nice bonus.
Continuous Integration and automating tasks is something I know would make my work more efficient.
Finally.
One of the best perks of going to a conference is meeting people.
So far, I don’t know I know anyone going there, so that’s a bit scary for me.
I don’t go to people that easily.
But I still hope to meet people and learning about the R environment in France.
In fact, I don’t even know the kind of people I’ll find there.
Mostly academics?
Probably some, but I believe a lot of companies now use R. I can just look at the sponsors.
One team I really expect to meet there is ThinkR.
They’re 5 R experts who founded an R Agency a few years ago and they seem to be growing a lot. I see them everywhere, they develop super useful tools (like golem
), so I’m curious to meet and learn from them.
Another team I’d like to meet is Ardata.
I know a few other data scientist freelancers as well (such as Dacta) that will go there and that I would like to meet.
It’s hard to set a goal for meeting people, but I expect to learn a lot about the R ecosystem and who are the R users.
More about this when I come back (;
]]>Et je ne sais pas trop à quoi m’attendre..
La dernière fois que je suis allé à une conférence comme ça, c’était la SSC (pour Statistical Society of Canada) pour leur meeting annuel en 2015.
À l’époque, j’étais encore étudiant en doctorat.
Et je comprenais pas grand chose aux différentes présentations.
En fait, ça m’excitait pas plus que ça.
Là, c’est différent.
Cette fois, j’ai choisi d’aller à useR!2019 parce que je commence à avoir une certaine expérience de R et ça fait plusieurs années que je l’utilise pour mes clients.
En fait, j’ai vraiment l’impression de faire partie du monde de R.
Du coup, j’ai essayé de me préparer et d’écrire mes attentes pour cette conf.
J’ai trouvé trois thèmes principaux :
C’est parti.
La liste des tutorials est disponible ici : Tutorials
Quasiment chaque jour où j’utilise R, je me rends compte qu’il y a des choses que je ne connais pas.
Alors, certes, il y a aussi des choses que je maîtrise, comme dplyr
, data.table
, shiny
, …
Il y a aussi des notions que je connais, que j’ai déjà appliquées, mais que je ne maîtrise pas totalement, comme plumber
, golem
, shinyproxy
, …
Et finalement..
Il y a les trucs qui me sont complètement inconnus, comme H20
, bookdown
, purrr
, …
C’est impossible de tout savoir.
Sauf que là ça me fait de plus en plus un gros syndrôme de l’imposteur quand je découvrer la masse de choses que je ne connais pas.
Alors, oui, j’ai envie d’apprendre.
Mais trouver le temps, entre deux clients, ou deux articles, c’est dur.
Là, pendant useR:2019, on m’a offert la possibilité d’avoir une journée complète avec le cul sur une chaise pour écouter un expert.
Alors j’ai saisi l’opportunité.
J’ai dit oui et je me suis inscrit à deux tutorials :
Automatic and Explainable Machine Learning with H20 in R.
Celui sur H20 me plaît beaucoup parce que je ne connais quasi rien en AutoML.
Et rien du tout sur H20 non plus, évidemment.
Et comme je fais du ML, ça me paraît un peu bête.. (vous le sentez le syndrôme là ?)
Alors 4 heures à écouter un expert, j’espère que ça me permettra au moins de démarrer et d’apprendre rapidement à l’utiliser pour mes clients.
Docker for Data Science: R, ShinyProxy and more.
Alors, Docker je connais un peu.
J’ai utilisé des images et je les ai installées sur des serveurs.
Mais la dernière fois que j’ai utilisé ShinyProxy, j’ai pas mal galéré et à la fin ça marchait pas bien.
L’idée, c’est que plutôt que de perdre mon temps tout seul devant mon ordi, je préférerais que quelqu’un me montre un process qui marche.
Il y a des tonnes de trucs que je veux apprendre, mais là, déjà, en 1 journée, je vais voir des nouveaux concepts que j’ai envie d’apprendre depuis super longtemps !
La liste des conférences est ici : Conférences
Ce sont de courtes présentations d’environ 15 minutes.
Idéal pour découvrir un sujet, et le potasser plus tard à la maison si vraiment ça nous intéresse.
Au moins on a vu la base et on sait à quoi ça sert.
Je me souviens quand j’étais étudiant et que j’allais à une conférence comme ça. Je regardais la liste des conférences et y’avait rien qui me parlait. Et quand j’y allais, j’étais perdu au bout de 3 slides.
C’est tellement différent maintenant !
Là je regarde la liste, et j’ai envie de TOUT voir !
Bon, ok, peut-être pas tout, mais y’a quand même beaucoup de présentations qui m’ont vraiment hypé.
Mercredi matin : Shiny 1
Shiny 1, c’est :
(Je vous laisse les titres originaux.)
À chaque session, on a le choix en gros entre 6 thèmes. C’est possible de changer de thème pendant une session, mais il faut changer de salle, déranger tout le monde, et le temps de bouger, on peut facilement louper une présentation entière.
Du coup j’ai plutôt décidé de choisir des thèmes entiers.
Là, c’est tout du Shiny.
Shiny c’est super important pour moi puisque la plupart de mes clients en veulent (qui n’en voudrait pas de toute façon ?!).
Là où je suis super curieux, c’est sur le dernier talk : Automated Surveys and Reports for Expert Elicitation with Shiny
J’ai lu l’abstract et… c’est quasi identique à une mission que j’ai faite pour un client l’année dernière ! Pourtant je vois pas trop de lien entre le présentateur et mon client.
Bon, on verra. Mais du coup, ça promet !
Mercredi après-midi : Shiny 2
Shiny 2, c’est :
Encore du Shiny !
Là je veux surtout apprendre sur Golem.
J’ai commencé à l’utiliser, mais pour l’instant ça me semble à la fois pratique et un peu bancal en même temps.
Je ne sais pas trop encore quoi en penser.
Et je pense qu’il faut juste s’habituer. Y’a plein de fonctions qui vont avec et que je ne connais pas encore, donc c’est clair qu’il faut un peu de temps pour s’y mettre.
Mais je pense qu’avoir un framework bien précis avec des fonctions qui évitent de recopier les mêmes trucs tout le temps, bah.. c’est forcément pratique.
Jeudi matin : Text mining
Text mining, c’est :
Le text mining, j’en fais surtout avec un client, chercheur en sciences politiques.
Au début je faisais surtout du web scraping, et peu à peu j’ai été amené à développer des algos de text mining.
Et oui j’ai fait beaucoup de web scraping. Du scraping brutal et pas très propre. Pas très poli, sans doute. Je pense qu’il y a des bonnes manières à apprendre avant d’aspirer un site, donc je veux écouter ce que les auteurs de polite
ont créé.
L’autre talk qui attise ma curiosité, c’est celui sur la prédiction du genre des noms en allemands. Ceux qui ont fait allemand savent à quel point c’est perturbant d’avoir 3 genres en allemand (masculin, féminin, neutre) qui n’ont rien à voir avec les genres en France. Chez nous, une table c’est féminin, et chez eux c’est masculin. Pourquoi ? Eh bien il semblerait qu’un algo ait réussi à prédire le genre d’un nom en allemand.
Jeudi après-midi : Data mining
Data mining, c’est :
Le Machine Learning, ça m’intéresse forcément. J’en fais pour mes clients. Et puis c’est juste chouette tout court.
Jusqu’ici, j’ai soit utilisé des bibliothèques en Python (comme scikit-learn
), soit le package caret
en R. Ou alors la myriade de packages tous spécialisés sur UN seul type d’algorithme avec aucune cohérence entre les packages.
J’ai toujours trouvé ça un peu galère de faire du ML en R. Et pourtant j’adore R !
Donc j’aimerais bien connaître les nouveaux outils qui ont été développés.
Jeudi après-midi : Operations & data products
Operations & data products, c’est :
Cette session est moins passionnante que le reste (de mon point de vue), et en plus je serai probablement fatigué après 4 jours de conf, mais bon, ça peut être un bonus sympa.
Quand il s’agit d’intégration continue, d’automatiser des tâches, ou juste de rendre mon travail plus efficace, je suis en général toujours là.
Ça se dit, un enthousiaste de R, en français ?
Bon.
Clairement, un des gros avantages d’une conférence, c’est quand même de rencontrer du monde.
Là on a des top experts de R qui vont venir du monde entier.
Probablement aussi des gens comme moi, un peu perdus, mais curieux.
Et plein d’autres.
En fait je ne sais pas trop à quoi m’attendre.
J’ai hâte de découvrir tout cet écosystème de R que j’ai jusqu’ici regarder surtout de loin.
J’ai déjà fait une petite liste des gens que j’aimerais rencontrer.
D’abord il y a les ThinkR, une petite agence française spécialisée en R. Et au passage auteurs de golem
et d’autres outils bien pratiques pour Shiny.
Et puis Ardata aussi.
Et des freelancers, comme Dacta.
Bref, que du beau monde, et probablement plein d’autres personnes que je ne connais pas encore !
C’est difficile de se fixer un objectif ou d’avoir des attentes, mais une chose est sûre.. j’ai hâte d’y être !
À bientôt pour un retour d’expérience (;
]]>Je pensais que si j’avais la data quelque part dans le graphe, ça suffisait.
Sauf que..
Quand je visitais les internets et que je voyais des graphes magnifiques, je comprenais pas trop.
Comment ils font pour faire des graphiques aussi beau ??
En fait, ils trichent.
Ils ne font pas leurs graphiques avec R.
D’ailleurs, R n’a pas été pensé à l’origine pour faire des graphes jolis prêts à être publiés.
Mais ça, c’était avant.
Aujourd’hui, on peut faire de superbes graphiques avec R !
Et en plus.. c’est pas si dur.
En fait, la BBC utilise R pour créer ses propres graphiques directement publiables dans leurs articles !
C’est pourquoi dans cet article, je vais vous montrer :
À vous les infographies en R !!!
Pour commencer, on va se prendre un jeu de données.
J’utilise le jeu de données PokemonGO
, créé par Alberto Barradas et partagé sur Kaggle : https://www.kaggle.com/abcsds/pokemongo
Et on va tracer la relation entre Max CP
et Max HP
.
Max CP
, c’est le maximum de dégâts que peut infliger un pokemon. Et Max HP
, c’est le maximum de dégâts qu’un pokemon peut recevoir.
Traçons un premier graphe avec le R natif :
Euh..
Pas terrible, hein.
Alors, certes, on remarque la relation entre nos deux variables.
Mais de là à dire que c’est publiable ? Peut-être pas.
C’est pour ça que Hadley Wickham et ses collaborateurs ont créé ggplot2
.
D’après le site de ggplot2
, c’est un package pour créer d’élégantes visualisations de données à l’aide d’une grammaire de graphiques.
Essayons.
Ok, pas mal !
Déjà, au lieu d’avoir des cercles vides pour représenter des points, on a des points pour de vrai.
Et le graphe est beaucoup plus clean !
Par exemple, on a pas besoin de tourner notre tête pour lire les libellés sur l’axe des ordonnées.
Et puis les polices sont plus jolies.
Mais bon.
C’est pas non plus fou fou. On peut sans doute faire mieux !
bbplot
Ce qui est chouette avec ggplot2
, c’est qu’on a le contrôle sur TOUT.
Et c’est facile à customiser.
On peut ajouter nos propres couleurs, polices, logo, etc.
Et au lieu de tout réécrire à chaque fois, on peut l’intégrer à un package.
Exactement comme la BBC l’a fait (source).
Plutôt que de créer des graphes un peu moches, ils ont créé leur propre package bbplot
pour faire des beaux graphiques avec LEUR style.
Regardez plutôt :
Par mal, hein ?
Il suffit d’ajouter la fonction bbc_style()
:
Hum..
C’est un peu mieux.
Notamment :
Mais c’est pas non plus révolutionnaire.
Bon.
C’est mieux, mais on a quand même encore du travail !
On va devoir :
C’est un peu normal, en fait. Chaque graphique est unique et doit être personnalisé autour de ce qu’on souhaite communiquer.
Mais..
J’en attendais un peu plus quand même, quand j’ai vu les exemples fournis par la BBC !
Du coup…
Ça fait toujours un peu peur d’aller voir le code derrière un package.
Mais en fait, le package bbplot
est super simple !
Il n’y a que deux fonctions.
La fonction bbc_style
qui crée un thème ggplot2
en spécifiant plein de détails, comme les polices, le style de la légende, la grille, etc.
Et la fonction finalise_plot
qui ajoute le footer, le logo, et enregistre le graphe.
La 2e fonction n’a besoin d’aucun changement.
Et la 1e est celle qu’on peut changer pour spécifier tous les petits détails pour avoir des graphiques jolis qui représentent le style de votre entreprise.
Et cette 1e fonction est très courte : https://github.com/bbc/bbplot/blob/master/R/bbc_style.R
En fait, on peut voir ce package comme un très bon point de départ.
Quand on regarde ce qu’ils ont mis dans cette fonction, on voit que :
Le 2e point est particulièrement important. Rappelez-vous qu’on se plaignait de ne plus avoir les libellés sur les axes.
Et ce qui est chouette, c’est qu’en utilisant ce package, ça ne coûte en gros qu’une seule ligne de code !
ggplot2
et bbplot
?Maintenant je vais vous montrer comment passer d’un graphique déjà pas mal à un graphique vraiment cool, prêt à être publié !
Pour ça, on va avancer étape par étape, et à chaque fois on améliorera notre code de manière itérative.
On commence avec le code sur lequel on s’était arrêtés :
Je ne vais pas répéter à chaque fois le chargement de la librairie et l’appel à finalise_plot
, donc rappelez-vous qu’il faut le faire.
On va plutôt se focaliser sur la grammaire de ggplot
.
La première chose qu’on va faire, c’est ajouter un titre.
Pour ça, on utilise la fonction labs
.
Et je vais lui donner la couleur du logo de Pokemon GO :
Le package bbplot
a fait disparaître les titres axes, pour une certaine raison.
En fait, c’est plus clair d’ajouter l’information directement sur l’échelle des axes que de mettre un titre.
Voyez par vous-même :
À présent, avec le titre ET les unités, c’est clair. On reste minimaliste et on évite la redondance.
Notre objectif initial était de voir la relation entre Max CP
et Max HP
.
Et on voit bien que cette relation existe.
Ça pourrait être chouette de tracer une ligne au milieu de ce nuage de point !
Une ligne de régression.
Pour ça, on peut utiliser geom_smooth
avec l’argument method = "lm"
, et se = FALSE
pour retirer les bandes de confiance.
Ah oui, et vous aurez peut-être remarqué qu’il y a un pokemon très très à droite sur le graphe. C’est un point un peu aberrant qui va avoir une grosse influence sur la droite. Du coup, uniquement pour le calcul de la droite, je le retire.
Et si on veut rajouter une dimension pour voir les différents types de pokemon ?
Est-ce que les pokemons Feu sont plus puissants que les pokemons Dragon ?
C’est facile à faire en ajoutant l’argument col
dans les aesthetics :
Mouais..
Les couleurs sont peu moches. Et difficile à discerner les unes des autres.
Les couleurs de ggplot2
par défaut ne sont pas incroyables.
Personnellement, j’aime bien utiliser celle de Tableau.
Vous pouvez les retrouver ici : Palettes de couleur de Tableau Software.
Dans notre cas, comme on a 15 types, je prends la palette Tableau 20.
Et comme la légende va prendre beaucoup de place, je vais réduire sa taille :
Les couleurs, c’est mieux.
Mais 15 couleurs, ce sera toujours difficile à discerner.
En fait, c’est super dur de voir 15 catégories à la fois sur un graphe.
Certes on pourrait faire un diagramme en barres, mais là l’objectif principal ça reste de voir la relation entre Max CP
et Max HP
.
Du coup… compromis.
Je vais faire le 2e choix.
Si on fait un tableau de fréquences, on peut voir que pas mal de types sont assez rares :
Si je garde seulement les types avec au moins 10 pokemons et que je regroupe le reste dans “Other”, je m’en sors avec seulement 7 catégories
J’ai aussi ré-ordonné les facteurs pour que “Other” se retrouve bien à la fin.
Voyons ce que ça donne :
Pas mal !
On distingue bien les couleurs.
Et je n’ai plus besoin de réduire la taille de la légende.
Alors, quoi d’autre ?
Où est Pikachu ?
Et qui est ce pokemon tout à droite qui semble faible mais avec beaucoup d’HPs ?
Qui sont les pokemons les plus forts ?
On peut ajouter des annotations pour faire transparaître ces informations sur le graphique :
Et voici une version plus grande du graphique final (cliquez pour zoomer) :
Pas mal !
Finalement ce n’était pas si dur..
Et remarquez la différence entre le premier graphique et le dernier.
ggplot2
est une librairie de visualisation très puissante pour faire de beaux graphiques.
On peut prendre bbplot
comme point de départ, puis itérer jusqu’à obtenir le résultat voulu.