-
-
Save z3tt/8b2a06d05e8fae308abbf027ce357f01 to your computer and use it in GitHub Desktop.
library(dplyr) | |
library(forcats) | |
library(ggplot2) | |
library(palmerpenguins) | |
library(ggtext) | |
library(colorspace) | |
library(ragg) | |
url <- "https://raw.githubusercontent.com/allisonhorst/palmerpenguins/master/man/figures/lter_penguins.png" | |
img <- magick::image_read((url)) | |
pic <- grid::rasterGrob(img, interpolate = TRUE) | |
pal <- c("#FF8C00", "#A034F0", "#159090") | |
add_sample <- function(x) { | |
return(c(y = max(x) + .025, | |
label = length(x))) | |
} | |
penguins |> | |
group_by(species) |> | |
mutate(bill_ratio = bill_length_mm / bill_depth_mm) |> | |
filter(!is.na(bill_ratio)) |> | |
ggplot(aes(x = fct_rev(species), y = bill_ratio)) + | |
ggdist::stat_halfeye( | |
aes(color = species, | |
fill = after_scale(lighten(color, .5))), | |
adjust = .5, | |
width = .75, | |
.width = 0, | |
justification = -.4, | |
point_color = NA | |
) + | |
geom_boxplot( | |
aes(color = stage(species, after_scale = darken(color, .1, space = "HLS")), | |
fill = after_scale(desaturate(lighten(color, .8), .4))), | |
width = .42, | |
outlier.shape = NA | |
) + | |
geom_point( | |
aes(color = stage(species, after_scale = darken(color, .1, space = "HLS"))), | |
fill = "white", | |
shape = 21, | |
stroke = .4, | |
size = 2, | |
position = position_jitter(seed = 1, width = .12) | |
) + | |
geom_point( | |
aes(fill = species), | |
color = "transparent", | |
shape = 21, | |
stroke = .4, | |
size = 2, | |
alpha = .3, | |
position = position_jitter(seed = 1, width = .12) | |
) + | |
stat_summary( | |
geom = "text", | |
fun = "median", | |
aes(label = round(after_stat(y), 2), | |
color = stage(species, after_scale = darken(color, .1, space = "HLS"))), | |
family = "Roboto Mono", | |
fontface = "bold", | |
size = 4.5, | |
vjust = -3.5 | |
) + | |
stat_summary( | |
geom = "text", | |
fun.data = add_sample, | |
aes(label = paste("n =", after_stat(label)), | |
color = stage(species, after_scale = darken(color, .1, space = "HLS"))), | |
family = "Roboto Condensed", | |
size = 4, | |
hjust = 0 | |
) + | |
coord_flip(xlim = c(1.2, NA), clip = "off") + | |
annotation_custom(pic, ymin = 2.9, ymax = 3.85, xmin = 2.7, xmax = 4.7) + | |
scale_y_continuous( | |
limits = c(1.57, 3.8), | |
breaks = seq(1.6, 3.8, by = .2), | |
expand = c(.001, .001) | |
) + | |
scale_color_manual(values = pal, guide = "none") + | |
scale_fill_manual(values = pal, guide = "none") + | |
labs( | |
x = NULL, | |
y = "Bill ratio", | |
title = "Bill Ratios of Brush–Tailed Penguins (*Pygoscelis* spec.)", | |
subtitle = "Distribution of bill ratios, estimated as bill length divided by bill depth.", | |
caption = "Gorman, Williams & Fraser (2014) *PLoS ONE* DOI: 10.1371/journal.pone.0090081<br>Visualization: Cédric Scherer • Illustration: Allison Horst" | |
) + | |
theme_minimal(base_family = "Zilla Slab", base_size = 15) + | |
theme( | |
panel.grid.minor = element_blank(), | |
panel.grid.major.y = element_blank(), | |
axis.ticks = element_blank(), | |
axis.text.x = element_text(family = "Roboto Mono"), | |
axis.text.y = element_text( | |
color = rev(darken(pal, .1, space = "HLS")), | |
size = 18 | |
), | |
axis.title.x = element_text(margin = margin(t = 10), | |
size = 16), | |
plot.title = element_markdown(face = "bold", size = 21), | |
plot.subtitle = element_text( | |
color = "grey40", hjust = 0, | |
margin = margin(0, 0, 20, 0) | |
), | |
plot.title.position = "plot", | |
plot.caption = element_markdown( | |
color = "grey40", lineheight = 1.2, | |
margin = margin(20, 0, 0, 0)), | |
plot.margin = margin(15, 15, 10, 15) | |
) |
Hey Vimi, thanks for the feedback, glad to hear it's helpful!
Thank you so much, Cédric, for your tutorial!
Thanks for the feedback Creeki! I've just updated the code to add some missing libraries, use the base pipe, and make proper use of after_scale()
in combination with stage()
and after_stat()
.
Superb Cédric!
Hi Cédric, thanks for the awesome work! I have been able to use it for some plots I wanted to make. Thanks!!
I have a couple of quick questions for you though. First of all I get a few warnings (some are font related, which are not supported on my Windows database, so very unimportant) and one is
'1: Vectorized input to element_text()
is not officially supported.'
And I think it may come from line 99 color = rev(darken(pal, .1, space = "HLS"))
, because if I comment out that axis.text.y =
warning goes away.
Also, I am trying to avoid loading libraries in all my scripts, and just calling the function from the library like ggplot2::ggplot()
. If I do this, the ggplot2::stage
in the geom_boxplot
(or any other stage) does not recognize the species as an object, on line 35 for example. If I simply define color = species
(with no stage, it works.
I guess this is all unimportant! but I wondered if you hade any comments about this! thanks!
ps: I have a forked gist of what I have done if you have a minute to see it here
Thanks for your feedback @francisvolh 🙌
cf. warning fonts:
Exactly, the fonts need to be installed locally. All font files are available via GoogleFonts:
https://fonts.google.com/specimen/Zilla+Slab
https://fonts.google.com/specimen/Roboto+Condensed
https://fonts.google.com/specimen/Roboto+Mono
cf. warning element_text:
Yes, vectorized inputs are not supported for theme elements but work! The warning means the resulting behavior is not guaranteed and the trick might stop working at some point in future. I use it here to color the labels on the y-axis -- it doesn't matter if you use the darken
function or not, the warning pops up because we feed a vector to the color argument. Just passing c("red", "green", "blue")
would cause the same warning, while setting a single color e.g. "black"
does not.
cf. namespace:
Hm, interesting. As I rarely use namespace convention when using ggplot2, I've never run into this issue. You mean when specifying ggplot2::aes(color = ggplot2::stage(species, after_scale = colorspace::darken(color, .1, space = "HLS"))))
, ggplot can't find the species column? There is a related issue on the ggplot2 repo, maybe this helps: tidyverse/ggplot2#6104.
Thanks for the comments @z3tt! And thanks a lot also for the input on my warnings/bullet points!
I checked the thread cf. namespace and it got a bit complicated for me to understand the idea of stage not being a proper function but I guess I can just avoid using "stage" and go on with my life for life! But you were on point, if no loading the library, and specifying ggplot2::stage
and it seems expected from the issue you linked me to.... and I think they dont want or thinkg is something to be fixed (as stage
seems not to be a proper function).
Cheers! and thanks again!
Tbh that discussion is also beyond me 🤓 Exactly, you can use the logic without stage()
by repeating the aesthetic, e.g.
geom_point(
aes(color = species,
color = after_scale(darken(color, .1, space = "HLS")))
)
It's not the official way to do it and you'll get a warning but it works perfectly fine and I often do it myself. Just trying to avoid spreading the unofficial approach in my workshops and tutorials.
PS: However, you might run into the same issue when using the afterscale()
function without loading {ggplot2} because it's not considered a "true function" as well.
Hi Cédric,
Thank you for the script. It was so helpful. Also, thank you for your blog.