Quoi de neuf avec ggiraph

David Gohel

2019/05/24


ggiraph 0.6.1 a évolué et il convient de faire une annonce rapide pour présenter les quelques travaux qui ont été réalisés ces derniers temps.

ggiraph, qu’est ce que c’est?

Le package ggiraph permet de travailler avec ggplot et de produire des graphiques interactifs. Le nombre de fonctionnalités est restreint et l’utilisation simple. Les trois fonctionnalités à retenir sont

  • la capacité à animer les points, polygones et lignes,
  • l’affichage de tooltip quand la souris passe sur ces éléments
  • la capacité de sélectionner ces éléments quand le graphique est affiché dans une application shiny.

Un exemple avec des chats qui miaulent

Une illustration s’impose… On va représenter les répartitions des chats et chiens parmi les animaux de compagnie aux Etats Unis. On va afficher un tooltip, animer les barres et le click déclenchera la lecture d’un son de chien ou de chat.

Tout d’abord, je défini quelques objets JavaScript. Ces objets sont des objets media qui nous permettront de jouer un son quand on cliquera sur une barre interactive.

var ouahouah = new Audio("/media/uhauha.ogg" ) ;
var meow = new Audio("/media/Meow.ogg" ) ;

Maintenant, il faut préparer les données. Le code suivant permet l’importation des données et la création d’un tableau de répartition par état.

library(ggiraph)
library(tidyverse)

# curl download file from github ----
xlfile <- tempfile()
curl::curl_download(
  url = paste0("https://github.com/davidgohel/budapestbi2017/",
    "blob/master/docs/ggiraph/data/", 
    "cats-vs-dogs.xlsx?raw=true"), destfile = xlfile)

# aggregate and prepare data for ggplot/ggiraph ----
data <- readxl::read_excel(xlfile) %>% 
  select(Location, `Percentage of Dog Owners`, `Percentage of Cat Owners`) %>% 
  set_names( c( "location", "dog", "cat") ) %>% 
  gather(animal, percentage, -location) %>% 
  mutate(clickjs = case_when(
    animal %in% "dog" ~ "ouahouah.play();",
    animal %in% "cat" ~ "meow.play();",
    TRUE ~ "coincoin.play();"
  ), data_id = paste0(location, animal) )

head(data)

location

animal

percentage

clickjs

data_id

District of Columbia

dog

13.100

ouahouah.play();

District of Columbiadog

Iowa

dog

33.400

ouahouah.play();

Iowadog

Missouri

dog

45.900

ouahouah.play();

Missouridog

Montana

dog

41.200

ouahouah.play();

Montanadog

New Jersey

dog

32.400

ouahouah.play();

New Jerseydog

Minnesota

dog

31.900

ouahouah.play();

Minnesotadog

On peut maintenant créer notre graphique ggplot, on utilise seulement geom_bar_interactive à la place de geom_bar.

gg <- ggplot( data = data, aes(x = location, y = percentage, fill = animal, 
    onclick = clickjs, data_id = data_id, tooltip = percentage ) ) + 
  geom_bar_interactive(stat = "identity") + theme_minimal() +
  scale_fill_manual(values = c("#FF4136", "#FFDC00") ) + 
  labs(title = "click on bars to play cat or dog sound!") + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
  
girafe(ggobj = gg, width_svg = 10, height_svg = 4 ) %>% 
  girafe_options(ggiraph::opts_hover(css="fill:#22222288;cursor:pointer;"))

Les nouveautés

Une nouvelle syntaxe

La fonction principale ggiraph devenait un peu lourde, le nombre d’arguments avait trop augmenté au fur et à mesure des évolutions. Les nouveaux utilisateurs ne comprenaient pas toujours quels arguments se rapportaient à quelles fonctionnalités.

Plutôt que casser l’existant, nous avons préféré implémenter de nouvelles fonctions : girafe pour transformer le ggplot en widget interactif et girafe_options pour personnaliser les effets et options de la visualisation.

girafe_options prend comme argument des appels à opts_tooltip, opts_hover, opts_zoom, opts_selection et opts_toolbar. Ces fonctions facilitent la compréhension des options offertes en les segmentant clairement. Ainsi, tout ce qui concerne le zoom peut être précisé en utilisant opts_zoom, tout ce qui est relatif aux tooltip peut être précisé avec opts_tooltip.

Illustrons cette syntaxe avec un exemple simple. Nous allons :

  • spécifier l’opacité de la couleur de fond du tooltip à 70% (opts_tooltip(opacity = .7)),
  • spécifier les niveaux de zoom autorisés, de 50% à 400% (opts_zoom(min = .5, max = 4)),
  • définir le style des points identifiables avec la couleur rouge pour le fond et grise pour le contour (opts_hover(css = "fill:red;stroke:gray;")).
library(tibble)
library(ggiraph)
library(magrittr)

gg_point <- ggplot(data = mtcars, 
  mapping = aes(x = wt, y = mpg, size = disp, color = as.factor(carb) ) ) +
  geom_point_interactive(aes(tooltip = row.names(mtcars), data_id = row.names(mtcars))) +
  scale_color_brewer(palette = "Set1", name = "carb") +
  scale_size(range = c(1, 15), name = "disp") +
  scale_x_continuous(limits = c(1, 6)) +
  scale_y_continuous(limits = c(7, 36)) +
  theme_minimal() +
  theme(legend.position = "bottom")

girafe_obj <- girafe(ggobj = gg_point) %>% 
  girafe_options(
    opts_tooltip(opacity = .7), 
    opts_zoom(min = .5, max = 4),
    opts_hover(css = "fill:red;stroke:white;stroke-width:2px;"))

Download as a png

Une nouvelle fonctionnalité de la visualisation est aussi apparue, c’est la capacité à télécharger la visualisation sous forme de png. Dans le graphique, il suffit de cliquer sur l’icone et l’image est téléchargée sur votre machine.

Cette option ne marche pas sur les anciens navigateurs. Elle peut être désactivée avec l’option opts_toolbar(saveaspng = TRUE).

Utilisation de webpack et d’ES6

Grâce à Babel et Webpack, nous avons pu réécrire complètement le code JavaScript sous forme d’un module ES6. Modulariser l’application a été l’opportunité de nettoyer et consolider le code mais aussi de mieux supporter les anciens navigateurs.

Cela a semble t-il facilité également la collaboration. Nous avons reçu l’aide de Panagiotis Skintzos qui a réalisé un super travail de revue et d’amélioration du rendu (si vous voyez un graphique ggiraph dans une application shiny depuis votre mobile, c’est grâce à lui par exemple).

visual studio code

visual studio code

Les évolutions à venir

Le projet est maintenant assez stable. Dans les prochaines itérations, les efforts porteront majoritairement sur la partie interactive. Le travail qui nous semble le plus motivant est la possibilité d’avoir une animation sur le point le plus proche du curseur et pas seulement sur le point sous le curseur.