 # Coordinate systems in ggplot2: easily overlooked and rather underrated

Lea Waniek

All plots have coordinate systems. Perhaps because they are such an integral element of plots, they are easily overlooked. However, in ggplot2, there are several very useful options to customize the coordinate systems of plots, which we will not overlook but explore in this blog post.

Since it is spring, we will use a random subset of the famous iris data set. When we plot the petal length against the petal width, and map species onto color and play around a little with the shape, color and sizes of aesthetics, one obtains this vernal plot: <span class="hljs-comment"># Base plot</span>
plot_base <- ggplot(data = df_iris) +
geom_point(aes(x = Petal.Length, y = Petal.Width, color = Species),
size = <span class="hljs-number">3</span>, alpha = <span class="hljs-number">0.9</span>, shape = <span class="hljs-number">8</span>) +
geom_point(aes(x = Petal.Length, y = Petal.Width),
color = <span class="hljs-string">"yellow"</span>, size = <span class="hljs-number">0.4</span>) +
scale_color_manual(values = c(<span class="hljs-string">"#693FE9"</span>, <span class="hljs-string">"#A089F8"</span>, <span class="hljs-string">"#0000FF"</span>)) +
theme_minimal()


## Cartesian coordinate system

### Zooming in and out

The coordinate system can be manipulated by adding one of ggplot’s different coordinate systems. When you are imagining a coordinate system, you are most likely thinking of a Cartesian one. The Cartesian coordinate system combines x and y dimension orthogonally and is ggplots default (coord_cartesian).

There also are several varaitions of the familiar Cartesian coordinate system in ggplot, namely coord_fixed, coord_flip and coord_trans. For all of them, the displayed section of the data can be specified by defining the maximal value depicted on the x (xlim =) and y (ylim =) axis. This allows to “zoom in” or “zoom out” of a plot. It is a great advantage, that all manipulations of the coordinate system only alter the depiction of the data but not the data itself.

<span class="hljs-comment"># Zooming in with xlim/ylim</span>
plot_base +
coord_cartesian(xlim = 5, ylim = 2) +
ggtitle("coord_cartesian <span class="hljs-keyword">with</span> xlim = <span class="hljs-number">5</span> <span class="hljs-keyword">and</span> ylim = <span class="hljs-number">2</span><span class="hljs-string">")
</span> ### Specifying the “aspect ratio” of the axes

Via coord_fixed one can specify the exact ratio of the length of a y unit relative to the length of a x unit within the final visualization.

# Setting the <span class="hljs-string">"aspect ratio"</span> of <span class="hljs-symbol">y</span> vs. <span class="hljs-symbol">x</span> units
plot_base +
coord_fixed(ratio = <span class="hljs-number">1</span>/<span class="hljs-number">2</span>) +
ggtitle(<span class="hljs-string">"coord_fixed with ratio = 1/2"</span>) ### Transforming the scales of the axes

This helps to emphasize the exact insight one wants to communicate. Another way to do so is coord_trans, which allows several transformations of the x and y variable (see table below, taken from Wickham 2016 page 97). Let me stress this again, very conveniently such transformations only pertain to the depicted – not the actual – scale of the data. This also is the reason why, regardless of the conducted transformation, the original values are used as axis labels.

<span class="hljs-comment"># Transforming the axes </span>
<span class="hljs-attribute">plot_base</span> +
coord_trans(x = <span class="hljs-string">"log"</span>, y = <span class="hljs-string">"log2"</span>) +
ggtitle(<span class="hljs-string">"coord_trans with x = \"log\" and y = \"log2\""</span>) ### Swapping the axes

The last of the Cartesian options, cood_flip, swaps x and y axis. For example, this option can be useful, when we intend to change the orientation of univariate plots as histograms or plot types – like box plots – that visualize the distribution of a continuous variable over the categories of another variable. Nonetheless, coord_flip also works with all other plots. This multiplies the overall possibilities for designing plots, especially since all Cartesian coordinate systems can be combined.

<span class="hljs-comment"># Swapping axes </span>
<span class="hljs-comment"># base plot #2</span>
p1 <- ggplot(data = df_iris) +
geom_bar(aes(<span class="hljs-keyword">x</span> = Species, fill = Species), alpha = <span class="hljs-number">0</span>.<span class="hljs-number">6</span>) +
scale_fill_manual(<span class="hljs-keyword">values</span> = c(<span class="hljs-string">"#693FE9"</span>, <span class="hljs-string">"#A089F8"</span>, <span class="hljs-string">"#4f5fb7"</span>)) +
theme_minimal()

<span class="hljs-comment"># base plot & coord_flip()</span>
p2 <- ggplot(data = df_iris) +
geom_bar(aes(<span class="hljs-keyword">x</span> = Species, fill = Species), alpha = <span class="hljs-number">0</span>.<span class="hljs-number">6</span>) +
scale_fill_manual(<span class="hljs-keyword">values</span> = c(<span class="hljs-string">"#693FE9"</span>, <span class="hljs-string">"#A089F8"</span>, <span class="hljs-string">"#4f5fb7"</span>)) +
theme_minimal() +
coord_flip()

gridExtra::grid.arrange(p1, p2, top = <span class="hljs-string">"Bar plot without and with coord_flip"</span>) ## Polar coordinate system

The customization of Cartesian coordinate systems allows for the fine tuning of plots. However, coord_polar, the final coordinate system discussed here, changes the whole character of a plot. By using coord_polar, bar geoms are transformed to pie charts or “bullseye” plots, while line geoms are transformed to radar charts. This is done by mapping x and y to the angle and radius of the resulting plot. By default, the x variable is mapped to the angle but by setting the theta augment in coord_polar to “y” this can be changed.  While such plots might shine with respect to novelty and looks, their perceptual properties are intricate, and their correct interpretation may be quite hard and rather unintuitive.

<span class="hljs-meta"># Base plot 2 (long format, x = 1 is summed up to generate count)</span>
plot_base_2 <- df_iris %>%
dplyr::mutate(x = <span class="hljs-number">1</span>) %>%
ggplot(.) +
geom_bar(aes(x = x, fill = Species), alpha = <span class="hljs-number">0.6</span>) +
theme(axis.text = element_blank(),
axis.ticks = element_blank(),
axis.title = element_blank()) +
scale_fill_manual(values = c(<span class="hljs-string">"#693FE9"</span>, <span class="hljs-string">"#A089F8"</span>, <span class="hljs-string">"#4f5fb7"</span>)) +
theme_minimal() +
ggtitle(<span class="hljs-string">"base plot"</span>)

<span class="hljs-meta"># Bullseye plot </span>
<span class="hljs-meta"># geom_bar & coord_polar(theta = <span class="hljs-meta-string">"x"</span>)</span>
p2 <- plot_base_2 +
coord_polar(theta = <span class="hljs-string">"x"</span>) +
ggtitle(<span class="hljs-string">"theta = \"x\""</span>)

<span class="hljs-meta"># Pie chart </span>
<span class="hljs-meta"># geom_bar & coord_polar(theta = <span class="hljs-meta-string">"y"</span>)</span>
p3 <- plot_base_2 +
coord_polar(theta = <span class="hljs-string">"y"</span>) +
ggtitle(<span class="hljs-string">"theta = \"y\""</span>)

gridExtra::grid.arrange(p2, p3, plot_base_2, top = <span class="hljs-string">"geom_bar & coord_polar"</span>, ncol = <span class="hljs-number">2</span>)

# Base plot <span class="hljs-number">3</span> (long format, <span class="hljs-built_in">mean</span> width/length of sepals/petals calculated)
plot_base_3 <- iris %>%
dplyr::group_by(Species) %>%
dplyr::summarise(Petal.Length = <span class="hljs-built_in">mean</span>(Petal.Length),
Sepal.Length = <span class="hljs-built_in">mean</span>(Sepal.Length),
Sepal.Width = <span class="hljs-built_in">mean</span>(Sepal.Width),
Petal.Width = <span class="hljs-built_in">mean</span>(Petal.Width)) %>%
reshape2::melt() %>%
ggplot() +
geom_polygon(aes(group = Species, color = Species, <span class="hljs-symbol">y</span> = value, <span class="hljs-symbol">x</span> = variable),
fill = NA) +
scale_color_manual(values = c(<span class="hljs-string">"#693FE9"</span>, <span class="hljs-string">"#A089F8"</span>, <span class="hljs-string">"#4f5fb7"</span>)) +
theme_minimal() +
ggtitle(<span class="hljs-string">"base plot"</span>)

# geom_polygon & coord_polar
p2 <- plot_base_3 +
theme_minimal() +
coord_polar() +
ggtitle(<span class="hljs-string">"coord_polar"</span>)

gridExtra::grid.arrange(plot_base_3, p2, top = <span class="hljs-string">"geom_polygon & coord_polar"</span>, ncol = <span class="hljs-number">2</span>)


## References

• Wickham, H. (2016). ggplot2: elegant graphics for data analysis. Springer.
###### Über den Autor #### Lea Waniek

I am a data scientist at STATWORX, apart from machine learning, I love to play around with RMarkdown and ggplot2, making data science beautiful inside and out.