munch

Enrichir les annotations ggplot2 avec le package ‘munch’

Ressources

Présentation Sources
https://www.ardata.fr/talks/munch-intro https://github.com/ardata-fr/munch-introduction

Introduction

‘munch’ crunches markdown text and ‘flextable’ paragraphs into grid graphics.

Sepal.Length

Sepal.Width

Petal.Length

Petal.Width

Species

5.7

3.0

4.2

1.2

versicolor

6.4

3.2

4.5

1.5

versicolor

6.0

2.9

4.5

1.5

versicolor

‘munch’ a pour objectifs de :

  • transformer des expressions markdown simples en paragraphes flextable
  • transformer des paragraphes flextables en objets graphiques ‘grid’ (grobs).

On peut ainsi annoter des graphiques R basés sur ‘grid’ (‘ggplot2’) avec des paragraphes contenant du texte riche. En bonus, ‘flextable’ bénéficie d’un support markdown simple sans pandoc/quarto.

Avant

Après

Une base de code existante

‘flextable’ contient déjà le code pour produire du texte riche dans ‘grid’ (https://www.ardata.fr/post/2023/04/03/flextable-grid/).

‘munch’ reprend la mécanique de ‘flextable’ et la dédie aux annotations ‘ggplot2’ en ajoutant le support du markdown grâce à ‘commonmark’.

Fonctionnalités principales

Deux types d’entrées sont supportés : Markdown (simple) ou chunks flextable (avancé).

Fonction Rôle Entrée
element_md() Markdown dans les éléments de thème Chaîne de caractères
element_chunks() Mise en forme avancée dans le thème Objet as_paragraph()
geom_text_md() geom texte Markdown Esthétique label
geom_label_md() geom label Markdown Esthétique label

Fonctionnalité non creusée dans cette présentation : support du markdown pour ‘flextable’ avec munch::as_paragraph_md().

Markdown dans le thème

Il faut utiliser element_md().

p_before + 
  labs(
    title = "~~Flipper~~ `and` bill **length** *(millimeters)*"
  ) +
  theme(
    plot.title = element_md(size = 12),
  )

Mise en forme avancée dans le thème

Il faut utiliser element_chunks(). Les fonctions chunk sont nombreuses : as_chunk, as_b, as_i, as_sub, as_sup, as_highlight, colorize, as_equation, as_image, gg_chunk.

a_paragraph <- as_paragraph(
  as_chunk(
    "made", 
    props = fp_text_default(font.size = 30, color = "#f2af00")
  ),
  as_chunk(
    " with ", 
    props = fp_text_default(color = "gray", font.size = 15)
  ),
  as_chunk(
    "munch", 
    props = fp_text_default(color = "#c32900", font.size = 45)
  )
)
p_before + 
  theme(
    plot.title = element_chunks(a_paragraph)
)

Dans le panneau

Il faut utiliser geom_text_md() ou geom_label_md().

note <- data.frame(
  mpg = 22, wt = 5.2,
  label = "Une **annotation** un peu *longue* se répartit automatiquement sur plusieurs lignes."
)
ggplot(mtcars, aes(mpg, wt)) +
  geom_point(alpha = .3) +
  geom_text_md(
    data = note, aes(label = label),
    width = "auto", hjust = 0, size = 4.5
  ) +
  geom_label_md(
    label = "Modèle **léger**",
    x = 28, y = 2, fill = "white"
  )

Fonctionnement

Deux entrées, ‘markdown’ ou chunks flextable.

Les chaînes ‘markdown’ et chunks flextable sont transformés tous les deux vers le même modèle de chunks, puis ces chunks sont mesurés, répartis sur une ou plusieurs lignes et enfin transformés en grobs.

Entrée Markdown commonmark le traite
Entrée Chunks flextable as_paragraph()
Modèle commun Chunks (mots)
→
Mesure Calcul des métriques gdtools & systemfonts
→
Découpe Production de lignes et wrapping automatique
→
Rendu grobs grid texte, raster, rectangles et lignes

La mesure du texte

‘systemfonts’ localise la police, ‘gdtools’ calcule les métriques avec ‘Cairo’. On mesure pour chaque chunk : sa largeur, son ascent et son descent.

Ces mesures vont permettre le wrapping, c’est-à-dire qu’on accumule la largeur des mots (et ou images) jusqu’à width, puis on passe à la ligne.

On doit ensuite ajouter les surlignages et barrés (rectangles et traits qui doivent être alignés avec la ligne mais positionnés sur le texte).

Une width ascent descent annotation un peu longue se répartit sur plusieurs lignes. largeur cible

Chaque mot est mesuré (width, ascent et descent depuis la ligne de base, en pointillé). On accumule les largeurs jusqu'à la largeur cible, puis on passe à la ligne.

La contrainte : polices & devices

Si le device n’utilise pas ‘systemfonts’ ou ‘cairo’, il ne travaillera pas avec le même système de métriques que celui de ‘munch’ et on pourra voir des chevauchements et ou débordements.

Device Rendu
ragg, svglite, ggiraph OK
cairo_pdf(), png(type = "cairo") OK si police installée au niveau système
pdf(), png(), jpeg() KO


‘munch’ avertit lorsque le device est incompatible. Pour utiliser ‘ragg’ dans RStudio par défaut, votre RStudio doit être configuré comme à droite.

Dans shiny : options(shiny.useragg = TRUE)

Avec knitr : opts_chunk$set(dev = "ragg_png")

Limites

‘munch’ fait peu de choses, mais les fait bien pour satisfaire les besoins d’ArData et de ses clients. Quelques limites à connaître :

Markdown : un seul paragraphe L'entrée markdown attend un seul paragraphe : pas de titres, de listes, ni de blocs de code. Pour du markdown multi-blocs, voir marquee.
Markdown ne couvre pas tout Exposant, indice, couleurs et surlignage ne sont pas en markdown : il faut passer aux chunks flextable avec element_chunks() (as_sup(), colorize(), as_highlight()…).
Il faut maîtriser sa chaîne graphique Comprendre ce qu'est un device, comprendre ce qu'est une police et savoir comment on les utilise et lesquels sont à choisir. L'utilisation de 'systemfonts' est une limite forte.
Pas de mise à l'échelle automatique L'empreinte d'un paragraphe a un minimum (son contenu à sa taille de police) ; un mot insécable ne se réduit pas. Si la zone allouée (un sous-titre sur un petit graphe, par ex.) est plus petite que ce minimum, le texte déborde ou est tronqué. Parade : réduire font.size, raccourcir le texte, ou agrandir la figure.

Illustration avec ggplot2+flextable+munch

caption_paragraph <- as_paragraph(
  as_chunk("Graph", props = fp_text_default(color = "#0097C3")), as_i(" made"), " with ",
  as_highlight("ggplot2", color = "#F8D7DA"), ", ", as_highlight("patchwork", color = "#D1ECF1"),
  ", ", as_highlight("flextable", color = "#FFF3CD"), " and ", as_highlight("munch", color = "#E2D9F3")
)