Rmarkdown et les tables

Dans les articles précédents1 nous avons vu tout ce qu’il fallait pour commencer à écrire des rapports un peu plus jolis que de la sortie R brute.

Parmi les types de données qu’il est important de savoir afficher, il y a bien sûr les tables qui ont une capacité analytique importante. Cet article est consacré à la sortie de ces éléments.

Un type de table spécifique pour les documents HTML

Parmi les moyens de représenter des tables de données, les pages HTML (quand on règle output: html_document dans l’entête) bénéficient d’une version interactive nommée “paged“. Nous en avons vu une illustration dans l’article La synthèse Markdown de base : les éléments de contenu :

Ce format permet d’afficher dans un espace réduit un nombre important de lignes en répartissant les données dans des pages qu’il est possible de feuilleter en cliquant sur les liens en bas à droite.

Nous avons vu que l’affichage par défaut des tables se règle via l’option df_print: sous output:html_document. Dans les faits, cela appelle implicitement la fonction rmarkdown::paged_table() en lui passant le data.frame ou apparenté en 1er paramètre (ce qui la rend compatible avec le pipe %>% de magrittr ou le nouveau pipe |> de rbase). Un second paramètre nommé options= attend une liste d’options nommées. Les principales sont :

  • max.print (défaut 1000) : Le nombre maximum de lignes pouvant être affichées sur l’ensemble des pages. A noter, qu’il y a une limite extrême haute à 10.000
  • rows.print (défaut 10) : Le nombre de lignes qui seront présentes par page de tableau
  • cols.print (défaut 10) : Le nombre de colonnes à afficher
  • cols.min.print (pas de valeur par défaut donc cols.print) : Le nombre minimum de colonnes à afficher. Attention à l’encombrement et à la lisibilité !

Le prototype d’appel est donc :

paged_table(x,options=list(...))
R

Je vous laisse trouver les quelques options restantes dans la page d’aide. Elles sont peu utiles.

Quoi qu’il en soit, vous ne devriez pas utiliser explicitement paged_table() mais utiliser l’entête pour configurer votre document. La raison en est que paged_table() n’a pas de sens si à la place de HTML, vous voulez générer un format non interactif comme PDF, DOCX ou PPT par exemple. Laissez donc le moteur faire son travail et choisir la bonne version.

Un format fixe pour les gouverner tous

L’autre fonction la plus courante pour la mise en forme de tables est la fonction kable() fournie par knitr. Celle-ci génère une table reprenant toutes les lignes de la source passée en 1er paramètre.

Tout d’abord, il n’est pas nécessaire de charger explicitement la librairie knitr si on veut juste utiliser les paramètres par défaut car la compilation du document le fait automatiquement… cependant dès que vous allez vouloir paramétrer individuellement une [k/t]able, il faudra que la fonction soit disponible donc autant importer le module par un

library(knitr)
R

en début de votre code R (dans le bloc de configuration, avec les autres library()). Cela vous permettra de simuler vos sorties y compris via l’invite de commande et d’appeler explicitement la fonction kable() dans votre code qui est la seule façon de passer les paramètres décrits ci-dessous.

A la différence de paged_table(), les options ne sont pas passées dans une liste mais directement en tant que paramètres individuels. Le prototype d’appel est donc :

kable(x, format, digits = getOption("digits"), row.names = NA,
  col.names = NA, align, caption = NULL, label = NULL,
  format.args = list(), escape = TRUE, ...)
R

Les principaux paramètres sont :

  • format= : est une chaine de caractère spécifiant quel format de table utiliser. Il en existe 5 :
    • "pipe" : produit une sortie markdown de la table (voir l’article La synthèse Markdown de base : les éléments de contenu pour un exemple tapé à la main) qui sera interprétée par pandoc pour le rendu intermédiaire
    • "simple" : produit une sortie au format pandoc “simple” pour le rendu intermédiaire
    • "rst" : produit un format ReStructuredText (du texte brut, non rendu ensuite)
    • "latex" : produit directement la syntaxe LaTeX (non portable vers des formats n’utilisant pas LaTeX comme processeur intermédiaire)
    • "html" : inversement, produit directement du HTML avec les mêmes limitations en sens inverse
  • digits= : permet comme on peut s’en douter de fixer le nombre de chiffres après la virgule pour l’ensemble du tableau (si une seule valeur) ou pour chaque colonne (si c’est un vecteur d’entiers). Par défaut, c’est la valeur de l’option “digits” de R qui est prise.
  • les autres paramètres de formatage sont passables à format.args=, par exemple pour désactiver la notation scientifique des grands nombres quand vous affichez des valorisations à plusieurs millions (10E+06 n’est pas très lisible pour les euros…). Je vous laisse regarder la page d’aide de la fonction format().
  • row.names= : (NA (défaut), TRUE ou FALSE) définit si on doit afficher les “noms” de lignes (qui sont fixables en faisant un appel à rownames(x)<- c(...) à une étape précédente. Si ces noms ne sont pas fixés manuellement, ils sont remplacés par une numérotation continue des lignes qui n’est alors pas imprimée par kable(). Vous pouvez aussi ne pas les utiliser et créer une 1ère colonne explicite les contenant. A vous de voir.
  • col.names= : Attention au piège, ce n’est pas le pendant de row.names= pour les colonnes. Ici, il est attendu un vecteur de chaines visant à remplacer, si vous le voulez (sinon laissez à NA), les noms des colonnes du data.frame original qui souvent peuvent être “cryptiques”.
  • align= : spécifie explicitement l’alignement des colonnes. Par défaut (NULL), le texte est aligné à gauche et les nombres à droite. Pour spécifier un alignement, il faut passer une chaine de caractères contenant autant de caractères qu’il y a de colonnes. Les caractères reconnus sont r (à droite/right), l (à gauche/left) et c (au centre)
  • ... : les autres paramètres non spécifiés sont pour passer des arguments propres au format de rendu, je ne les aborderai pas.

En gros pour le format=, je vous conseille d’en rester au format “pipe“, qui est celui par défaut, car compatible avec tous les modes de sortie et assez souple. Si votre document sera exclusivement rendu vers un format particulier, vous pouvez utiliser le format= adapté pour plus de souplesse mais si vous changez d’avis, il vous faudra tout corriger.
Pour digits=, modifiez le à 2 mais au niveau de R avec options(digits = 2) car nous avons rarement besoin de manipuler des nombres avec des significativités plus grandes en PMSI.

Il existe d’autres paramètres pour kable() qui sont réglables via options(). Ils commencent tous par knitr.kable. . Citons les plus utiles :

  • knitr.table.format= : qui définit le format de kable par défaut (“pipe” donc)
  • knitr.kable.NA= : (par défaut NULL) contrôle comment sera représenté la valeur NA dans la table. Par défaut, “NA” sera affiché, ce qui n’est pas très“user-friendly”, il faut bien le reconnaître… Mais vous pouvez spécifier une chaine de caractère, par exemple la chaine vide "" qui laissera la case du tableau vide, ou n’importe quelle autre valeur que vous trouvez pertinente (à l’usage testez avec “-” (le tiret), “.” (Un point unique) ou “•” (un point median)). Personnellement, j’en reste souvent à la chaîne vide si elle n’est pas une valeur significative du jeu de données.

Attention cependant il s’agit là de réglages globaux qui seront modifiés à partir de la commande options() et jusqu’à nouveau changement. (PS: Ca n’est pas considéré comme de la programmation très propre)

Des tables côte à côte

Sachez qu’au lieu de passer un seul data.frame à kable(), il est possible de passer une liste de data.frames. Dans ce cas, les n tables seront présentées centrées verticalement et partageront les mêmes paramètres ce qui peut ne pas convenir si elles sont de formats trop disparates. Malheureusement, pour modifier, par exemple, l’alignement vertical, il faudra passer par du code spécifique du format de destination… ce qui dépasse largement le périmètre des articles de ce site

Si ce n’est pas exactement ce que vous voulez faire, vous pouvez utiliser kables() (le pluriel de kable()). Cette fonction attendant une série de kable() et non les données brutes. La fonction se chargera de les organiser côte-à-côte.

Je vous conseille aussi de faire attention à l’encombrement de votre largeur de document si il y a trop de colonnes ou trop de tableaux.

Pour le PMSI, il y a souvent des textes de libellés assez longs donc les tables côte-à-côte ne sont pas très adaptées.

En pratique

Pour illustrer nous allons écrire le bloc qui serait nécessaire à la réponse de la question 4 de l’article Jouons avec les tarifs (corrections) : “De combien le prix de base (GHSPRIX) a-t-il évolué pour chaque GHS en valeur absolue et en pourcentage ?”. Attention, ça va aller vite 🙂.

Dans l’article, nous avons produit ce code :


GHS_N %>% inner_join(GHS_R,by="GHS", suffix=c(".N", ".R")) %>%
    mutate(CMD = substr(GHM.N, 1,2),
           CATGHM = substr(GHM.N,3,3),
           `variation%` = 100 * ((GHSPRIX.N/GHSPRIX.R)-1)) %>%
    select(GHS, CMD, CATGHM, `variation%`)
R

Si nous l’insérons tel quel dans un document Rmarkdown vers HTML où le paramètre de configuration df_print: est réglé à paged, nous allons obtenir :

et si nous passons le df_print: à kable : (attention il y en a pour 3698 lignes)

Si nous cherchons à produire un PDF, notre entête YAML va contenir :

output:
  pdf_document:
    toc: yes
    number_sections: yes
    df_print: kable
YAML

et donner un résultat assez similaire mais plus adapté à un document “imprimé”

Au final, c’est un résultat déjà très satisfaisant sans même taper une ligne de code de plus.

Mais à tout moment, on peut préciser la sortie via :

GHS_N %>% inner_join(GHS_R,by="GHS", suffix=c(".N", ".R")) %>%
    mutate(CMD = substr(GHM.N, 1,2),
           CATGHM = substr(GHM.N,3,3),
           `variation%` = 100 * ((GHSPRIX.N/GHSPRIX.R)-1)) %>%
    select(GHS, CMD, CATGHM, `variation%`) %>%
    kable(format = "pipe", col.names = c("GHS","CMD","Catégorie de GHM","Variation %")) 
R

C’est à dire un appel explicite de kable() avec les paramètres spécifiques que l’on désire lui passer.

Conclusion

Voilà, nous avons vu comment faire une sortie écran (HTML) ou document (HTML ou PDF ou autre) d’un tableau le plus simplement possible. Vous pouvez vous entrainer avec les autres questions de l’article sur les tarifs des GHS.

Une prochaine fois, nous allons continuer à customiser “l’impression” de ce tableau avec d’autres fonctions spécifiques de personnalisation de la sortie de kable().


  1. Produire des jolis rapports, La syntaxe Markdown de base : la structure du document,La synthèse Markdown de base : les éléments de contenu et Insérer du code R dans Markdown pour générer du contenu ↩︎

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *