9. Quelques outils de visualisation dynamique/interactive

Tout comme leaflet pour les cartes, il existe de nombreux outils R dédiés à la visualisation interactive. Nous en présentons quelques uns dans cette partie.

Représentations classiques avec rAmCharts, plotly et highcharter

Le package rAmCharts est très utile pour donner un caractère interactif à des représentations graphiques standards (nuages de points, séries temporelles, histogrammes…). Ce package a été fait dans l’esprit d’utiliser les fonctions graphiques de R en ajoutant le préfixe am. La syntaxe est très proche de celle des fonctions graphiques standards. On a par exemple :

library(rAmCharts)
amHist(iris$Petal.Length)
amPlot(iris, col = colnames(iris)[1:2], type = c("l", "st"), 
       zoom = TRUE, legend = TRUE)
amBoxplot(iris)

plotly permet de faire des choses semblables avec avec une syntaxe spécifique. Les commandes plotly se décomposent essentiellement en 3 parties :

  • le type de représentation graphique (plot_ly}) ;
  • les ajouts que l’on souhaite effectuer (add_trace) ;
  • la gestion de la fenêtre graphique (axes, titres…) (layout).

On trouvera un descriptif complet de ces 3 composantes ici. On propose de tracer un nuage de points en dimension 2 et d’y ajouter la droite de régression. On commence par générer le nuage et ajuster le modèle linéaire :

library(plotly)
n <- 100
X <- runif(n,-5,5)
Y <- 2+3*X+rnorm(n,0,1)
D <- data.frame(X,Y)
model <- lm(Y~X,data=D)

On effectue maintenant le tracé

D |> plot_ly(x=~X,y=~Y) |>
  add_markers(type="scatter",mode="markers",
              marker=list(color="red"),name="Nuage") |>
  add_trace(y=fitted(model),type="scatter",mode='lines',
            name="Régression",line=list(color="blue")) |> 
  layout(title="Régression",xaxis=list(title="abscisse"),
         yaxis=list(title="ordonnées"))

Contrairement à ggplot, plotly permet de faire de la 3D. Par exemple

plot_ly(z = volcano, type = "surface")
plot_ly(z = volcano, type = "contour")

Il est possible de convertir des graphes ggplot au format plotly avec la fonction ggplotly :

p <- ggplot(iris)+aes(x=Species,y=Sepal.Length)+geom_boxplot()+theme_classic()
ggplotly(p)

highcharter est un package R basé sur la librairie JavaScript Highcharts. Il pproduit des graphiques interactifs, élégants et hautement personnalisables, et est très bien adapté pour des applications web, dashboards (Shiny, Flexdashboard, quarto), ou rapports interactifs.

  • Fonctions principales : hchart() (graphes simples) et highchart() (graphes plus complexes).
  • Prend en charge de nombreux types de graphiques : ligne, barres, bulles, secteurs, heatmaps, etc.
  • hc_theme_... pour des thèmes prédéfinis et hc_tooltip pour personnaliser les info-bulles.

On peut par exemple faire un nuage de points avec

library(highcharter)
hchart(iris, "scatter", 
       hcaes(x = Sepal.Length, y = Sepal.Width, 
             group = Species))

Pour un diagramme en barres

data <- iris |> count(Species)
highchart() |> 
  hc_chart(type = "column") |> 
  hc_xAxis(categories = data$Species) |> 
  hc_add_series(name = "Effectif", data = data$n) |> 
  hc_title(text = "Nombre d'individus par espèce") |>
  hc_add_theme(hc_theme_google())

Highcharter est également souvent utilisé pour des séries temporelles :

library(forecast)
airforecast <- forecast(auto.arima(AirPassengers), level = 95)
hchart(airforecast)

Exercice 1 (Graphes standards avec rAmCharts et plotly) Pour le jeu de données iris on effectuera les graphes suivants en rAmCharts et plotly.

  1. Nuage de points représentant les longueurs et largeurs de Sépales. On utilisera une couleur différente en fonction de l’espèce.

  2. Boxplot permettant de visualiser la distribution de la variable Petal.Length en fonction de l’espèce.

Exercice 2 (Plotly pour visualiser espérance de vie vs PIB) On considère les données gapminder :

library(gapminder)
head(gapminder)
# A tibble: 6 × 6
  country     continent  year lifeExp      pop gdpPercap
  <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
1 Afghanistan Asia       1952    28.8  8425333      779.
2 Afghanistan Asia       1957    30.3  9240934      821.
3 Afghanistan Asia       1962    32.0 10267083      853.
4 Afghanistan Asia       1967    34.0 11537966      836.
5 Afghanistan Asia       1972    36.1 13079460      740.
6 Afghanistan Asia       1977    38.4 14880372      786.
  1. Pour une année de votre choix, faire un nuage de points représentant l’espérance de vie en fonction du PIB par habitant, en utilisant plot_ly et add_markers.

    plot_ly(data = data_annee, 
            x = ~..., 
            y = ~..., 
            type = 'scatter', 
            mode = 'markers', 
            color = ~..., 
            size = ~...,
            text = ~...)

    On pourra également ajouter

    layout(xaxis(type="log"))

    pour avoir une échelle logarithmique sur l’axe des x.

  2. Animer le graphe précédent en faisant bouger les points en fonctions de l’année.

    On pourra utiliser frame dans plot_ly pour animer le graphe et la fonction animation_opts pour contrôler la vitesse de l’animation.

Exercice 3 (Highcharter)  

  1. Faire le diagramme en barres de la variable cut du jeu de données diamonds en utilisant le package highcharter.

    On peut utiliser la fonction hchart pour créer un diagramme en barres :

    library(highcharter)
    effectifs <- diamonds |> count(cut)
    hchart(effectifs,"...",hcaes(x=...,y=...,color=...))
  2. Faire le même graphe en utilisant les fonctions highchart et hc_add_series et ajouter un tooltip qui affiche l’effectif de chaque modalité de cut.

    highchart() |> 
      hc_add_series(effectifs,...)) |> 
      hc_tooltip(pointFormat = "...")
  3. Ajouter un graphique en secteurs (pie chart) représentant la répartition des modalités de cut dans le même graphique.

    On pourra utiliser hc_add_series avec type="pie" pour ajouter un graphique en secteurs.

Exercice 4 (Séries temporelles avec highcharter) On reprend les données gapminder et on calcule l’espérance de vie moyenne en fonction du continent et de l’année avec

don <- gapminder |> 
  group_by(year,continent) |> 
  summarize(lifeExp = mean(lifeExp)) |> 
  mutate(year=ymd(paste0(year,"01-01")))
head(don)
# A tibble: 6 × 3
# Groups:   year [2]
  year       continent lifeExp
  <date>     <fct>       <dbl>
1 1952-01-01 Africa       39.1
2 1952-01-01 Americas     53.3
3 1952-01-01 Asia         46.3
4 1952-01-01 Europe       64.4
5 1952-01-01 Oceania      69.3
6 1957-01-01 Africa       41.3
  1. Représenter l’évolution de l’espérance de vie pour chaque continent avec highcharter.

    On pourra utiliser hc_add_series et group=continent dans hcaes pour colorer les lignes par continent.

  2. Ajouter l’option type=stock dans highchart et analyser la sortie.

    L’option type="stock" permet de zommer sur des périodes des séries.

Dynamiser ggplot2 avec gganimate et ggiraph.

ggiraph est une extension de ggplot2 qui produit des graphes interactifs. On pourra trouver une documentation complète à l’url https://www.ardata.fr/ggiraph-book/. D’une façon générale, les graphes s’obtiennent en utilisant la syntaxe de ggplot2 et en ajoutant le suffixe _interactive aux fonctions geom. Par exemple

library(ggiraph)
p <- ggplot(data=iris) +
  aes(x=Sepal.Length,y=Sepal.Width,
      tooltip=Petal.Width,data_id=Species) + 
  geom_point_interactive(size=3,hover_nearest=TRUE)

On visualise le graphe avec la fonction girafe :

girafe(ggobj = p)

Le graphe est dynamique dans le sens où lorsque la souris approche un point

  • les points de la même espèce que le point où se trouve la souris se colorie (option data_id) ;
  • la largeur de pétale du point s’affiche (option tooltip).

Il est bien entendu possible de personnaliser le caractère dynamique. On peut par exemple introduire du css pour changer la couleur (ou les autres attributs) des points qui se colorient lorsque la souris parcourt le graphe :

girafe(ggobj = p,
       options=list(
         opts_hover(css="fill:yellow;stroke:black;stroke-width:2px;")
       )
)

Le coté interactif peut s’obtenir en permettant à l’utilisateur de sélectionner des parties du graphe, par exemple des individus sur une nuage de points

preselection <- iris$Species[1]
girafe(ggobj = p,
       options=list(
         opts_selection(
           selected = preselection,
           css="fill:yellow;stroke:black;stroke-width:1px;",
           type="single",only_shiny = FALSE
         )
       ))

gganimate est un package qui permet de faire des animations à partir de graphes ggplot2. Il est très utile pour visualiser l’évolution d’une variable au cours du temps ou d’une autre variable. On pourra trouver une documentation complète à l’url https://gganimate.com/.

IndicationsFonctions principales
  • transition_... : comment les données sont réparties et liées dans le temps au fil de l’animation :
    • transition_states : pour animer les transitions entre différents états (catégories).
    • transition_time : pour animer en fonction du temps (variable continue).
  • view_... : vue de l’animation, comme le zoom ou la position de la caméra.
    • view_follow : pour suivre un point ou une trajectoire.
    • view_static : pour une vue fixe.
IndicationsFonctions avancées
  • shadow_... : pour ajouter des ombres ou des effets de flou aux objets animés.
  • enter_... et exit_... : pour contrôler l’apparition et la disparition des objets dans l’animation.
  • ease_aes : pour contrôler la vitesse de l’animation.

On peut par exemple animer un nuage de points des iris en faisant bouger les points en fonction de l’espèce avec :

library(gganimate) #nécessite le package gikski
ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +
  geom_point() +
  transition_states(Species, transition_length = 2, 
                    state_length = 1) +
  labs(title = "Espèce : {closest_state}")

Exercice 5 (Diagramme en barres dynamique) On considère le graphe diagramme en barres suivant

ggplot(diamonds)+aes(x=cut,fill=color)+geom_bar()

  1. Créer un graphe dynamique qui permet de visualiser la valeur de la variable color (la lettre) lorsque la souris parcourt sur le graphe.

    Utiliser tooltip dans aes et geom_bar_interactive.

  2. Faire la même chose en affichant cette fois les effectifs de la classe à la place de la valeur de color. On pourra utiliser la fonction after_stat

  3. Ajouter au graphe précédent une sélection qui identifie toutes les barres d’une même valeur de color (en les coloriant en rouge).

    Utiliser data_id dans aes.

Exercice 6 (gganimate : syntaxe)  

  1. Crée un tibble avec trois colonnes : x (allant de 1 à 10), y (égal à 1) et time (identique à x).

  2. Utilise ggplot pour afficher le point (geom_point), et gganimate pour animer le déplacement du point au fil du temps.

    Utiliser transition_time pour animer le point en fonction de la colonne time.

  3. Arrangez-vous pour que la frame de l’animation suive la colonne time : c’est à dire qu’on passe directement du point 1 au point 2 puis 3…

    Utiliser animate avec l’option nframes.

Exercice 7 (Animations sur un nuage de points) On considère les données gapminder :

library(gapminder)
head(gapminder)
# A tibble: 6 × 6
  country     continent  year lifeExp      pop gdpPercap
  <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
1 Afghanistan Asia       1952    28.8  8425333      779.
2 Afghanistan Asia       1957    30.3  9240934      821.
3 Afghanistan Asia       1962    32.0 10267083      853.
4 Afghanistan Asia       1967    34.0 11537966      836.
5 Afghanistan Asia       1972    36.1 13079460      740.
6 Afghanistan Asia       1977    38.4 14880372      786.

et le graphe suivant

big_countries <- c("China", "India", "United States", "Brazil", "Russia")
p <- ggplot(gapminder, aes(
    x = gdpPercap, 
    y = lifeExp, 
    size = pop, 
    color = continent
)) +
  geom_point(alpha = 0.7, show.legend = TRUE) +
  scale_x_log10() +
  scale_size(range = c(2, 14), guide = "none") +
  geom_text(
    data = filter(gapminder, country %in% big_countries),
    aes(label = country),
    color = "black", size = 3, vjust = -1, show.legend = FALSE
  ) +
  labs(
    title = "Espérance de vie vs PIB par habitant",
    x = "PIB par habitant (log)",
    y = "Espérance de vie (années)",
    color = "Continent"
  )
p

qui permet de visualiser l’espérance de vie en fonction du PIB pour toutes les années.

  1. Créer un graphique similaire à celui ci-dessus en animant le graphique pour chaque année.

    On pourra utiliser transition_time pour animer le graphique en fonction de l’année.

  2. Contrôler la vitesse de l’animation avec animate.

    On pourra utiliser animate avec l’option nframes.

Graphes pour visualiser des réseaux avec visNetwork

De nombreuses données peuvent être visualisées à l’aide d’un graphe, notamment lorsqu’il s’agit de représenter des connexions entre individus. Un individu est alors représentés par un noeud et les individus connectés sont reliés par des arêtes. Le package igraph propose une visualisation statique d’un réseau. Pour donner un caractère dynamique à ce type de représentation, on pourra utiliser le package visNetwork. Une représentation standard visNetwork s’effectue en spécifiant les nœuds et connexions d’un graphe, par exemple :

nodes <- tibble(id = 1:15, label = paste("Id", 1:15),
                    group=sample(LETTERS[1:3], 15, replace = TRUE))
edges <- tibble(from = trunc(runif(15)*(15-1))+1,to = trunc(runif(15)*(15-1))+1)
library(visNetwork)
visNetwork(nodes,edges)
visNetwork(nodes, edges) |> visOptions(highlightNearest = TRUE)
visNetwork(nodes, edges) |> visOptions(highlightNearest = TRUE,
                                        nodesIdSelection = TRUE)


visNetwork(nodes, edges) |> visOptions(selectedBy = "group")

Exercice 8 (Interactions entre media) On considère un graphe qui représente des liens entre différents médias. Les données sont présentées ici et on peut les importer avec

nodes <- read_csv("data/Dataset1-Media-Example-NODES.csv")
links <- read_csv("data/Dataset1-Media-Example-EDGES.csv")
head(nodes)
# A tibble: 6 × 5
  id    media               media.type type.label audience.size
  <chr> <chr>                    <dbl> <chr>              <dbl>
1 s01   NY Times                     1 Newspaper             20
2 s02   Washington Post              1 Newspaper             25
3 s03   Wall Street Journal          1 Newspaper             30
4 s04   USA Today                    1 Newspaper             32
5 s05   LA Times                     1 Newspaper             20
6 s06   New York Post                1 Newspaper             50
head(links)
# A tibble: 6 × 4
  from  to    weight type     
  <chr> <chr>  <dbl> <chr>    
1 s01   s02       10 hyperlink
2 s01   s02       12 hyperlink
3 s01   s03       22 hyperlink
4 s01   s04       21 hyperlink
5 s04   s11       22 mention  
6 s05   s15       21 mention  

L’objet nodes représente les nœuds du graphe et l’objet links les arêtes. On définit l’objet graphe avec

library(igraph)
media <- graph_from_data_frame(d=links, vertices=nodes, directed=T) 
V(media)$name <- nodes$media

et on peut le visualiser en faisant un plot de cet objet

plot(media)

  1. Visualiser ce graphe avec VisNetwork. On pourra utiliser la fonction toVisNetworkData.

  2. Ajouter une option qui permette de sélectionner le type de media (Newspaper, TV ou Online).

  3. Utiliser une couleur différente pour chaque type de media.

  4. Faire des flèches d’épaisseur différente en fonction du poids (weight). On pourra également ajouter l’option visOptions(highlightNearest = TRUE).

    Il suffit de donner le nom width ou value à la variable weight.

Dashboard

Un tableau de bord permet de visualiser “facilement” et “rapidement” divers graphes et/ou résumés statistiques en lien avec une problématique donnée. Sur R le package flexdashboard permet de construire de tels tableaux de bord. On trouvera un descriptif précis de ce package à cette url : https://rmarkdown.rstudio.com/flexdashboard/. Quarto vient également de proposer de construire des dashboard en utilisant l’entête

--- 
title: "Mon dashboard"
author: "LR"
format: dashboard
--- 

La documentation est disponible à cet url : https://quarto.org/docs/dashboards/. On utilisera cette documentation pour faire l’exercice suivant.

Exercice 9 (Dashboard pour modèles linéaires) On considère le jeu de données ozone.txt. Le problème est d’expliquer la concentration maximale en ozone quotidienne (variable maxO3) par d’autres variables météorologiques (températures et indicateurs de nébulosité relevés à différents moments de la journée…). On souhaite faire un tableau de bord qui permettra de :

  • visualiser les données : la base de données ainsi qu’un ou deux graphes descriptifs sur la variable à expliquer ;

  • visualiser les modèles linéaires simples : on choisit une variable explicative et on visualise le graphe de la régression ainsi que le modèle ;

  • visualiser le modèle linéaire complet : on affiche le résultat de la régression avec toutes les variables et on représente le graphe des résidus ;

  • choisir les variables explicatives du modèle et afficher les sorties;

  1. Avant de réaliser le dashboard, on propose d’écrire quelques commandes pour calculer les différentes sorties :

    1. On considère uniquement les variables quantitatives du jeu de données. Visualiser les corrélations entre ces variables à l’aide de la fonction corrplot du package corrplot.

    2. Représenter l’histogramme de la variable maxO3, on fera le graphe avec ggplot, rAmCharts, plotly (en utilisant ggplotly par exemple) et ggiraph.

    3. Construire le modèle linéaire permettant d’expliquer maxO3 par les autres variables. Calculer les résidus studentisés (rstudent) et visualiser ces résidus en fonction de la variable maxO3. Là encore on pourra ajouter un lisseur sur le graphe.

  2. On peut maintenant passer au tableau de bord. Créer un document quarto et y insérer l’entête pour faire le tableau de bord et cnstruire un premier onglet permettant de visualiser

    • le jeu de données sur une colonne (on pourra utiliser la fonction datatable du package DT).

    • l’histogramme de la variable maxO3 ainsi que la matrice des corrélations entre les variables quantitatives sur une deuxème colonne.

    On pourra utiliser la structure suivante :

    #  Statistiques descriptives
    
    ## column {width=60%}
    
    ###  Données
    
    ``{r}
    #| title: Les données
    
    ...
    ```
    
    ## column {width=40%}
    
    ###  Row
    
    ``{r}
    #| title: Matrice des corrélations
    
    ```
    
    ###  Row
    
    ``{r}
    #| title: La variable à expliquer
    
    ```
    
  3. Ajouter un nouvel onglet qui permet de visualiser le summary du modèle linéaire complet. On pourra utiliser la fonction datatable du package DT. Indication : ce nouvel onglet peut se créer avec

    ``` markdown
    Name of the tab
    =====================================  
    ```
  4. Ajouter un nouvel onglet qui permet de visualiser un modèle linéaire simple avec la variable explicative de votre choix. On pourra afficher dans cet onglet le summary du modèle ainsi que le nuage de points et la droite de régression.

  5. Pour aller plus loin : ajouter un dernier onglet qui permette à l’utilisateur de choisir la variable explicative du modèle simple. Indications : on pourra utiliser les commandes Shiny

    • Choix de la variable

      radioButtons("variable1",
                    label="Choisir la variable explicative",
                    choices=names(df)[-1],
                    selected=list("T9"))
    • Mise à jour du résumé et du graphe interactif. Il faut définir un chunk server avec l’otion #| context: server pour calculer les sorties :

      mod1 <- reactive({
        XX <- paste(input$variable1,collapse="+")
        form <- paste("maxO3~",XX,sep="") |> formula()
        lm(form,data=df)
      }
      )
      
      output$sum1 <- DT::renderDT({
        mod.sum1 <- summary(mod1())$coefficients |> round(3) |> as.data.frame()
        DT::datatable(mod.sum1,options = list(dom = 't'))
      })
      
      output$resplotly <- renderPlotly({
        (ggplot(df)+
           aes(x=!!as.name(input$variable1),y=maxO3)+
           geom_point()+geom_smooth(method="lm")) %>% 
          ggplotly()
      })

    Enfin il ne faudra pas oublier d’ajouter

    server: shiny
  6. Ajouter un dernier onglet permettant de choisir les variables explicatives dans le modèle linéaire. Là encore on pourra utiliser des commandes Shiny, par exemple

    checkboxGroupInput("variable",
                       label="Choisir la variable",
                       choices=names(df)[-1],
                       selected=list("T9"))

    Pour les variables choisies, on affichera dans ce nouvel onglet les coefficients du modèle linéaire ainsi que le graphe des résidus studentisés.

Le tableau de bord finalisé pourra ressembler à

Il est disponible à l’url https://lrouviere.shinyapps.io/dashboard/.