ggplot2 3.5.0
ggplot2 3.5.0
Photo by
Megan Bucknall
2024/02/23
ggplot2
ggplot2-3-5-0
Teun van den Brand
We’re tickled pink to announce the release of
ggplot2
3.5.0. ggplot2 is a system for declaratively creating graphics, based on The Grammar of Graphics. You provide the data, tell ggplot2 how to map variables to aesthetics, what graphical primitives to use, and it takes care of the details.
You can install it from CRAN with:
install.packages
"ggplot2"
This blog post will cover a bunch of new features included in the latest release. In addition to rewriting the guide system, we made progress supporting newer R graphics capabilities, re-purposed the use of
I()
, and introduce an improved polar coordinate system, along with other improvements. As the release is quite large, we are making a
series of blog posts
covering the major changes.
You can see a full list of changes in the
release notes
library
ggplot2
library
patchwork
library
grid
Guide rewrite
Axes and legends, collectively called guides, are an important component to plots, as they allow the translation of visual information back to data qualities. The extension mechanism of ggplot2 allows others to develop their own layers, facets, coords and scales through the ggproto object-oriented system. Finally, after years of being the only major system in ggplot2 still clinging to the S3 system, guides have been rewritten to use ggproto. With this rewrite, guides officially become an extension point that let developers implement their own guides. We have added a section to the
Extending ggplot2
vignette on how to develop a new guide.
Alongside the rewrite, we made a slew of improvements to guides along the way. As these are somewhat meaty and focused topics, we are going to cover them in separate blog posts about axes and legends.
Patterns and gradients
Patterns and gradients are provided by the grid package, which ggplot2 builds on top of. They were first introduced in R 4.1.0 and were refined in R 4.2.0 to support multiple patterns and gradients. If your graphics device supported it, theme elements could already be set to patterns or gradients, even before this release.
Note: On Windows machines, the default device in RStudio and in the knitr package is
png()
, which does not support patterns. In RStudio, you can go to ‘Tools > Global Options > General > Graphics’ and choose the ‘ragg’ or ‘Cairo PNG’ device from the dropdown menu to display patterns.
gray_gradient
<-
linearGradient
scales
::
pal_grey
10
ggplot
mpg
aes
displ
hwy
geom_point
theme
panel.background
element_rect
fill
gray_gradient
We are pleased to report that as of this release, patterns can be used as the
fill
aesthetic in most layers. To use a pattern, first build a gradient using {grid}‘s
linearGradient()
radialGradient()
functions, or a pattern using the
pattern()
function. Because handling patterns and gradients is very similar, we will treat gradients as if they were patterns: when we say ‘pattern’ in the text below, please mind that we mean patterns and gradients alike. These patterns can be passed to a layer as the
fill
aesthetic. Below, you can see two behaviours of the
linearGradient()
pattern, depending on its
group
argument. The pattern with
group = FALSE
will display the gradient in every rectangle and
group = TRUE
will apply the gradient to all rectangles together.
colours
<-
scales
::
viridis_pal
10
grad_ungroup
<-
linearGradient
colours
, group
FALSE
grad_grouped
<-
linearGradient
colours
, group
TRUE
ungroup
<-
ggplot
mpg
aes
factor
cyl
geom_bar
fill
grad_ungroup
labs
title
"Ungrouped gradient"
grouped
<-
ggplot
mpg
aes
factor
cyl
geom_bar
fill
grad_grouped
labs
title
"Grouped gradient"
ungroup
grouped
Besides passing a static pattern as the
fill
aesthetic, it is also possible to map values to patterns using
scale_fill_manual()
. To map values to patterns, pass a list of patterns to the
values
argument of the scale. When providing patterns as a list, the list can be a mix of patterns and plain colours, like
"limegreen"
in the plot below. We are excited that people may come up with nice pattern palettes that can be used in similar fashion.
patterns
<-
list
linearGradient
colours
, group
FALSE
"limegreen"
radialGradient
colours
, group
FALSE
pattern
rectGrob
0.25
0.75
, y
0.25
0.75
, width
0.5
, height
0.5
width
unit
"mm"
, height
unit
"mm"
, extend
"repeat"
gp
gpar
fill
"limegreen"
ggplot
mpg
aes
factor
cyl
, fill
factor
cyl
geom_bar
scale_fill_manual
values
patterns
The largest obstacle we had to overcome to support gradients in ggplot2 was to apply the
alpha
aesthetic consistently to the patterns. The regular
scales::alpha()
function does not work with patterns, so we implemented a new
fill_alpha()
function that applies the
alpha
aesthetic to the patterns. By switching out
fill = alpha(fill, alpha)
with
fill = fill_alpha(fill, alpha)
in the
grid::gpar()
function, extension developers can enable pattern fills in their own layer extensions.
The
fill_alpha()
function checks if the active device supports patterns and spits out a friendlier warning or error on demand. For extension developers that want to use newer graphics features, you can reuse the
check_device()
function to check feature availability or throw messages in a similar fashion.
# The currently active device is the ragg::agg_png() device
check_device
feature
"patterns"
, action
"test"
#> [1] TRUE
check_device
feature
"glyphs"
, action
"abort"
#>
Error
#>
The
agg_png
device does not support
typeset glyphs
Ignoring scales
In this release, ggplot2 has changed how the plots interact with variables created with
I()
(‘AsIs’ variables). The change is somewhat subtle, so it takes a bit of explaining.
It
used to be
the case that ‘AsIs’ variables automatically added an identity scale to the plot. Identity scales in ggplot2 preserve the original input, without mapping or transforming them. For example, iif you give literal colour names as the
colour
aesthetic, the plot will use these exact colours.
set.seed
42
my_colours
<-
sample
"red"
"green"
"blue"
nrow
mpg
, replace
TRUE
ggplot
mpg
aes
displ
hwy
geom_point
aes
colour
my_colours
scale_colour_identity
However, because identity scales
are
true scales, you cannot combine literal colours in one layer with mapped colours in the next. Trying to do so, will confront you with the ‘unknown colour name’ error.
ggplot
mpg
aes
displ
hwy
geom_point
aes
colour
drv
, shape
, size
geom_point
aes
colour
my_colours
scale_colour_identity
#>
Error
in `geom_point()`:
#>
Problem while converting geom to grob.
#>
Error occurred in the 1st layer.
#>
Caused by error:
#>
Unknown colour name: f
In order to prevent such clashes between identity scales that map nothing and regular scales, we have changed how ‘AsIs’ variables interact with scales. Instead of adding an identity scale, ‘AsIs’ variables are now altogether
ignored
by the scale systems. On the surface, the new behaviour is very similar to the old one, in that for example literal colours are used. However, with ‘AsIs’ variables ignored, you can now freely combine layers with ‘AsIs’ input with layers that map input. If you need a legend for the literal variable, we recommend to use the identity scale mechanism instead.
ggplot
mpg
aes
displ
hwy
geom_point
aes
colour
drv
, shape
, size
geom_point
aes
colour
my_colours
, show.legend
FALSE
Perhaps more salient than avoid scale clashes, is that the same applies to the
and
position aesthetics. There has never been a
scale_x_identity()
or
scale_y_identity()
function, so what this means may be unexpected. Internally, scales transform every continuous variable to the 0-1 range before drawing the graphics. So too do ‘AsIs’ position aesthetics work: you can use numbers between 0 and 1 to set the position. These positions are relative to the plot’s panel and this mechanism opens up a great way to add plot annotations that are independent of the data.
<-
seq
pi
, length.out
100
ggplot
mpg
aes
displ
hwy
geom_point
colour
"grey50"
annotate
"rect"
xmin
0.05
, xmax
0.95
ymin
0.05
, ymax
0.95
fill
NA
, colour
"red"
annotate
"path"
cos
0.5
, y
sin
0.5
colour
"blue"
annotate
"text"
label
"Text in the middle"
0.5
, y
0.5
size
Please take note that discrete variables as ‘AsIs’ position aesthetic have no interpretation and will likely result in errors.
Other improvements
Coordinating text sizes between the theme and
geom_text()
geom_label()
has been a hassle, since the theme uses text sizes in points (pt) and geoms use text size in millimetres. Now, one can control what the
size
aesthetic means for text, by setting the
size.unit
argument.
<-
ggplot
mtcars
aes
wt
mpg
, label
rownames
mtcars
geom_text
size
10
, size.unit
"pt"
theme
axis.text
element_text
size
10
Two improvements have been made to
geom_label()
. The first is that it now obeys an
angle
aesthetic.
geom_label
aes
angle
runif
nrow
mtcars
45
45
In addition,
geom_label()
‘s
label.padding
argument can be controlled individually for every side of the text by using the
margin()
function. The legend keys for labels has also changed to reflect the geom more accurately.
geom_label
aes
colour
factor
cyl
label.padding
margin
, r
20
, b
, l
Like
geom_density()
before it,
geom_violin()
now gains a
bounds
argument to restrict the range wherein density is estimated.
df
<-
data.frame
rbeta
100
0.5
0.5
rbeta
100
rbeta
100
group
rep
"A"
"B"
"C"
, each
100
ggplot
df
aes
group
geom_violin
bounds
The
geom_boxplot()
has acquired an option to remove (rather than hide) outliers. Setting
outliers = FALSE
removes outliers so that the plot limits do not take these into account. For hiding (and not removing) outliers, you can still set
outlier.shape = NA
. Also, it has gained a
staplewidth
argument that can be used to draw staples: horizontal lines at the end of the boxplot whiskers. The default,
staplewidth = 0
, will suppress the staples so your current box plots continue to look the same.
ggplot
diamonds
aes
cut
price
geom_boxplot
outliers
FALSE
, staplewidth
0.5
The scales functions now do a better job at reporting
which
scale has encountered an error.
scale_colour_brewer
breaks
, labels
#>
Error
in `scale_colour_brewer()`:
#>
`breaks` and `labels` must have the same length.
ggplot
mpg
aes
class
displ
geom_boxplot
scale_x_continuous
#>
Error
in `scale_x_continuous()`:
#>
Discrete values supplied to continuous scale.
#>
Example values:
"compact"
"compact"
"compact"
"compact"
, and
"compact"
ggplot
msleep
aes
bodywt
brainwt
geom_point
na.rm
TRUE
scale_x_log10
#> Warning in transformation$transform(x): NaNs produced
#> Warning in scale_x_log10():
log-10
transformation introduced infinite values.
Acknowledgements
Thank you to all people who have contributed issues, code and comments to this release:
@92amartins
@a-torgovitsky
@aarongraybill
@aavogt
@agila5
@ahcyip
@AlexanderCasper
@alexkrohn
@alofting
@andrewgustar
@antagomir
@aphalo
@Ari04T
@AroneyS
@Asa12138
@ashgreat
@averissimo
@bakerwm
@balling-dev
@banbh
@barracuda156
@BartJanvanRossum
@beansrowning
@benimwolfspelz
@bfordAIMS
@bguiastr
@bnicenboim
@BrianDiggs
@bsgerber
@burrapreeti
@bwiernik
@ccsarapas
@CGlemser
@chiajungTung
@chipsin87
@cjvanlissa
@CorradoLanera
@danielneilson
@danli349
@DasHammett
@davidhodge931
@DavisVaughan
@dieghernan
@Ductmonkey
@edent
@Elham-adabi
@ELICHOS
@eliocamp
@ellisp
@emuise
@erikdeluca
@f2il-kieranmace
@FDylanT
@fkohrt
@francisbarton
@fredcallaway
@frezza-metabolomics
@GabrielHoffman
@gaospecial
@garyzhubc
@gavinsimpson
@Generalized
@ghost
@giadasp
@GMSL1
@grantmcdermott
@hadley
@hlynurhallgrims
@holgerbrandl
@hpages
@HRodenhizer
@hub-shale
@hughjonesd
@ibuiltthis
@ingewortel
@isaacvock
@Istalan
@istvankleijn
@jacobkasper
@jammainen
@jan-glx
@JaredAllen2
@jashapiro
@jimjam-slam
@jmuhlenkamp
@jonspring
@JorisChau
@joshhwuu
@jpeasari
@jromanowska
@jsacerot
@jtlandis
@jtr13
@jttoivon
@karchern
@klin333
@kmavrommatis
@kramerrs
@krlmlr
@kylebutts
@larmarange
@latot
@lhami
@liang09255
@linzi-sg
@lionel-
@lnarwhale
@manjumc1975
@mariadelmarq
@matanhakim
@math-mcshane
@mattgalbraith
@matthewjnield
@mcwayrm
@melissagwolf
@MichaelChirico
@MikkoVihtakari
@MjelleLab
@mjskay
@mkoohafkan
@mmokrejs
@modmost
@moodymudskipper
@morrisseyj
@mps9506
@Nh-code
@njtierney
@oliviercailloux
@olivroy
@otaviolovison
@pablobernabeu
@paulatn240
@phauchamps
@quantixed
@ralmond
@ramiromagno
@reallzg
@retodomax
@robbiebatley
@Rong-Zh
@rossellhayes
@RoyalTS
@rvalieris
@s-andrews
@s-elsheikh
@schloerke
@Sckende
@sdmason
@sirallen
@slowkow
@spaette
@steveharoz
@sunroofgod
@szimmer
@tbates
@teunbrand
@tfjaeger
@thomasp85
@TimBMK
@TimTaylor
@tjebo
@trekonom
@tungttnguyen
@twest820
@UliSchopp
@vnijs
@warnes
@wbvguo
@willgearty
@Yann-C-INN
@yannk-lm
@Yunuuuu
@yutannihilation
@yuw444
@zekiakyol
, and
@zhenglukai
Contents
The tidyverse is proudly supported by
US