# packageslibrary(ggplot2)library(dplyr)library(forcats)
# set default global themetheme_set(theme_minimal(base_size = 13) + theme(axis.text.x = element_text(colour = "snow3")))
Use Barplot in Polar Coordinate with ggplot2 to Visualize Cars Engine Power

In this article, we’ll visualize the automobiles’ horsepower using an ordered barplot in a polar coordinate. Major technical highlights in this article include:
- Reorder the bar plot.
- Transform a linear coordinate into a polar coordinate.
- Display elements that overflow beyond the plot boundary.
- Adjust text positions in the polar coordinate.
Data cleanup
In this visualization, we’ll use the mtcars
dataset which is built in base R.
Prepare the dataset to generate an ordered barplot:
Turn the numeric variable
cyl
(cylinder number ) to a categorical variable. This way, whencyl
is mapped tocolor
, the cylinder number (4, 6, or 8) will be appropriately displayed as distinct colors on a discrete color scale (legend), instead of on a less readable continuous (gradient) color scale (color bar).Reorder the barplot by horsepower (
hp
). Here we first rearrange the rows byhp
, and turn thecar
variable to a factor with levels (car names) following the current row order. More details on graphic elements reordering in ggplot2 can be found in this complete guide.
<- mtcars %>% as_tibble() %>% mtcars2 mutate(car = rownames(mtcars), cyl = factor(cyl)) %>% # reorder cars by horsepower arrange(hp) %>% mutate(car = factor(car, levels = car))
head(mtcars2, 4) # ready for visualization
Output:
# A tibble: 4 × 12
mpg cyl disp hp drat wt qsec vs am gear carb car
<dbl> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <fct>
1 30.4 4 75.7 52 4.93 1.62 18.5 1 1 4 2 Honda Civic
2 24.4 4 147. 62 3.69 3.19 20 1 0 4 2 Merc 240D
3 33.9 4 71.1 65 4.22 1.84 19.9 1 1 4 1 Toyota Coro…
4 32.4 4 78.7 66 4.08 2.2 19.5 1 1 4 1 Fiat 128
Visualization
Create an ordered bar plot.
<- mtcars2 %>% p1 ggplot(aes(x = car, y = hp, fill = cyl)) + geom_col() p1
Transform the linear coordinate to a polar coordinate.
theta = "x"
(default) maps thex
aesthetic from linear coordinate to angles in the polar coordinate. In contrast,theta = "y"
maps they
aesthetic to angles, and is used to create pie and donut charts.Use
clip = "off"
to display graphical elements that extend beyond the plot boundary (often done together with plot margin adjustment; see more in the step of creating plotp5
).
<- p1 + coord_polar(theta = "x", clip = "off") p2 p2
We aim to label the car names in line with the orientation of associated bars. To do this, first we need to calculate the angles by which the texts need to be rotated.
<- nrow(mtcars2) n = 360 / n angle_perSlice
# degrees of angle to rotate per bar / text angle_perSlice
Output:
[1] 11.25
Create a vector of angles of successive rotations for car names. The rotation starts from the first bar (i.e., the most left in linear coordinate) - Honda Civic, with an initial angle of 81° (by visual estimate of p2
).
<- 81 - (0:(n-1)) * angle_perSlice angles_to_rotate angles_to_rotate
Output:
[1] 81.00 69.75 58.50 47.25 36.00 24.75 13.50 2.25 -9.00
[10] -20.25 -31.50 -42.75 -54.00 -65.25 -76.50 -87.75 -99.00 -110.25
[19] -121.50 -132.75 -144.00 -155.25 -166.50 -177.75 -189.00 -200.25 -211.50
[28] -222.75 -234.00 -245.25 -256.50 -267.75
Add labels of car names. The x-axis from a linear coordinate collapses into the central point in a polar coordinate. Accordingly, mapped with x = car
(inherited from the ggplot
line), all texts are headed towards the center of the circle. In addition, the texts are left aligned (by hjust = 0
) to the y
position that is 40 units above the height of the lollipop (y = hp + 40
), with respective rotations specified in angles_to_rotate
.
+ geom_text(aes(label = car, y = hp + 10), p2 angle = angles_to_rotate, hjust = 0)
In the above plot, texts on the right half are nicely rotated, but those on the left half are turned upside down, and are difficult to read. The following edit aims to readjust the rotation angles of texts on the left side.
# same text angles for the first 17 cars (right half) from Honda civic to Merc 280C<- angles_to_rotate[1:17] angles1
# update text angles for the left half cars from Dodge to Maserati<- 71 - (0: (n - 17 -1)) * angle_perSlice angles2
# an updated angle vector<- c(angles1, angles2) angles_to_rotate_new
+ geom_text(aes(label = car, y = hp + 10), p2 angle = angles_to_rotate_new, hjust = 0)
Now the text rotation angle are well adjusted. However, as all texts are left aligned to y = hp + 10
, the left-half texts are shifted towards the right, and mostly overlapped with the bars. To fix this, we’ll instead right-justify texts on the left side, while keep the left justification for texts on the right side.
# trial 3: fix the alignment+ geom_text(aes(label = car, y = hp + 10), p2 angle = angles_to_rotate_new, hjust = c(rep(0, 17), rep(1, n - 17) ))
Much better - now add a few more touch on the texts font and size. In particular, we’ll use smaller font sizes of 2 ~ 2.5 for the first 5 cars (of smaller horsepower) that occupy a more jammed space, and a larger size 3 for the other texts. Note that the last (largest) bar’s text label “Maserati Bora” is not displayed completely. We’ll fix it at the last step.
<- p2 + p3 geom_text(aes(label = car, y = hp + 10), angle = angles_to_rotate_new, hjust = c(rep(0, 17), rep(1, n - 17) ), size = c(2, 2, 2.2, 2.3, 2.5, rep(3, n - 5)), fontface = "bold") p3
Add plot title at the circle center, and update color scale.
<- p3 + p4 annotate(geom = "text", x = 0, y = 0, label = "Turbine \nPower", size = 4, fontface = "bold", color = "white") + scale_fill_manual(values = c("snow4", "steelblue", "brown")) p4
A touch on the theme:
- Apply the theme of void to make a clean background.
- Relocate the legend to the top right corner (using relative coordinates of a linear coordinate).
- Increase the margin at the top of the plot which, together with the prior
clip = "off"
, displays completely the “Maserati” text label.
<- p4 + theme_void() + p5 theme(legend.position = c(.7, .93), plot.margin = margin(t = 30, unit = "pt")) p5
# packageslibrary(ggplot2)library(dplyr)library(forcats)
# set default global themetheme_set(theme_minimal(base_size = 13) + theme(axis.text.x = element_text(colour = "snow3")))
# data cleanup<- mtcars %>% as_tibble() %>% mtcars2 mutate(car = rownames(mtcars), cyl = factor(cyl)) %>% # reorder cars by horsepower arrange(hp) %>% mutate(car = factor(car, levels = car))
head(mtcars2, 4) # ready for visualization
# Create an ordered bar plot.<- mtcars2 %>% p1 ggplot(aes(x = car, y = hp, fill = cyl)) + geom_col() p1
# Transform the linear coordinate to a polar coordinate.<- p1 + coord_polar(clip = "off") p2 p2
# manually label car names in line with the orientation of associated bars# first calculate the angles by which the texts need to be rotated. <- nrow(mtcars2) n = 360 / n angle_perSlice
# degrees of angle to rotate per bar / text angle_perSlice
# Create a vector of angles of successive rotations for all car names.<- 81 - (0:(n-1)) * angle_perSlice angles_to_rotate angles_to_rotate
# Add labels of car names.+ geom_text(aes(label = car, y = hp + 10), p2 angle = angles_to_rotate, hjust = 0)
# Readjust the rotation angles of texts on the left side. # keep the same text angles for the first 17 cars (right half) from Honda civic to Merc 280C<- angles_to_rotate[1:17] angles1 # update text angles for the left half cars from Dodge to Maserati<- 71 - (0: (n - 17 -1)) * angle_perSlice angles2 # an updated angle vector<- c(angles1, angles2) angles_to_rotate_new
+ geom_text(aes(label = car, y = hp + 10), p2 angle = angles_to_rotate_new, hjust = 0)
# Update text justification:## Right-justify texts instead on the left side of plot;## and keep the same left justification for texts on the right side. + geom_text(aes(label = car, y = hp + 10), p2 angle = angles_to_rotate_new, hjust = c(rep(0, 17), rep(1, n - 17) ))
# Update the labels size and font. # use smaller font for the first 5 cars that occupy a very small space<- p2 + p3 geom_text(aes(label = car, y = hp + 10), angle = angles_to_rotate_new, hjust = c(rep(0, 17), rep(1, n - 17) ), size = c(2, 2, 2.2, 2.3, 2.5, rep(3, n - 5)), fontface = "bold") p3
# Add plot title at the circle center, and update color scale. <- p3 + p4 annotate(geom = "text", x = 0, y = 0, label = "Turbine \nPower", size = 4, fontface = "bold", color = "white") + scale_fill_manual(values = c("snow4", "steelblue", "brown")) p4
# Relocate the legend to the top right corner. # Increase the margin at the top of the plot<- p4 + theme_void() + p5 theme(legend.position = c(.7, .93), plot.margin = margin(t = 30, unit = "pt")) p5
Continue Exploring — 🚀 one level up!
In the following article, we’ll update the above barplot into a donut-lollipop with color and font-enriched plot title.
All the plots above are generated by converting the x
aesthetic from a linear coordinate into the angles within a polar coordinate. More common in practice, however, the y
aesthetic is mapped into angles to make “authentic” pie and donut charts to visualize the “proportion of a whole”. Check the following exploded donut plots in faceted panels that visualize the contribution of the top 4 largest GDP makers to the total GDP in each continent.