Le but de ce référentiel est de suivre les astuces GGPLOT2 soignées que j'ai apprises. Cela suppose que vous vous êtes familiarisé avec les bases de GGPLOT2 et que vous pouvez construire vos propres complots. Sinon, veuillez parcourir le livre à votre location.
Je ne m'adapte pas incroyablement dans des complots glorieusement composés et des thèmes et des palettes de couleurs experts, vous devriez donc me pardonner. L'ensemble de données mpg
est très polyvalent pour le traçage, vous en verrez donc beaucoup de choses en lisant. Les forfaits d'extension sont super, et je me suis mordonné, mais je vais essayer de me limiter aux astuces GGPLOT2 Vanilla ici.
Pour l'instant, ce sera principalement un sac de trucs en lecture uniquement, mais je pourrai décider plus tard de les mettre en groupes séparés dans d'autres fichiers.
En chargeant la bibliothèque et en définissant un thème de traçage. La première astuce ici est d'utiliser theme_set()
pour définir un thème pour tous vos tracés à travers un document. Si vous vous retrouvez à définir un thème très verbeux pour chaque intrigue, voici l'endroit où vous définissez tous vos paramètres communs. Ensuite, n'écrivez plus jamais un roman d'éléments de thème 1 !
library( ggplot2 )
library( scales )
theme_set(
# Pick a starting theme
theme_gray() +
# Add your favourite elements
theme(
axis.line = element_line(),
panel.background = element_rect( fill = " white " ),
panel.grid.major = element_line( " grey95 " , linewidth = 0.25 ),
legend.key = element_rect( fill = NA )
)
)
La documentation ?aes
ne vous le dit pas, mais vous pouvez épisser l'argument mapping
dans GGPLOT2. Qu'est-ce que cela signifie? Eh bien, cela signifie que vous pouvez composer l'argument mapping
en déplacement avec !!!
. Ceci est particulièrement astucieux si vous avez besoin de recycler l'esthétique de temps en temps.
my_mapping <- aes( x = foo , y = bar )
aes( colour = qux , !!! my_mapping )
# > Aesthetic mapping:
# > * `x` -> `foo`
# > * `y` -> `bar`
# > * `colour` -> `qux`
Mon utilisation préférée de cela est de faire en sorte que la couleur fill
correspond à la couleur colour
, mais légèrement plus claire 2 . Nous utiliserons le système d'évaluation retardé pour cela, after_scale()
dans ce cas, que vous en verrez plus dans la section suivant celui-ci. Je vais répéter cette astuce plusieurs fois tout au long de ce document.
my_fill <- aes( fill = after_scale(alpha( colour , 0.3 )))
ggplot( mpg , aes( displ , hwy )) +
geom_point(aes( colour = factor ( cyl ), !!! my_fill ), shape = 21 )
Vous pouvez vous retrouver dans une situation dans laquelle on vous demande de faire une carte thermique d'un petit nombre de variables. En règle générale, les échelles séquentielles passent de clair à sombre ou vice versa, ce qui rend le texte en une seule couleur difficile à lire. Nous pourrions concevoir une méthode pour écrire automatiquement le texte en blanc sur un fond sombre et noir sur un fond clair. La fonction ci-dessous considère une valeur légère pour une couleur et renvoie le noir ou le blanc en fonction de cette légèreté.
contrast <- function ( colour ) {
out <- rep( " black " , length( colour ))
light <- farver :: get_channel( colour , " l " , space = " hcl " )
out [ light < 50 ] <- " white "
out
}
Maintenant, nous pouvons faire une esthétique pour être épissée dans l'argument mapping
d'une couche à la demande.
autocontrast <- aes( colour = after_scale(contrast( fill )))
Enfin, nous pouvons tester notre engin de contraste automatique. Vous remarquerez peut-être qu'il s'adapte à l'échelle, vous n'auriez donc pas besoin de faire un tas de formatage conditionnel pour cela.
cors <- cor( mtcars )
# Melt matrix
df <- data.frame (
col = colnames( cors )[as.vector(col( cors ))],
row = rownames( cors )[as.vector(row( cors ))],
value = as.vector( cors )
)
# Basic plot
p <- ggplot( df , aes( row , col , fill = value )) +
geom_raster() +
geom_text(aes( label = round( value , 2 ), !!! autocontrast )) +
coord_equal()
p + scale_fill_viridis_c( direction = 1 )
p + scale_fill_viridis_c( direction = - 1 )
Il y a quelques extensions qui offrent des versions de mi-génie des choses. Parmi ceux que je connais, Gghalves et le package Voir offrent des demi-géomètres.
Voici comment abuser du système d'évaluation retardé pour faire le vôtre. Cela peut être utile si vous n'êtes pas prêt à prendre une dépendance supplémentaire pour cette fonctionnalité.
Le cas facile est le boîtier à boîte. Vous pouvez définir xmin
ou xmax
sur after_scale(x)
pour garder respectivement les parties droite et gauche d'un boxplot. Cela fonctionne toujours bien avec position = "dodge"
.
# A basic plot to reuse for examples
p <- ggplot( mpg , aes( class , displ , colour = class , !!! my_fill )) +
guides( colour = " none " , fill = " none " ) +
labs( y = " Engine Displacement [L] " , x = " Type of car " )
p + geom_boxplot(aes( xmin = after_scale( x )))
La même chose qui fonctionne pour les boîtes à boîte, fonctionne également pour les barres d'erreur.
p + geom_errorbar(
stat = " summary " ,
fun.data = mean_se ,
aes( xmin = after_scale( x ))
)
Nous pouvons à nouveau faire la même chose pour les parcelles de violon, mais la couche se plaint de ne pas savoir l'esthétique xmin
. Il utilise cette esthétique, mais seulement après la configuration des données, il n'est donc pas destiné à être une esthétique accessible par l'utilisateur. Nous pouvons faire taire l'avertissement en mettant à jour le xmin
par défaut à NULL
, ce qui signifie qu'il ne se plaindra pas, mais ne l'utilise pas non plus s'il est absent.
update_geom_defaults( " violin " , list ( xmin = NULL ))
p + geom_violin(aes( xmin = after_scale( x )))
Pas laissé comme un exercice pour le lecteur cette fois, mais je voulais juste montrer comment cela fonctionnerait si vous alliez combiner deux moitiés et que vous vouliez qu'ils soient un peu décalés les uns des autres. Nous abuserons des barres d'erreur pour servir de produits de base pour les boîtes à boîte.
# A small nudge offset
offset <- 0.025
# We can pre-specify the mappings if we plan on recycling some
right_nudge <- aes(
xmin = after_scale( x ),
x = stage( class , after_stat = x + offset )
)
left_nudge <- aes(
xmax = after_scale( x ),
x = stage( class , after_stat = x - offset )
)
# Combining
p +
geom_violin( right_nudge ) +
geom_boxplot( left_nudge ) +
geom_errorbar( left_nudge , stat = " boxplot " , width = 0.3 )
Disons que vous avez une meilleure intuition des couleurs que moi, et que trois couleurs ne sont pas suffisantes pour vos besoins divergents de palette de couleurs. Un point de douleur est qu'il est difficile d'obtenir le bon point si vos limites ne sont pas parfaitement centrées autour de lui. Entrez l'argument rescaler
en ligue avec scales::rescale_mid()
.
my_palette <- c( " dodgerblue " , " deepskyblue " , " white " , " hotpink " , " deeppink " )
p <- ggplot( mpg , aes( displ , hwy , colour = cty - mean( cty ))) +
geom_point() +
labs(
x = " Engine displacement [L] " ,
y = " Highway miles per gallon " ,
colour = " Centered n value "
)
p +
scale_colour_gradientn(
colours = my_palette ,
rescaler = ~ rescale_mid( .x , mid = 0 )
)
Une alternative est de simplement centrer les limites de x. Nous pouvons le faire en fournissant une fonction aux limites de l'échelle.
p +
scale_colour_gradientn(
colours = my_palette ,
limits = ~ c( - 1 , 1 ) * max(abs( .x ))
)
Vous pouvez étiqueter les points avec geom_text()
, mais un problème potentiel est que le texte et les points se chevauchent.
set.seed( 0 )
df <- USArrests [sample(nrow( USArrests ), 5 ), ]
df $ state <- rownames( df )
q <- ggplot( df , aes( Murder , Rape , label = state )) +
geom_point()
q + geom_text()
Il existe plusieurs solutions typiques à ce problème, et elles sont toutes accompagnées des inconvénients:
nudge_x
et nudge_y
. Le problème ici est que ceux-ci sont définis dans les unités de données, donc l'espacement est imprévisible, et il n'y a aucun moyen de les faire dépendre des emplacements d'origine.hjust
et vjust
. Il vous permet de dépendre des emplacements d'origine, mais ceux-ci n'ont pas de compensations naturelles.Voici les options 2 et 3 en action:
q + geom_text( nudge_x = 1 , nudge_y = 1 )
q + geom_text(aes(
hjust = Murder > mean( Murder ),
vjust = Rape > mean( Rape )
))
Vous pourriez penser: «Je peux simplement multiplier les justifications pour obtenir un décalage plus large», et vous auriez raison. Cependant, car la justification dépend de la taille du texte, vous pourriez obtenir des décalages inégaux. Remarquez dans l'intrigue ci-dessous que le «Dakota du Nord» est trop compensé dans la direction Y et le «Rhode Island» dans la direction X.
q + geom_text(aes(
label = gsub( " North Dakota " , " North n Dakota " , state ),
hjust = (( Murder > mean( Murder )) - 0.5 ) * 1.5 + 0.5 ,
vjust = (( Rape > mean( Rape )) - 0.5 ) * 3 + 0.5
))
La belle chose de geom_label()
est que vous pouvez éteindre la zone d'étiquette et garder le texte. De cette façon, vous pouvez continuer à utiliser d'autres choses utiles, comme le paramètre label.padding
, pour donner un décalage absolu (indépendant des données) du texte à l'étiquette.
q + geom_label(
aes(
label = gsub( " " , " n " , state ),
hjust = Murder > mean( Murder ),
vjust = Rape > mean( Rape )
),
label.padding = unit( 5 , " pt " ),
label.size = NA , fill = NA
)
C'était une astuce pour mettre des étiquettes de facettes dans des panneaux, qui étaient auparavant compliqués. Avec GGPLOT2 3.5.0, vous n'avez plus à jouer avec la définition de positions infinies et le peaufinage des paramètres hjust
ou vjust
. Vous pouvez maintenant utiliser x = I(0.95), y = I(0.95)
pour placer du texte dans le coin supérieur droit. Ouvrez les détails pour voir l'ancien conseil.
Mettre des annotations de texte sur des parcelles à facettes est une douleur, car les limites peuvent varier sur une base par panel, il est donc très difficile de trouver la bonne position. Une extension qui explore la réduction de cette douleur est l'extension du tagger, mais nous pouvons faire une chose similaire dans la vanille GGPLOT2.
Heureusement, il y a un mécanicien dans les axes de position de GGPLOT2 qui ont permis à -Inf
et Inf
de la limite minimale et maximale de l'échelle . Vous pouvez l'exploiter en choisissant x = Inf, y = Inf
pour mettre les étiquettes dans un coin. Vous pouvez également utiliser -Inf
au lieu d' Inf
pour placer en bas au lieu du haut, ou de gauche au lieu de droite.
Nous devons faire correspondre les arguments hjust
/ vjust
sur le côté de l'intrigue. Pour x/y = Inf
, ils devraient être hjust/vjust = 1
, et pour x/y = -Inf
ils doivent être hjust/vjust = 0
.
p + facet_wrap( ~ class , scales = " free " ) +
geom_text(
# We only need 1 row per facet, so we deduplicate the facetting variable
data = ~ subset( .x , ! duplicated( class )),
aes( x = Inf , y = Inf , label = LETTERS [seq_along( class )]),
hjust = 1 , vjust = 1 ,
colour = " black "
)
Malheureusement, cela place le texte directement à la frontière du panneau, qui peut offenser notre sens de la beauté. Nous pouvons devenir légèrement plus chics en utilisant geom_label()
, ce qui nous permet de contrôler plus précisément l'espacement entre le texte et les bordures du panneau en définissant l'argument label.padding
.
De plus, nous pouvons utiliser label.size = NA, fill = NA
pour masquer la partie de la zone de texte du géom. À des fins d'illustration, nous plaçons maintenant la balise en haut à gauche au lieu de la droite.
p + facet_wrap( ~ class , scales = " free " ) +
geom_label(
data = ~ subset( .x , ! duplicated( class )),
aes( x = - Inf , y = Inf , label = LETTERS [seq_along( class )]),
hjust = 0 , vjust = 1 , label.size = NA , fill = NA ,
label.padding = unit( 5 , " pt " ),
colour = " black "
)
Disons que nous sommes chargés de fabriquer un tas de parcelles similaires, avec différents ensembles de données et colonnes. Par exemple, nous pourrions vouloir faire une série de Barplots 4 avec des pré-sets spécifiques: nous aimerions que les barres touchent l'axe X et ne dessinent pas de grilles verticales.
Une façon bien connue de faire un tas de parcelles similaires consiste à envelopper la construction de la parcelle en fonction. De cette façon, vous pouvez utiliser Encoder tous les préréglages que vous souhaitez dans votre fonction.
Je cas que vous ne savez peut-être pas, il existe différentes méthodes à programmer avec la fonction aes()
, et l'utilisation de {{ }}
(Curly-Curly) est l'une des façons les plus flexibles 5 .
barplot_fun <- function ( data , x ) {
ggplot( data , aes( x = {{ x }})) +
geom_bar( width = 0.618 ) +
scale_y_continuous( expand = c( 0 , 0 , 0.05 , 0 )) +
theme( panel.grid.major.x = element_blank())
}
barplot_fun( mpg , class )
Un inconvénient de cette approche est que vous verrouillez toute esthétique dans les arguments de la fonction. Pour contourner cela, un moyen encore plus simple est de simplement passer ...
directement à aes()
.
barplot_fun <- function ( data , ... ) {
ggplot( data , aes( ... )) +
geom_bar( width = 0.618 ) +
scale_y_continuous( expand = c( 0 , 0 , 0.1 , 0 )) +
theme( panel.grid.major.x = element_blank())
}
barplot_fun( mpg , class , colour = factor ( cyl ), !!! my_fill )
Une autre méthode pour faire une chose très similaire est d'utiliser des «squelettes» de parcelles. L'idée derrière un squelette est que vous pouvez construire un tracé, avec ou sans aucun argument data
, et ajouter les détails plus tard. Ensuite, lorsque vous souhaitez réellement créer un tracé, vous pouvez utiliser le %+%
pour remplir ou remplacer l'ensemble de données, et + aes(...)
pour définir l'esthétique pertinente.
barplot_skelly <- ggplot() +
geom_bar( width = 0.618 ) +
scale_y_continuous( expand = c( 0 , 0 , 0.1 , 0 )) +
theme( panel.grid.major.x = element_blank())
my_plot <- barplot_skelly % + % mpg +
aes( class , colour = factor ( cyl ), !!! my_fill )
my_plot
Une chose intéressante à propos de ces squelettes est que même lorsque vous avez déjà rempli les data
et mapping
les arguments, vous pouvez simplement les remplacer encore et encore.
my_plot % + % mtcars +
aes( factor ( carb ), colour = factor ( cyl ), !!! my_fill )
L'idée ici est de ne pas squeletter tout l'intrigue, mais juste un ensemble de pièces fréquemment réutilisées. Par exemple, nous pourrions vouloir étiqueter notre Barplot et emballer toutes les choses qui composent un Barplot étiqueté. L'astuce consiste à ne pas ajouter ces composants avec +
, mais simplement les mettre dans une list()
. Vous pouvez ensuite +
votre liste avec l'appel principal de l'intrigue.
labelled_bars <- list (
geom_bar( my_fill , width = 0.618 ),
geom_text(
stat = " count " ,
aes( y = after_stat( count ),
label = after_stat( count ),
fill = NULL , colour = NULL ),
vjust = - 1 , show.legend = FALSE
),
scale_y_continuous( expand = c( 0 , 0 , 0.1 , 0 )),
theme( panel.grid.major.x = element_blank())
)
ggplot( mpg , aes( class , colour = factor ( cyl ))) +
labelled_bars +
ggtitle( " The `mpg` dataset " )
ggplot( mtcars , aes( factor ( carb ), colour = factor ( cyl ))) +
labelled_bars +
ggtitle( " The `mtcars` dataset " )
#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.3.2 (2023-10-31 ucrt)
#> os Windows 11 x64 (build 22631)
#> system x86_64, mingw32
#> ui RTerm
#> language (EN)
#> collate English_Netherlands.utf8
#> ctype English_Netherlands.utf8
#> tz Europe/Amsterdam
#> date 2024-02-27
#> pandoc 3.1.1
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> cli 3.6.2 2023-12-11 [] CRAN (R 4.3.2)
#> colorspace 2.1-0 2023-01-23 [] CRAN (R 4.3.2)
#> digest 0.6.34 2024-01-11 [] CRAN (R 4.3.2)
#> dplyr 1.1.4 2023-11-17 [] CRAN (R 4.3.2)
#> evaluate 0.23 2023-11-01 [] CRAN (R 4.3.2)
#> fansi 1.0.6 2023-12-08 [] CRAN (R 4.3.2)
#> farver 2.1.1 2022-07-06 [] CRAN (R 4.3.2)
#> fastmap 1.1.1 2023-02-24 [] CRAN (R 4.3.2)
#> generics 0.1.3 2022-07-05 [] CRAN (R 4.3.2)
#> ggplot2 * 3.5.0.9000 2024-02-27 [] local
#> glue 1.7.0 2024-01-09 [] CRAN (R 4.3.2)
#> gtable 0.3.4 2023-08-21 [] CRAN (R 4.3.2)
#> highr 0.10 2022-12-22 [] CRAN (R 4.3.2)
#> htmltools 0.5.7 2023-11-03 [] CRAN (R 4.3.2)
#> knitr 1.45 2023-10-30 [] CRAN (R 4.3.2)
#> labeling 0.4.3 2023-08-29 [] CRAN (R 4.3.1)
#> lifecycle 1.0.4 2023-11-07 [] CRAN (R 4.3.2)
#> magrittr 2.0.3 2022-03-30 [] CRAN (R 4.3.2)
#> munsell 0.5.0 2018-06-12 [] CRAN (R 4.3.2)
#> pillar 1.9.0 2023-03-22 [] CRAN (R 4.3.2)
#> pkgconfig 2.0.3 2019-09-22 [] CRAN (R 4.3.2)
#> R6 2.5.1 2021-08-19 [] CRAN (R 4.3.2)
#> ragg 1.2.7 2023-12-11 [] CRAN (R 4.3.2)
#> rlang 1.1.3 2024-01-10 [] CRAN (R 4.3.2)
#> rmarkdown 2.25 2023-09-18 [] CRAN (R 4.3.2)
#> rstudioapi 0.15.0 2023-07-07 [] CRAN (R 4.3.2)
#> scales * 1.3.0 2023-11-28 [] CRAN (R 4.3.2)
#> sessioninfo 1.2.2 2021-12-06 [] CRAN (R 4.3.2)
#> systemfonts 1.0.5 2023-10-09 [] CRAN (R 4.3.2)
#> textshaping 0.3.7 2023-10-09 [] CRAN (R 4.3.2)
#> tibble 3.2.1 2023-03-20 [] CRAN (R 4.3.2)
#> tidyselect 1.2.0 2022-10-10 [] CRAN (R 4.3.2)
#> utf8 1.2.4 2023-10-22 [] CRAN (R 4.3.2)
#> vctrs 0.6.5 2023-12-01 [] CRAN (R 4.3.2)
#> viridisLite 0.4.2 2023-05-02 [] CRAN (R 4.3.2)
#> withr 3.0.0 2024-01-16 [] CRAN (R 4.3.2)
#> xfun 0.41 2023-11-01 [] CRAN (R 4.3.2)
#> yaml 2.3.8 2023-12-11 [] CRAN (R 4.3.2)
#>
#>
#> ──────────────────────────────────────────────────────────────────────────────
Eh bien, vous devez le faire une fois au début de votre document. Mais alors plus jamais! Sauf dans votre prochain document. Écrivez simplement un script plot_defaults.R
et source()
à partir de votre document. Copier-coller ce script pour chaque projet. Ensuite, vraiment, plus jamais : cœur:. ↩
C'est un mensonge. En réalité, j'utilise aes(colour = after_scale(colorspace::darken(fill, 0.3)))
au lieu d'éclaircir le remplissage. Je ne voulais pas que cette lecture ait une dépendance à {ColorSpace}. ↩
Sauf si vous auto-saboter vos parcelles en définissant oob = scales::oob_censor_any
dans l'échelle par exemple. ↩
Dans votre âme des âmes, voulez-vous vraiment faire un tas de barreaux? ↩
L'alternative consiste à utiliser le pronom .data
, qui peut être .data$var
si vous souhaitez verrouiller cette colonne à l'avance, ou .data[[var]]
lorsque var
est passé comme un caractère. ↩
Ce bit était à l'origine appelé «squelette partiel», mais comme une cage thoracique fait partie d'un squelette, ce titre semblait plus évocateur. ↩