Force ggplot2 scatter plot to be square shaped

RGgplot2Scatter Plot

R Problem Overview


I can force ggplot2 scatter plot to be square shaped with the same x and y scaling using xlim() and ylim(), but it needs manual calculation of the limits. Is there any more convenient way of doing it?

By square shape I mean two requirements:

  1. The same scale on x and y axis.
  2. The equal length of x and y axis.

R Solutions


Solution 1 - R

If you want to make the distance scale points the same, then use coord_fixed():

p <- ggplot(...)
p <- p + coord_fixed() # ratio parameter defaults to 1 i.e. y / x = 1

If you want to ensure that the resulting plot is square then you would also need to specify the x and y limits to be the same (or at least have the same range). xlim and ylim are both arguments to coord_fixed. So you could do this manually using those arguments. Or you could use a function to extract out limits from the data.

Solution 2 - R

Note: for a square shape (irrespective of the data being plotted),

ggplot() + theme(aspect.ratio=1)

enter image description here

Solution 3 - R

Probably the ugliest code you'll see today, but it does the trick.

The ranges of your x and y axes are accessible from ggplot_build:

r<-max(abs(ggplot_build(your_plot)$panel$ranges[[1]]$x.range))
s<-max(abs(ggplot_build(your_plot)$panel$ranges[[1]]$y.range))
t<-round(max(r,s),1)
your_plot<-your_plot+coord_equal(xlim=c(-t,t),ylim=c(-t,t))

Solution 4 - R

All the solutions provided in the previous answers do not work for my R version: R version 3.6.1.

ggplot_build(pot)$panel$ranges[[1]]$x.range # return NULL value

The solution mentioned by @Gerhard Burger in the linked URL works for my case:

r<-max(abs(layer_scales(plt)$x$range$range))
s<-max(abs(layer_scales(plt)$y$range$range))
t<-round(max(r,s),1)
plt<-plt+coord_equal(xlim=c(0,t),ylim=c(0,t))

Solution 5 - R

Building on Ramons answer, this function works nicely for me and I consider it not as ugly, since one can hide the function definition...

squarePlot <- function(plt){
    return(plt+coord_equal()+
            expand_limits(x=ggplot_build(plt)$panel$ranges[[1]]$y.range,
                          y=ggplot_build(plt)$panel$ranges[[1]]$x.range))
}

just wrapping Ramon's code in a function didn't work out for me because the t variable is defined in the "wrong" environment.

Solution 6 - R

Although an older post, this is still a top hit on google for me.

This function in the tune package seems to accomplish this now:

p <- 
  mtcars %>% 
  ggplot(aes(drat, wt)) + 
  geom_abline(slope = 1, intercept = 0) +
  geom_point()
p

enter image description here

p + tune::coord_obs_pred()

enter image description here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAliView Question on Stackoverflow
Solution 1 - RmysView Answer on Stackoverflow
Solution 2 - RbaptisteView Answer on Stackoverflow
Solution 3 - RRamonView Answer on Stackoverflow
Solution 4 - RGood WillView Answer on Stackoverflow
Solution 5 - RSven StodtmannView Answer on Stackoverflow
Solution 6 - RbryanLView Answer on Stackoverflow