Comprendre les problèmes de taille et de résolution avec ggplot2

Qu’est-ce qui est difficile avec la taille et la résolution ?


Avez-vous déjà essayé de reproduire une visualisation comme la première et d’apporter des modifications mineures sur la taille ou la résolution et de vous retrouver avec un résultat comme sur la deuxième visualisation ? Si oui, vous êtes sur la bonne page, c’est la situation que nous allons expliquer dans cet article.

résolution en pixels résolution en pixels

Il y a plusieurs choses à remarquer :

  • Principalement, la taille de la police n’est pas la même et est beaucoup plus grande.
  • La carte est un peu plus petite, mais semble toujours correcte.
  • Les différents graphes semblent écrasés, leur largeur est la même, mais pas leur hauteur. En fait, ils semblent prendre la même place dans l’ensemble (texte du titre et de l’axe inclus) mais comme la police est plus grande, il y a moins de place pour le graphique.
  • La légende ne tient plus, et est tronquée.

Nous allons donc voir quel est le problème, quand il survient, comment le résoudre, et surtout comment l’éviter.


Écran et fichier image

Passons d’abord en revue quelques concepts de base.

Dimensions et résolution de l’écran

Un écran est essentiellement une matrice de pixels, qui est le plus petit élément pouvant être affiché.

Si on regarde la dimension physique, mon écran a une diagonale de 24 pouces, avec un ratio de 16/10, soit 20 x 12,5 pouces.

La résolution est de 1920 x 1200, mon écran est une matrice de 1920 pixels de largeur et 1200 pixels de hauteur. Le nombre de pixels par pouce est de 96 (ppi).

Maintenant on peut faire quelques calculs, tout correspond :

  • largeur : 1920 (px) / 96 (px/in) = 20 inches
  • hauteur: 1200 (px) / 96 (px/in) = 12.5 inches
  • ratio: 1920/1200 = 20/12.5 = 16/10


Dimensions et résolution des fichiers image


L’image enregistrée sur un disque est également représentée sous la forme d’une matrice de points. ggplot et ggsave fonctionnent en dimension physique (in, cm ou mm). Pour passer de la dimension en pouces à un nombre de points, ggsave utilise le nombre de points par pouces (dpi).

Donc, si nous créons une visualisation dans ggplot et l’enregistrons avec une dimension de 12 x 10 avec le dpi par défaut de 300 pour ggsave, le fichier sera une matrice de (12 * 300) x (10 * 300) = 3600 x 3000 points .

Maintenant, si vous ouvrez ce fichier sur votre ordinateur, chaque point représente un pixel, ce qui signifie que l’image a une résolution de 3600 x 3000 px.

La relation est : (taille en pouces) = (taille de l’écran en pixel) / PPI ou (taille de l’écran en pixel) = DPI * (taille en pouces)


Faisons l’expérience


Nous allons faire une petite expérience pour comprendre la relation entre la résolution de l’écran et la taille lors de l’enregistrement d’un graphique.

Disons que la résolution de votre écran est de 1920 * 1200, la device graphique par défaut prend presque tout l’écran. Le DPI de mon écran est de 96, donc lors de l’enregistrement sur disque, nous pouvons nous attendre à ce que le fichier soit enregistré avec une dimension de 20 x 12,5 pouces.

Regardons ça :

Chargez d’abord les packages et les données

library(ggplot2)
library(dplyr)
library(palmerpenguins)
theme_set(theme_bw())

Ouvrez ensuite un nouveau périphérique graphique avec x11() (ou quartz(), ou windows() selon votre système) d’une dimension de 1920 x 1200.

x11()

Exécutez ensuite le code suivant pour créer un simple graphique et enregistrez-le sur le disque.

plt <- penguins %>%
  ggplot(aes(bill_length_mm, bill_depth_mm, color = species)) +
  geom_point()

ggsave("test.png")),
       plot = plt, dpi = 300)

dev.off()

Vous pouvez voir que le graphique est enregistré avec une taille de 20 x 11,7 pouces, qui est la dimension de l’écran (moins la barre de menu de la fenêtre) divisée par le DPI d’écran par défaut de 96 comme prévu.

enregistrer en pouces

Mais si vous vérifiez le fichier, vous verrez que la résolution n’est pas de 1920 x 1200, mais de 6000 x 3521 !

enregistrer en pouces

Que s’est-il passé?

La dimension du graphique est convertie en pouces avant d’être enregistrée sur le disque, en utilisant le DPI d’écran de 96 pour effectuer la conversion (1920 / 96) x (1200 / 96) = 20 x 12,5. Ensuite, ggsave utilise cette dimension en pouces avec le DPI de 300 pour l’enregistrer sur le disque, créant une image de (20 * 300) x (12,5 * 300) = 6000 x 3510 points.

Il c’est donc important de comprendre cela lorsqu’on expérimente et visualise dans RStudio, puis qu’on enregistre sur disque. La relation entre la taille de l’écran et la taille physique dépend du DPI (dot per inch) du périphérique graphique, qui est de 96 par défaut pour l’écran. Ensuite, si vous enregistrez le tracé avec un DPI supérieur ou inférieur, le fichier est enregistré sous forme de matrice de points en utilisant le nouveau DPI et la taille en pouces. Ainsi, lorsqu’il est ouvert sur l’ordinateur, la dimension de la taille en pixel est différente.

Pourquoi est-ce important pour le problème d’origine ?

C’est important car certains éléments du graphique s’ajustent à l’espace disponible, et certains sont fixes et mesurés dans leur dimension réelle (mm, pouces) comme les polices, créant une distorsion lors du changement de dimension du graphique ou de sa résolution.


Le problème de police


Avant de continuer, assurez-vous que la fenêtre que nous avons ouverte précédemment est fermée.

En enregistrant dans différentes dimensions, le graphique s’adaptera et utilisera la taille réelle. Donc pour le graphique précédent, si on l’enregistre en 5x5 ou 10x10, ça marche, les deux graphique seront toujours très similaires

ggsave("font_test1_5x5_300.png", plot = plt, width = 5, height = 5, units = "in", dpi = 300)

ggsave("font_test1_10x10_300.png", plot = plt, width = 10, height = 10, units = "in", dpi = 300)

Mais on peut remarquer que la taille du point et la taille de la police semblent plus petites dans la seconde.

résolution en pixels résolution en pixels

En fait, ils ne sont pas plus petits, ils ont toujours la même taille en pouces, et comme nous avons enregistré avec la même résolution (dpi = 300), ils ont le même nombre de points (taille en pouces * 300). Mais la police apparaît plus petite à l’écran si vous affichez le graphique pour qu’il tienne dans l’écran, car le deuxième graphique est plus grand (3000x3000 vs 1500 x 1500) :

La police est la même, les distances changent

Avec cet exemple, il devient plus facile de comprendre un problème qui se produit régulièrement lors de la réplication d’une visualisation ou du changement de dimension ou de résolution, et peut être déroutant au début :

plt <- penguins %>%
  ggplot(aes(bill_length_mm, bill_depth_mm, color = species)) +
  geom_point()+
  geom_text(x = 45, y = 20, label = "Example of font problem", size = 15, inherit.aes = FALSE) +
  labs(title = "Bill length and depth relation by species") +
  theme(plot.title = element_text(size = 15))

ggsave("font_test2_10x10_300.png", plot = plt, width = 10, height = 10, units = "in", dpi = 300)

ggsave("font_test2_5x5_300.png", plot = plt, width = 5, height = 5, units = "in", dpi = 300)

résolution en pixels résolution en pixels

Ici encore, la dimension physique de la police est la même (illustrée dans l’image ci-dessous avec la dimension physique réelle), mais la taille du graphique est différente, donc relativement, le texte ne rentre plus dans le graphique

Ici encore la taille réelle est la même

Il y a donc 3 choix :

  • Ajustez la taille de la police lorsque vous modifiez la taille ou la résolution de votre visualisation, mais si la visualisation est complexe, cela peut impliquer de nombreuses modifications.
  • Définissez la taille de la visualisation en pouces et la résolution au début, et travaillez ensuite sur la personnalisation.
  • Le package ragg a un paramètre d’échelle pour gérer les changements de dimension tout en gardant les proportions, voir plus de détails à la fin de l’article.

L’option pour laquelle j’ai opté, est d’avoir cela à l’esprit, de réfléchir à la proportion de votre visualisation finale et de définir la taille du graphique avant de procéder à une personnalisation complexe avec du texte et de la taille. Sachant pourquoi cela se produit, il devient facile de le réparer quand cela se produit et de choisir la meilleure option à ce moment-là.


Différence de taille de police


Peut-être avez-vous remarqué autre chose dans l’exemple précédent, le texte sur le graphique et le titre ont tous deux une taille de 15, mais ils ont l’air complètement différents. Alors pourquoi ?

Taille de police différente avec la même taille de paramètre = 15

Dans le thème, la taille est définie en pts. Donc ici 15, ça veut dire 15 pts. Dans geom_text, la taille est définie en mm, elle est donc de 15 mm.

Quelle est la relation entre pts et mm ou in ? Si on veut exactement la même taille pour le titre et le texte dans le graphique, comment peut-on la définir ? Il a besoin d’une conversion :

  • 1 pt = 1/72 in
  • 1 pt = 0.35 mm

Donc si on veut que le texte soit de la même taille que le titre, la taille en mm sera de 15 pt * 0,35 pt/mm = 5,25 mm

Dans ggplot, il existe une constante définie pour effectuer la conversion, .pt = 2.845276. (1/.pt = 0,35). Vous pouvez taper .pt dans la console et il affichera sa valeur :

ggplot2::.pt
## [1] 2.845276

Donc pour faire la conversion :

  • de pt à mm : mm = pt / .pt -> 15 / 2.845276 = 5.27
  • de mm à pt : pt = mm * .pt -> 5.27 * 2.845276 = 15

Modifions la taille du geom_text pour qu’elle soit la même que celle du titre en utilisant size = 15/.pt :

plt <- penguins %>%
  ggplot(aes(bill_length_mm, bill_depth_mm, color = species)) +
  geom_point()+
  geom_text(x = 45, y = 20, label = "Example of font problem", size = 15/.pt, inherit.aes = FALSE) +
  labs(title = "Bill length and depth relation by species") +
  theme(plot.title = element_text(size = 15))

ggsave("font_title_10x10_300.png", plot = plt, width = 10, height = 10, units = "in", dpi = 300)

Même taille réelle, en convertissant les pts en mm

That’s aussi simple que ça !


Exemple avec showtext


Faisons exactement le même graphique, mais en utilisant une police différente avec showtext.

library(showtext)

font_add_google("Roboto", "roboto")

showtext_auto(enable = TRUE)

plt <- penguins %>%
  ggplot(aes(bill_length_mm, bill_depth_mm, color = species)) +
  geom_point()+
  geom_text(x = 45, y = 20, label = "Example of font problem", size = 15/.pt, inherit.aes = FALSE) +
  labs(title = "Bill length and depth relation by species") +
  theme(plot.title = element_text(size = 15))

ggsave("showtext1_10x10_300.png", plot = plt, width = 10, height = 10, units = "in", dpi = 300)

La taille de la police a de nouveau changé en utilisant une police différente

Et maintenant ? J’ai changé la police, et du coup la taille du texte diminue, je croyais avoir compris, mais est-ce que j’ai encore manqué quelque chose ?

Non, c’est le même problème ici. Lors de l’appel de showtext_auto(), le dpi du texte par défaut est 96 ! Nous avons défini notre taille de police pour le dpi que nous avons utilisé dans ggsave qui était de 300, mais maintenant le DPI utilisé pour la police est de 96. Faisons donc le calcul et rappelons d’abord ce que nous avons vu auparavant, lors de l’enregistrement d’un graphique, nous enregistrons un raster image qui est une matrice de points :

La police qui était de 5,27 mm à 300 dpi, soit 0,2074803 en * 300 dpi = 62 points Le graphique faisait 10 pouces de hauteur, à 300 dpi = 3000 points, donc le texte avait une hauteur de 62 points sur 3000 points.

Maintenant à 96 ppp, c’est 0,2075*96 = 20 points, mais la taille du tracé est toujours de 10 pouces à 300 ppp. Donc, relativement, le texte a maintenant une hauteur de 20 points sur un tracé de hauteur de 3000 points, ce qui le fait paraître environ 3 fois plus petit.

Je recommanderais donc de configurer toutes les options et les polices avant de faire trop de personnalisation. Mais si vous avez déjà personnalisé lorsque vous décidez de charger une nouvelle police ; la façon de résoudre le problème est de dire à showtext d’utiliser un DPI = 300 :

library(showtext)

font_add_google("Roboto", "roboto")

showtext_opts(dpi = 300)
showtext_auto(enable = TRUE)

plt <- penguins %>%
  ggplot(aes(bill_length_mm, bill_depth_mm, color = species)) +
  geom_point()+
  geom_text(x = 45, y = 20, label = "Example of font problem", size = 15/.pt, inherit.aes = FALSE) +
  labs(title = "Bill length and depth relation by species") +
  theme(plot.title = element_text(size = 15))

ggsave("showtext2_10x10_300.png", plot = plt, width = 10, height = 10, units = "in", dpi = 300)

La taille de la police est revenue à la normale

Maintenant, c’est revenu à la normale.

C’est quelque chose à considérer lorsque cela se produit, vérifiez la dimension et la résolution, et voyez s’il n’y a pas de valeur par défaut provenant d’un package.


Utilisation du package ragg et de l’option de mise à l’échelle


Maintenant, si vous avez terminé votre visualisation et que vous devez enregistrer dans une dimension de fichier différente, vous devez généralement modifier la taille de la police si vous ne voulez pas faire face au problème précédent, et certains éléments ne conserveront pas les mêmes dimensions ou des proportions, le point paraîtra plus gros ou plus petit, les distances entre eux ne seront pas les mêmes.

Le package ragg peut vous aider, il fournit un paramètre pour mettre à l’échelle la visualisation, par exemple, si nous voulons réduire notre visualisation de 10x10 à 5x5 et que nous voulons conserver la même proportion pour le texte, nous pouvons définir l’option de mise à l’échelle sur 0,5 .

plt <- penguins %>%
  ggplot(aes(bill_length_mm, bill_depth_mm, color = species)) +
  geom_point()+
  geom_text(x = 45, y = 20, label = "Example of font problem", size = 15/.pt, inherit.aes = FALSE) +
  labs(title = "Bill length and depth relation by species") +
  theme(plot.title = element_text(size = 15))

ragg::agg_png("ragg_10x10.png", width = 10, height = 10, units = "in", res = 300)
plt
dev.off()
ragg::agg_png("ragg_20x20.png", width = 20, height = 20, units = "in", res = 300, scaling = 2)
plt
dev.off()
ragg::agg_png("ragg_5x5.png", width = 5, height = 5, units = "in", res = 300, scaling = 0.5)
plt
dev.off()

Ces 3 fichiers auront des dimensions différentes, mais toutes les proportions seront conservées (cliquez dessus pour voir la taille réelle).

Half size 5x5

Moitié de la taille 5x5

Normal size 10x10

Normal size 10x10

Double size 20x20

double taille 20x20

Pour une explication différente et plus de détails sur la façon dont ragg résout le problème, vous pouvez consulter ce post (en anglais) Taking Control of Plot Scaling

Notez que si vous utilisez agg_png comme type de périphérique dans ggsave, l’argument scale ne fonctionne plus, il produit toujours la même taille d’image, comme s’il divisait la nouvelle taille par le facteur des mise à l’échelle (quand j’écris ce post, avec la version 0.4.1, ça sera probablement corrigé dans une version ultérieure). Je recommande donc de l’utiliser comme device, comme dans l’exemple précédent.

Christophe Nicault
Christophe Nicault
Stratégie des Systèmes d’Information
Transformation Numérique
Data Science

Je travaille sur la stratégie des systèmes d’information, les projets informatiques et la science des données.

Sur le même sujet