We are happy to announce the release of ggplot2 3.5.0. This is one blogpost among several outlining a new polar coordinate system. Please find the main release post to read about other exciting changes.
Polar coordinates are a good reminder of the flexibility of the Grammar of Graphics: pie charts are just bar charts with polar coordinates. While the tried and tested
coord_polar()
has served well in the past to fulfill your pie chart needs, we felt it was due some modernisation. We realised we could not adapt
coord_polar()
to fit with the
new guide system without severely breaking existing plots, so
coord_radial()
was born to give a facelift to the polar coordinate system in ggplot2.
Relative to
coord_polar()
,
coord_radial()
can:
- Draw circle sectors instead of only full circles.
- Avoid data vanishing in the centre of the plot.
- Adjust text angles on the fly.
- Use the new guide system.
An updated look
The first noticeable contrast with
coord_polar()
is that
coord_radial()
is not particularly suited to building pie charts. Instead, it uses the scale expansion conventions like
coord_cartesian()
. This makes sense for most chart types, but not pie charts. Nonetheless, you can use the expand = FALSE
setting to use
coord_radial()
for pie charts.
library(ggplot2)
library(patchwork)
library(scales)
pie <- ggplot(mtcars, aes(y = factor(1), fill = factor(cyl))) +
geom_bar(width = 1) +
scale_y_discrete(guide = "none", name = NULL) +
guides(fill = "none")
default <- pie + coord_radial() + ggtitle("default")
no_expand <- pie + coord_radial(expand = FALSE) + ggtitle("expand = FALSE")
polar <- pie + coord_polar() + ggtitle("coord_polar()")
default | no_expand | polar
Some visual differences stand out in the plots above. In
coord_radial()
, the panel background covers the data area of the plot, not a rectangle. It also does not have a grid-line encircling the plot and instead uses tick marks to indicate values along the theta (angle) coordinate. You may also notice that
coord_polar()
still draws the radius axis, despite instructions to use guide = "none"
. That is the integration with the guide system that birthed
coord_radial()
.
Partial polar plots
Another important difference is that
coord_radial()
does not necessarily need to display a full circle. By setting the start
and end
arguments separately, you can now make a partial polar plot. This makes it much easier to make semi- or quarter-circle plots.
p <- ggplot(mpg, aes(displ, hwy)) +
geom_point()
half <- p + coord_radial(start = -0.5 * pi, end = 0.5 * pi) +
ggtitle("−0.5π to +0.5π")
quarter <- p + coord_radial(start = 0, end = 0.5 * pi) +
ggtitle("0 to +0.5π")
half | quarter
Donuts
It was already possible to turn a pie-chart into a donut-chart with
coord_polar()
. This is made even easier in
coord_radial()
by setting the inner.radius
argument to make a donut hole. For most plots, this avoids crowding data points in the center of the plot: points with a widely different theta
coordinate but similarly small r
coordinate are placed further apart.
p + coord_radial(inner.radius = 0.3, r_axis_inside = TRUE)
Text annotations
A common grievance with about polar coordinates is that it was cumbersome to rotate text annotations along with the theta
coordinate. Calculating the correct angles for labels is pretty involved and usually changes from plot to plot depending on how many items need to be displayed. To remove some of this hassle
coord_radial()
has a rotate_angle
switch, that will line up the text’s angle
aesthetic with the theta coordinate. For text angles of 0 degrees, this will place text in a tangent orientation to the circle and for angles of 90 degrees, this places text along the radius, as in the plot below.
ggplot(mtcars, aes(seq_along(mpg), mpg)) +
geom_col(width = 1) +
geom_text(
aes(y = 32, label = rownames(mtcars)),
angle = 90, hjust = 1
) +
coord_radial(rotate_angle = TRUE, expand = FALSE)
Axes
Because the logic of drawing axes for polar coordinates is not the same as when axes are perfectly vertical or horizontal, we used the new guide system to build an axis specific to
coord_radial()
: the
guide_axis_theta()
axis. Guides for
coord_radial()
can be set using theta
and r
name in the
guides()
function. While the r
axis can be the regular
guide_axis()
, the theta
axis uses the highly specialised
guide_axis_theta()
. The theta axis shares many features with typical axes, like setting the text angle or the new minor.ticks
and cap
settings. More on these settings in the
axis blog. As seen in previous plots, the default is to place text horizontally. One neat trick we’ve put into
coord_radial()
is that we can set a relative text angle in the guides, such as in the plot below.
ggplot(mpg, aes(class, displ)) +
geom_boxplot() +
coord_radial(start = 0.25 * pi, end = 1.75 * pi) +
guides(
theta = guide_axis_theta(angle = 0),
r = guide_axis(angle = 0)
)
The theme elements to style these axes have the theta
or r
position indication, so to change the the axis line, you use the axis.line.theta
and axis.line.r
arguments. The theme settings can also be used to set the absolute angle of text.
ggplot(mpg, aes(class, displ)) +
geom_boxplot() +
coord_radial(start = 0.25 * pi, end = 1.75 * pi) +
theme(
axis.line.theta = element_line(colour = "red"),
axis.text.theta = element_text(angle = 90),
axis.text.r = element_text(colour = "blue")
)
Lastly, there can also be secondary axes. We anticipate that this is practically never needed, as grid lines follow the primary axes and without them, it is very hard to read from axes in polar coordinates. However, if there is some reason for using secondary axes on polar coordinates, you can use the theta.sec
and r.sec
names in the
guides()
function to control the guides. Please note that a secondary theta axis is entirely useless when inner.radius = 0
(the default). There are no separate theme options for secondary r/theta axes, but to style them separately from the primary axes, you can use the theme
argument in the guide instead.
ggplot(pressure, aes(temperature, pressure)) +
geom_line(colour = "blue") +
scale_x_continuous(
labels = label_number(suffix = "°C"),
sec.axis = sec_axis(~ .x * 9/5 + 35, labels = label_number(suffix = "°F"))
) +
scale_y_continuous(
labels = label_number(suffix = " mmHg"),
sec.axis = sec_axis(~ .x * 0.133322, labels = label_number(suffix = " kPa"))
) +
guides(
theta.sec = guide_axis_theta(theme = theme(axis.line.theta = element_line())),
r.sec = guide_axis(theme = theme(axis.text.r = element_text(colour = "red")))
) +
coord_radial(
start = 0.25 * pi, end = 1.75 * pi,
inner.radius = 0.3
)