Polar co-ordinates in ggplot2
allow the creation of a
variety of circular plots. These include pie charts, donut plots, radar
charts, and various circular versions of line, bar and scatter plots. In
many circumstances, curved text that follows the polar co-ordinate
system can be a clearer and more visually appealing way to label
elements of these plots and their axes. However, this is not natively
possible in ggplot
due to the mechanism of text handling in
the underlying grid
graphics system.
The textpathGrob
at the heart of the
geomtextpath
package now makes it possible to create curved
labels and axis text in polar co-ordinates with a minimum of effort.
This vignette gives a brief demonstration of these features. We start by
loading the necessary packages.
This package includes a new coord
called
coord_curvedpolar
, which is almost identical to
coord_polar
, but draws curved axis labels around the
circumferential (theta) axis.
The native coord_polar
in ggplot
produces
horizontal axis labels, as we can show in the following example:
df <- data.frame(Temperature = c(4.4, 4.6, 6.3, 8.7, 11.6, 14.1, 15.9, 15.5,
13.1, 9.7, 6.7, 4.3, 3.6, 3.9, 6.4, 9.7, 13.2,
15.8, 18, 17.8, 15.1, 11.2, 7.2, 4.4),
City = rep(c("Glasgow", "Amsterdam"), each = 12),
Month = factor(rep(month.name, 2), month.name))
p <- ggplot(df, aes(Month, Temperature)) +
geom_col(aes(fill = City), position = position_dodge(width = 1)) +
geom_vline(xintercept = 1:13 - 0.5, color = "gray90") +
geom_hline(yintercept = 0:3 * 5, color = "gray90") +
scale_fill_manual(values = c("darkorange", "dodgerblue4")) +
ggtitle("Average monthly temperature in Amsterdam and Glasgow") +
theme_bw() +
theme(panel.border = element_blank(),
axis.text.x = element_text(size = 14),
axis.title.x = element_blank(),
panel.grid.major = element_blank())
p + coord_polar()
Whereas, if we want curved axis labels, we can simply substitute
coord_curvedpolar
in as a replacement for
coord_polar
The usual theme adjustments are available via
theme(axis.text.x = element_text())
p + coord_curvedpolar() +
theme(axis.text.x = element_text(face = 2, color = "orangered3", hjust = 0))
Note that the text that would normally be upside down is automatically flipped to improve legibility.
It is also straightforward to create other labels that curve
appropriately in polar co-ordinates, using
geom_textpath
.
Straight text paths in Cartesian coordinates become curved in polar coordinates.
df <- data.frame(x = c(1, 1000), y = 1, text = "This is a perfectly flat label")
p <- ggplot(df, aes(x, y, label = text)) +
geom_textpath(size = 6) +
ylim(c(0.9, 1.1))
p
Similarly, the other geoms that are based on the same underlying mechanism curve in polar co-ordinates. For example, take the following plot in standard co-ordinates with a horizontal and vertical reference line:
p <- ggplot(mtcars, aes(mpg, disp)) +
geom_point() +
geom_texthline(yintercept = 200, label = "displacement",
hjust = 0, vjust = -0.2, color = "red4") +
geom_textvline(xintercept = 20, label = "consumption", hjust = 0.8,
linetype = 2, vjust = 1.3, color = "blue4")
p
#> Warning in geom_texthline(yintercept = 200, label = "displacement", hjust = 0, : All aesthetics have length 1, but the data has 32 rows.
#> i Did you mean to use `annotate()`?
#> Warning in geom_textvline(xintercept = 20, label = "consumption", hjust = 0.8, : All aesthetics have length 1, but the data has 32 rows.
#> i Did you mean to use `annotate()`?
If we translate to polar co-ordinates, the reference lines transform, so that our horizontal line becomes a circle and our vertical line becomes a radial line. However, we don’t need to change our labels; they will adhere to each path as it transforms without needing any other input from the user:
p + coord_polar()
#> Warning in geom_texthline(yintercept = 200, label = "displacement", hjust = 0, : All aesthetics have length 1, but the data has 32 rows.
#> i Did you mean to use `annotate()`?
#> Warning in geom_textvline(xintercept = 20, label = "consumption", hjust = 0.8, : All aesthetics have length 1, but the data has 32 rows.
#> i Did you mean to use `annotate()`?
This means that with geomtextpath, you are free to design and label plots or diagrams in Cartesian co-ordinates:
df <- data.frame(x1 = c(seq(0, 10/6 * pi, pi/3),
seq(0, 10/6 * pi, 2*pi/3)),
y1 = c(rep(2, 6), rep(-1, 3)),
x2 = c(seq(0, 10/6 * pi, pi/3) + pi/3,
seq(0, 10/6 * pi, 2*pi/3) + 2*pi/3),
y2 = c(rep(4, 6), rep(2, 3)),
group = letters[c(1:6, (1:3) * 2)],
alpha = c(rep(1, 6), rep(0.4, 3)))
p <- ggplot(df, aes(x1, y1)) +
geom_rect(aes(xmin = x1, xmax = x2, ymin = y1, ymax = y2, fill = group,
alpha = alpha),
color = "white", size = 2) +
geom_textpath(data = data.frame(x1 = seq(0, 2 * pi, length = 300),
y1 = rep(0.5, 300),
label = rep(c("stats", "effects", "polar"), each = 100)),
aes(label = label), linetype = 0, size = 8,
upright = TRUE) +
geom_textpath(data = data.frame(x1 = seq(0, 2 * pi, length = 300),
y1 = rep(3, 300),
label = rep(c("density", "smooth", "unique", "organic",
"easy to use", "automatic"),
each = 50)),
aes(label = label), linetype = 0, size = 4.6, color = "white",
upright = TRUE) +
scale_y_continuous(limits = c(-5, 4)) +
scale_x_continuous(limits = c(0, 2*pi)) +
scale_fill_manual(values = c("deepskyblue3", "deepskyblue4",
"green3", "green4","tomato", "tomato2")) +
scale_alpha_identity() +
theme_void() +
theme(legend.position = "none")
#> Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
#> i Please use `linewidth` instead.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.
p
Which will flip nicely to polar co-ordinates without the need for re-labelling.