3

This is essentially a follow up question on How does ggplot calculate its default breaks? and I came across this when trying to find a slightly more elegant solution for How to add y-axis labels inside coord_polar graph ggplot?.

Apparently, the breaks are always calculated with scales::extended_breaks. However, it seems that the limits of those breaks are dropped with polar coordinates, as well as with a legend guide on continuous data.

Where does this happen?

library(ggplot2)

ggplot(mtcars, aes(x = mpg, y = mpg, size = hp)) +
  geom_point() +
  coord_polar() +
  labs(color = 'am')

Compare with


scales::extended_breaks()(mtcars$mpg)
#> [1] 10 15 20 25 30 35
scales::extended_breaks()(mtcars$hp)
#> [1]  50 100 150 200 250 300 350

Created on 2023-04-01 with reprex v2.0.2

tjebo
  • 21,977
  • 7
  • 58
  • 94
  • Just had a quick look. But as far as I get it's not the limits which get dropped, it's the scale or the limits which does not get expanded, i.e. only the position scales have an `expand` argument. And for `coord_polar` even the expansion of the positional scales is set to zero which happens in `CoordPolar$setup_panel_params`. Actually you get the same breaks for the x and y scale with `coord_cartesian` when you remove the expansion via `expand=c(0, 0)`. – stefan Apr 01 '23 at 18:13

1 Answers1

2

As I mentioned in my comment the issue with both the guides for non-positional scales and coord_polar is that the scale or more exactly the limits don't get expanded. For coord_polar even the default exapnsion is set to zero which happens in CoordPolar$setup_panel_params:

...
if (self$theta == n) {
  expansion <- ggplot2:::default_expansion(scale, c(0, 0.5), c(0, 0))
} else {
  expansion <- ggplot2:::default_expansion(scale, c(0, 0),   c(0, 0))
}
...

As a consequence the lower and upper breaks returned by scales::breaks_extended will in general get dropped as it does not fall inside the limits.

This said, one option would be to extend the limits or to use a custom function which does this by computing the limits as range(scales::breaks_extended()):

library(ggplot2)

limits_extended <- function() {
  function(x) {
    range((scales::breaks_extended())(x))
  }
}

ggplot(mtcars, aes(x = mpg, y = mpg, size = hp)) +
  geom_point() +
  scale_x_continuous(limits = limits_extended()) +
  scale_y_continuous(limits = limits_extended()) +
  scale_size(limits = limits_extended()) +
  coord_polar() +
  labs(color = 'am')

stefan
  • 90,330
  • 6
  • 25
  • 51
  • Interesting enough, the actual magic - as alluded to in the other post - seems to happen in `labeling::extended`, not a very easy to understand function, where the breaks must be calculated relative to the "extended" (expanded!) range of your data, but then, as you say, when there is no expansion, the limits will be "dropped". – tjebo Apr 02 '23 at 16:08
  • 1
    As always a very nice question which reminded me to keep an eye on the limits and teached me something new about `coord_polar`. A good opportunity to once again dig a bit deeper into the ggplot2 source code. (: – stefan Apr 02 '23 at 18:54