11
votes

I am trying to create a squared transform for the y-axis using scales::trans_new but am hitting an error.

MWE

data = data.frame(x = 1:10, y = runif(10), z=rnorm(10, 10))

library(ggplot2)
ggplot(data, aes(x, y, size=z)) +
geom_point() +
scale_y_continuous(trans=scales::trans_new("sq", function(x) x^2, function(x) x^(1/2)))

gives the error

Error in if (zero_range(as.numeric(limits))) { :
missing value where TRUE/FALSE needed

I tries adding limits and domain but got the same error.

scale_y_continuous(trans=scales::trans_new("sq", function(x) x^2, function(x) x^(1/2)), limits=c(0,1))
scale_y_continuous(trans=scales::trans_new("sq", function(x) x^2, function(x) x^(1/2), domain=c(0,1)), limits=c(0,1))

It seesm to be the inverse argument that is causing the error - but all values of y are positive, so I don't understand. How can I do this please?

1
why can't you just create a new variable for the y squared value and plot that?Mike
@Mike ; because this takes care of axis scaling / lables -- it should be less effort ;)user2957945
last solution and then I will give up. scale_y_continuous(trans = scales::trans_new("sq", function(x){x^2}, function(x){sqrt(abs(x))}))Mike
thanks @mike. Indeed adding abs worked. Dave2e has come to the same conclusion by the looks of it - but I thknk a slightly safer workaround. Thanks again for your help.user2957945

1 Answers

12
votes

It seems ggplot is using the inverse function to set the y-axis limits and axis labels. If y is near zero, gglot, is padding the lower limit and is calculating a negative value. This is a "negative" (pun intended) side-effect from prettifying the limits.

Here is a work around, modifying the inverse function to prevent it from accepting a value less than zero, it will avoid the square root of a negative number error.
The lower limit of the axis will always be greater or equal to zero.

library(ggplot2)

#square function
sq<-function(x){
  x^2
}
#inverse square function (square root)
isq<-function(x){
  print(paste("isq",x))  #debug statement
  x<-ifelse(x<0, 0, x)
  sqrt(x)
}

data =  data.frame(x = 1:10, y = runif(10), z=rnorm(10, 10))

print(data$y)
ggplot(data, aes(x, y, size=z)) +
  geom_point() +
  scale_y_continuous(trans=scales::trans_new("sq", sq, isq))

Note: Remove the print function when transferring to production code.

See the link in the comment below for the bug report at Github.