Données réactives Shiny : filtrer et transformer un data frame depuis les entrées

Construisez un seul reactive() qui filtre et transforme vos données depuis les entrées, puis réutilisez-le dans chaque sortie — calculer une fois, afficher partout.

Programming
Shiny

La plupart des applications filtrent ou transforment les mêmes données pour une table, un graphique et un résumé. Le faire à l’intérieur de chaque render répète le travail et ralentit l’application. La solution est un seul reactive() qui traite les données une seule fois depuis les entrées ; chaque sortie le lit. Apprenez à extraire un sous-ensemble d’un data frame selon les valeurs des entrées, à protéger avec req(), à piloter plusieurs sorties à partir d’un seul reactive, et à enchaîner des reactives en un pipeline.

Date de publication

28 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • Traitez les données une seule fois, dans un seul reactive(). Filtrer ou transformer à l’intérieur de chaque render*() répète le même travail sur chaque sortie et ralentit l’application. Un seul reactive() le fait une fois et met le résultat en cache.
  • Lisez les entrées dans un sous-ensemble en base R. df[df$col >= input$min, ], subset() et aggregate() transforment les valeurs des entrées en un data frame filtré ou résumé — sans package supplémentaire.
  • Protégez avec req(). req(input$col) empêche le reactive de s’exécuter sur une entrée NULL ou vide, de sorte que les sorties attendent en silence au lieu de provoquer une erreur pendant le chargement de la page.
  • Chaque sortie lit le même reactive. Appelez data() dans la table, le graphique et le résumé — tous partagent un seul calcul mis en cache.
  • Enchaînez des reactives pour un pipeline. Un reactive filtered() peut alimenter un reactive summary() ; chaque étape ne se recalcule que lorsque ses propres entrées changent.

Introduction

Un tableau de bord montre presque toujours la même tranche de données de trois manières : une table des lignes, un graphique de celles-ci et un résumé d’une ligne. La tranche dépend de l’utilisateur — une valeur minimale, un groupe choisi, une plage de dates. La façon tentante de le construire est de filtrer les données à l’intérieur de chaque sortie :

output$table   <- renderTable({ mtcars[mtcars$mpg >= input$min_mpg, ] })
output$plot    <- renderPlot({  plot(mtcars[mtcars$mpg >= input$min_mpg, ]) })
output$count   <- renderText({  nrow(mtcars[mtcars$mpg >= input$min_mpg, ]) })

Cela filtre les données trois fois à chaque changement du curseur — le même sous-ensemble, calculé encore et encore. Avec mtcars c’est sans conséquence ; avec un vrai jeu de données et une transformation plus lourde, c’est la différence entre une application qui semble instantanée et une qui saccade. Et c’est répétitif : changez la logique du filtre et vous l’éditez à trois endroits.

La programmation réactive vous a donné l’outil pour résoudre ces deux problèmes : reactive(), une valeur calculée une seule fois à partir de ses entrées et réutilisée partout. Cette leçon en présente la tâche la plus courante — le pipeline de données réactif : filtrer et transformer les données à un seul endroit, puis laisser chaque sortie le lire.

NoteCopiez n’importe quel bloc dans app.R et exécutez-le en local

Le code de cette page n’est pas exécuté ici — une application Shiny a besoin d’une session R active, elle ne peut donc pas tourner dans une page web statique. Copiez n’importe quel bloc dans un fichier app.R et exécutez-le en local avec shiny::runApp() (ou cliquez sur Run App dans RStudio). Chaque extrait se dépose dans la fonction server d’une application.

La solution : un seul reactive, chaque sortie le lit

Sortez le filtre des renders et placez-le dans un seul reactive(). Il calcule le sous-ensemble une fois ; la table, le graphique et le résumé l’appellent chacun. C’est le patron phare — calculer une fois, afficher partout.

server <- function(input, output, session) {

  # filter ONCE, from the inputs
  filtered <- reactive({
    mtcars[mtcars$mpg >= input$min_mpg, ]
  })

  # every output reads the SAME cached result with ()
  output$table <- renderTable({ filtered() })
  output$plot  <- renderPlot({  plot(filtered()$wt, filtered()$mpg) })
  output$count <- renderText({  paste(nrow(filtered()), "cars") })
}

Quand le curseur bouge, filtered() se recalcule une seule fois ; Shiny met le résultat en cache et remet le même data frame aux trois sorties. Vous le lisez en l’appelant — filtered(), avec les parenthèses — exactement comme n’importe quel reactive. Changez la règle du filtre et vous l’éditez à un seul endroit. C’est toute l’idée : le pipeline de données vit dans le reactive, les sorties ne font que l’afficher.

Lire les entrées dans un filtre

Le corps du reactive est du R ordinaire — vous extrayez un sous-ensemble d’un data frame à l’aide des valeurs des entrées. Base R vous offre trois manières lisibles de le faire, sans package supplémentaire.

Extraire un sous-ensemble selon une condition avec [. La plus directe : garder les lignes qui correspondent.

filtered <- reactive({
  iris[iris$Sepal.Length >= input$min_length, ]
})

Filtrer selon une catégorie choisie avec subset() ou %in%. Quand l’entrée est un groupe sélectionné (ou plusieurs), %in% se lit clairement :

filtered <- reactive({
  iris[iris$Species %in% input$species, ]   # input$species is a character vector
})

Combiner des conditions. Enchaînez-les avec & à l’intérieur du [ — un seuil de valeur et un groupe choisi :

filtered <- reactive({
  iris[iris$Sepal.Length >= input$min_length & iris$Species %in% input$species, ]
})

Le reactive renvoie toujours le data frame traité ; ce que vous mettez à l’intérieur n’est que du R. Gardez la transformation ici — extraire le sous-ensemble, puis ajouter une colonne, puis trier — pour que les sorties reçoivent des données prêtes à afficher.

Transformer, pas seulement filtrer

Un pipeline remodèle souvent les données, et ne fait pas que les restreindre. Agrégez-les, ajoutez une colonne dérivée, triez-les — toujours à l’intérieur de l’unique reactive, toujours en base R.

Agréger en une table de résumé avec aggregate() — la moyenne des miles par gallon par nombre de cylindres :

summary_df <- reactive({
  aggregate(mpg ~ cyl, data = mtcars[mtcars$mpg >= input$min_mpg, ], FUN = mean)
})

output$table <- renderTable({ summary_df() })

Ajouter une colonne dérivée avant que la moindre sortie ne voie les données :

processed <- reactive({
  d <- mtcars[mtcars$mpg >= input$min_mpg, ]
  d$kpl <- d$mpg * 0.4251           # miles-per-gallon -> km-per-litre
  d[order(-d$kpl), ]                # sort, best first
})

Le reactive renvoie le data frame finalisé — filtré, augmenté, trié — et chaque sortie lit le même résultat prêt à afficher. Faire la transformation une fois, à un seul endroit, voilà tout l’intérêt.

Protéger le pipeline avec req()

Au démarrage (et chaque fois que l’utilisateur efface un contrôle) une entrée peut être NULL ou vide. Si votre reactive s’exécute quand même, il provoque une erreur — et l’erreur surgit dans chaque sortie qui le lit. req() arrête cela : il vérifie que ses arguments sont truthy et, si ce n’est pas le cas, arrête silencieusement le reactive de sorte que les sorties attendent simplement.

filtered <- reactive({
  req(input$species)                         # do nothing until a species is chosen
  iris[iris$Species %in% input$species, ]
})

Tant que input$species n’a pas de valeur, filtered() ne s’exécute pas et les sorties restent vides au lieu d’afficher une erreur rouge. Placez req() en haut du reactive, en listant chaque entrée dont le corps a besoin. C’est l’unique ligne qui fait qu’un pipeline piloté par les entrées se comporte bien sur un démarrage vide.

Notereq() vs validate()

req() arrête silencieusement — utilisez-le pour « l’utilisateur n’a pas encore choisi ». Quand vous voulez plutôt afficher un message (« Choisissez au moins un groupe »), tournez-vous vers validate(need(...)), qui affiche le message dans la sortie. req() pour le silence, validate() pour une incitation visible.

Enchaîner des reactives en un pipeline

Quand le traitement comporte des étapes — filtrer, puis résumer — vous n’êtes pas obligé de les entasser dans un seul reactive. Séparez- les : un reactive filtered() alimente un reactive summary(). Chaque étape se met en cache indépendamment et ne se recalcule que lorsque ses entrées changent.

server <- function(input, output, session) {

  # stage 1: filter the rows
  filtered <- reactive({
    req(input$species)
    iris[iris$Species %in% input$species, ]
  })

  # stage 2: summarise the filtered data — reads filtered()
  means <- reactive({
    aggregate(Sepal.Length ~ Species, data = filtered(), FUN = mean)
  })

  output$rows  <- renderTable({ filtered() })   # the rows
  output$means <- renderTable({ means() })       # the per-group means
}

means() dépend de filtered(), qui dépend de input$species. Changez l’espèce et les deux se recalculent ; mais si vous ajoutez plus tard une entrée que seul means() lit, filtered() ne se réexécutera pas pour elle. La chaîne garde le travail de chaque étape isolé — le même gain DRY-et-rapide que reactive() apporte à une seule étape, maintenant à travers un pipeline.

Quelle approche choisir

Les pièces ci-dessus répondent à deux questions : combien faire dans un seul reactive, et comment extraire le sous-ensemble.

Vous voulez… Tournez-vous vers
Filtrer/transformer une fois, partager entre les sorties un seul reactive(), lu avec data() dans chaque sortie
Garder les lignes qui correspondent à une condition df[df$col >= input$min, ]
Garder les lignes d’un groupe choisi (ou de plusieurs) df[df$col %in% input$group, ]
Agréger en une table de résumé aggregate(y ~ g, data = df, FUN = mean)
Attendre qu’une entrée ait une valeur req(input$x) en haut du reactive
Traiter par étapes (filtrer → résumer) enchaîner des reactives : summary() lit filtered()

La règle générale : faites le travail sur les données dans des reactives, gardez les sorties minces. Un render*() devrait lire un reactive et l’afficher — pas filtrer, transformer et résumer à partir de zéro à chaque exécution.

Problèmes fréquents

Filtrer à l’intérieur de chaque render au lieu d’un seul reactive. Si output$table, output$plot et output$count écrivent chacun df[df$col >= input$min, ], le sous-ensemble est calculé une fois par sortie, à chaque changement — trois fois le travail et trois endroits à éditer. Sortez-le dans un filtered <- reactive({ … }) et faites appeler filtered() par chaque sortie. Calculer une fois, afficher partout.

Oublier les () quand vous lisez le reactive. filtered est l’objet reactive — une fonction ; filtered() est le data frame courant. Écrire nrow(filtered) (sans parenthèses) vous donne la fonction, pas les lignes, et la sortie casse. Appelez-le toujours : nrow(filtered()), renderTable({ filtered() }).

Pas de req(), donc l’application provoque une erreur sur une entrée vide. Extraire un sous-ensemble avec une entrée NULL (iris[iris$Species %in% NULL, ] au démarrage, avant que l’utilisateur ne choisisse quoi que ce soit) ne renvoie rien d’utile et lève souvent une erreur — et l’erreur s’affiche dans chaque sortie qui lit le reactive. Ajoutez req(input$species) en haut du reactive pour qu’il attende une vraie valeur au lieu de s’exécuter à vide.

Questions fréquentes

Extrayez un sous-ensemble à l’intérieur d’un reactive() à l’aide de la valeur de l’entrée : filtered <- reactive({ df[df$col >= input$min, ] }) pour un seuil numérique, ou df[df$col %in% input$group, ] pour une catégorie choisie. Puis lisez-le dans chaque sortie en appelant filtered(). Faire le filtre dans un reactive — et non dans chaque render — le calcule une fois et le partage.

Mettez le traitement dans un seul reactive() et appelez-le depuis chaque sortie. Créez data <- reactive({ … }), puis lisez data() à l’intérieur de renderTable(), renderPlot() et renderText(). Shiny calcule le reactive une fois par changement et met le résultat en cache, de sorte que toutes les sorties partagent les mêmes données sans les recalculer.

Sortez le calcul des blocs render*() et placez-le dans un seul reactive(). Un reactive() exécute son corps une fois quand ses entrées changent, met la valeur en cache et remet le même résultat à chaque sortie qui l’appelle — de sorte que le travail se fait une fois au lieu d’une fois par sortie. Filtrer ou transformer directement à l’intérieur de chaque render répète le travail à chaque fois.

req() vérifie que ses arguments ont une valeur utilisable et, si ce n’est pas le cas, arrête silencieusement le reactive de s’exécuter — de sorte que les sorties attendent en silence au lieu de provoquer une erreur sur une entrée NULL ou vide au démarrage. Placez req(input$x) en haut du reactive, en listant les entrées dont le corps a besoin. Utilisez validate(need(...)) plutôt quand vous voulez afficher un message à l’utilisateur.

Oui — un reactive peut en lire un autre. Un reactive filtered() peut alimenter un reactive summary() (aggregate(y ~ g, data = filtered(), …)), formant un pipeline. Chaque étape se met en cache pour elle-même et se recalcule seulement quand ses propres entrées changent, ce qui garde le traitement en plusieurs étapes à la fois DRY et rapide.

Testez vos connaissances

Partez d’une application dont l’UI a un curseur sliderInput("min_mpg", "Min mpg", 10, 35, 20) et trois sorties (une table, un graphique, un compteur). Réécrivez le server pour que les lignes de mtcars dont le mpg est au niveau du curseur ou au-dessus soient filtrées une seule fois dans un unique reactive, et que la table, le graphique et le compteur lisent tous cet unique reactive.

Créez filtered <- reactive({ mtcars[mtcars$mpg >= input$min_mpg, ] }) une fois, puis appelez filtered() à l’intérieur de chaque render*(). Lisez-le avec les parenthèses — filtered(), et non filtered — pour obtenir le data frame, et non la fonction.

server <- function(input, output, session) {

  filtered <- reactive({
    mtcars[mtcars$mpg >= input$min_mpg, ]    # filter once
  })

  output$table <- renderTable({ filtered() })
  output$plot  <- renderPlot({  plot(filtered()$wt, filtered()$mpg) })
  output$count <- renderText({  paste(nrow(filtered()), "cars") })
}

filtered calcule le sous-ensemble une fois par changement du curseur ; Shiny le met en cache et les trois sorties appellent chacune filtered() pour réutiliser le même data frame — calculer une fois, afficher partout, avec la logique du filtre à un seul endroit.

Votre application filtre le même data frame pour une table, un graphique et un résumé, le tout depuis un seul curseur. Où le filtrage devrait-il vivre ?

A. À l’intérieur de chaque render*() — répéter df[df$col >= input$min, ] dans les trois. B. Dans un seul reactive() que chaque sortie lit avec data(). C. Dans un observeEvent() sur le curseur. D. Dans une simple variable définie une fois en haut de server.

B. Un seul reactive() filtre une fois par changement et met le résultat en cache ; la table, le graphique et le résumé appellent chacun data() pour le réutiliser — calculer une fois, afficher partout. A répète le travail trois fois et la logique à trois endroits. C sert aux effets de bord (enregistrer, notifier), pas à produire une valeur que les sorties lisent. D ne fonctionne pas — une simple variable n’est pas réactive, donc elle ne se met jamais à jour quand le curseur bouge.

Conclusion

Un pipeline de données Shiny appartient à un reactive(), pas à vos sorties. Filtrez et transformez les données une seule fois depuis les entrées — l’extraction de sous-ensembles en base R (df[df$col >= input$min, ], %in%, aggregate()) suffit — et laissez la table, le graphique et le résumé lire chacun le même reactive mis en cache avec data(). Protégez-le avec req() pour qu’il attende de vraies entrées au lieu de provoquer une erreur sur un démarrage vide, et enchaînez des reactives quand le traitement comporte des étapes. Le gain est celui pour lequel Shiny est conçu : calculer une fois, afficher partout — rapide à mesure que les données grandissent, avec la logique à un seul endroit.

Ensuite, vous brancherez ce pipeline sur des conditions — afficher des sorties différentes, ou traiter de différentes manières, selon ce que choisit l’utilisateur.

Leçons connexes

Cette page vous a-t-elle été utile ?

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Données réactives Shiny : filtrer et transformer un data
    frame depuis les entrées},
  date = {2026-06-28},
  url = {https://www.datanovia.com/learn/programming/shiny/server/data-processing},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Données réactives Shiny : filtrer et transformer un data frame depuis les entrées.” 2026. June 28. https://www.datanovia.com/learn/programming/shiny/server/data-processing.