8

In ggplot2, the coord_fixed() coordinate system ensures that the aspect ratio of the data is maintained at a given value. So, the shape of the panel changes to maintain the shape of the data. Meanwhile coord_flip() swaps the axes of the plot. However, a plot in ggplot2 must have exactly one coordinate system, so these functions cannot be combined.

My question is:

Does there exist a way to combine the behaviours of coord_fixed() and coord_flip(), resulting in a coordinate system with the x and y axes exchanged and a fixed aspect ratio of the data?

This is a popular question, however the common answer is incorrect:

The commonly suggested answer is to use coord_flip() together with theme(aspect.ratio = 1) instead of coord_fixed(). However, as per the ggplot2 documentation, this setting refers to the "aspect ratio of the panel." Thus, the data will change shape to maintain the shape of the panel.

I suspect that this is a feature that does not currently exist in ggplot2. But more importantly I think that a correct solution or at least response to this question should be documented.

Quick minimal example of the issue:

library(ggplot2)
x <- 1:100; data <- data.frame(x = x, y = x * 2)
p <- ggplot(data, aes(x, y)) + geom_point()

p # by default panel and data both fit to device window
p + coord_fixed() # panel changes shape to maintain shape of data
p + theme(aspect.ratio = 1) # data changes shape to maintain shape of panel
p + coord_fixed() + coord_flip() # coord_flip() overwrites coord_fixed()

# popular suggested answer does not maintain aspect ratio of data:
p + coord_flip() + theme(aspect.ratio = 1)
Community
  • 1
  • 1
mb7744
  • 390
  • 2
  • 12

2 Answers2

6

I agree that the theme solution isn't really a proper one. Here is a solution that does work programatically by calculating the aspect from the actual axes ranges stored in the plot object, but it takes a few lines of code:

ranges <- ggplot_build(p)$layout$panel_ranges[[1]][c('x.range', 'y.range')]
sizes <- sapply(ranges, diff)
aspect <- sizes[1] / sizes[2]

p + coord_flip() + theme(aspect.ratio = aspect)

enter image description here

The solution I would probably use in practice, is to use the horizontal geoms in the ggstance package (although this may not always be feasible).

Note: This will only give the exact correct answer for two continuous scales with an equal multiplicative extend argument (i.e. the default).

edit: In many cases I would recommend using coord_equal combined with the ggstance package instead of this solution.

Axeman
  • 32,068
  • 8
  • 81
  • 94
  • Cheers, I think that solution is the best that currently exists. (But regarding your first sentence -- the `theme` solution is just wrong, no qualification necessary. I am looking at spatial data and it is critical to know whether the data is being shown in its true shape. The `theme` solution can have the opposite effect and distort data further.) – mb7744 Feb 08 '17 at 15:20
  • @mb7744 I was merely referring to the fact that for _many_ people (but certainly not all) the original solution is fine. I'm not disagreeing with you (which is why I answered the question). – Axeman Feb 08 '17 at 15:23
  • Sure---but what I'm getting is that that it may only be fine for people that don't notice the difference, and misinterpret their plot. I think this is why the last two questions about this on SO have incorrect accepted answers. But this is just me nitpicking. I appreciate your solution! – mb7744 Feb 08 '17 at 16:18
1

I ended up just flipping the x and y arguments in the aes specification. So for example instead of:

ggplot(mtcars,aes(x=wt,y=drat))+geom_point()+coord_fixed()

I did:

ggplot(mtcars,aes(x=drat,y=wt))+geom_point()+coord_fixed()
nisetama
  • 7,764
  • 1
  • 34
  • 21