tidyverse / ggplot2

An implementation of the Grammar of Graphics in R
https://ggplot2.tidyverse.org
Other
6.39k stars 2k forks source link

When x values contain 0, using coord_trans("sqrt") removes the axis line #5919

Open kdarras opened 1 month ago

kdarras commented 1 month ago
ggplot(iris,aes(Petal.Length,Sepal.Width))+
  geom_point()+
  coord_trans(x="sqrt")+
  theme(axis.line.x = element_line(color="black"))

image

iris$Petal.Length[1]=0
ggplot(iris,aes(Petal.Length,Sepal.Width))+
  geom_point()+
  coord_trans(x="sqrt")+
  theme(axis.line.x = element_line(color="black"))

image

I believe the axis line should stay preserved, since sqrt(0)=0, or is there a conceptual error in my reasoning?

teunbrand commented 1 month ago

You're right that the axis should be preserved. I think it probably has an issue with calculating the left-most side of the axis that is smaller than 0. I'll have to dig a little bit to see what is going on.

teunbrand commented 1 month ago

This isn't an issue with the axis, it happens as well with regular lines. Notice how the line disappears when 0 is included in the limits.

library(ggplot2)

p <- ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  coord_trans(x = "sqrt")

p + annotate("line", x = c(-Inf, 4), y = 30, colour = "red", linewidth = 5)

last_plot() +
  xlim(c(0, NA))

Created on 2024-05-31 with reprex v2.1.0

It is most likely a coord munching artefact.

teunbrand commented 1 month ago

I can't currently see an elegant solution to this problem. Essentially, the scale expension produces negative values that cannot be transformed properly. We faced this in scale_x_sqrt() break calculations too, but for coord transforms we cannot simply squish the limits to the domain of the tranformation.

Instead, I'll suggest the following workaround, where we make a new transformation that will just be sqrt() for all positive values, but sign-preserves sqrt(abs(x)) for negative values. This was previously suggested here as well.

library(ggplot2)
library(scales)

sqrt2 <- new_transform(
  "sqrt2",
  transform = \(x) sign(x) * sqrt(abs(x)),
  inverse = \(x) sign(x) * abs(x)^2
)

plot(sqrt2, xlim = c(-5, 5))

ggplot(iris,aes(Petal.Length,Sepal.Width))+
  geom_point()+
  coord_trans(x=sqrt2, xlim = c(0, NA))+
  theme(axis.line.x = element_line(color="black"))

Created on 2024-06-03 with reprex v2.1.0

kdarras commented 1 month ago

This did the trick, thanks Teun!