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.
#> ℹ Please consider using `annotate()` or provide this layer with data containing
#> a single row.
#> Warning in geom_textvline(xintercept = 20, label = "consumption", hjust = 0.8, : All aesthetics have length 1, but the data has 32 rows.
#> ℹ Please consider using `annotate()` or provide this layer with data containing
#> a single row.
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.
#> ℹ Please consider using `annotate()` or provide this layer with data containing
#> a single row.
#> Warning in geom_textvline(xintercept = 20, label = "consumption", hjust = 0.8, : All aesthetics have length 1, but the data has 32 rows.
#> ℹ Please consider using `annotate()` or provide this layer with data containing
#> a single row.
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.
#> ℹ 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.