4

I generate a fine histogram here with both positive and negative numbers.

x <- rnorm(5000,0,1000)
library(ggplot2)
df <- data.frame(x)
ggplot(df, aes(x = x)) + geom_histogram()

What I want is to have a logged x-axis. When I do this for only positive numbers with scale_x_log10(), it works like a charm. But here it does not and it either removes my negative numbers are adds them to the positive numbers.

ggplot(df, aes(x = x)) + geom_histogram() + scale_x_log10()

All I really want is for the ticks and the spacing between the ticks to follow the log pattern and for either side of 0 on the x-axis to be mirror images of each other but I cannot seem to get that.

Ben Bolker
  • 211,554
  • 25
  • 370
  • 453
James Lloyd
  • 127
  • 1
  • 4

2 Answers2

16

This is possible to do by defining a new transformation (a "signed log", sign(x)*log(abs(x)); the asinh transformation suggested by Histogram with "negative" logarithmic scale in R might be more principled, or a signed square root as suggested in the comments above), but I question whether it's a good idea or not. Nevertheless ... ("Teach a man to fish and you feed him for a lifetime; give him a rope, and he can go hang himself ...") ... you can define your own axis transformations via trans_new as shown below.

Setup:

library(ggplot2); theme_set(theme_bw())
set.seed(101)
df <- data.frame(x=rnorm(5000,0,1000))

Set up the new transformation:

weird <- scales::trans_new("signed_log",
       transform=function(x) sign(x)*log(abs(x)),
       inverse=function(x) sign(x)*exp(abs(x)))

Try it out -- first on the raw points:

ggplot(df,aes(x,x))+geom_point()+
    scale_y_continuous(trans=weird)

enter image description here

Now on the histogram:

ggplot(df, aes(x = x)) + geom_histogram()+
    scale_x_continuous(trans=weird)

enter image description here

Things you should worry about:

  • this transformation is going to be nonsensical when you have values between -1 and 1
  • you might have to worry about transforming the axis of a histogram without scaling bin height appropriately: it may give you a misleading impression of the probability density -- although in this case ggplot(df, aes(x = weird$transform(x))) + geom_histogram() looks about the same as the plot above ...
Community
  • 1
  • 1
Ben Bolker
  • 211,554
  • 25
  • 370
  • 453
  • Thank you. When I try to apply this to some code that I am calling a column in a complex dataframe, the $ is giving an error in the `weird <- scales::trans_new("signed_log", transform=function(x) sign(x)*log(abs(x)), inverse=function(x) sign(x)*exp(abs(x)))` part of the code – James Lloyd May 25 '16 at 20:48
  • I'm sorry, but that's not enough information for me to troubleshoot your problem. If you just want to do the transformation outside ggplot (which would be simpler), define `signedlog <- function(x) sign(x)*log(abs(x))` – Ben Bolker May 25 '16 at 21:46
  • 1
    I would also consider using `log1p` and `expm1` in the transform. – Eli Korvigo Oct 07 '18 at 17:06
0

This works with small numbers!

if (require("lme4") && require("glmmTMB")) { 
weird <- scales::trans_new("signed_log",
       transform=function(x) sign(x)*log1p(abs(x)),
       inverse=function(x) sign(x)*expm1(abs(x)))

p<- plot_model(fit.me.model)

p+ scale_y_continuous(trans=weird)
}

enter image description here

Fkiran
  • 24
  • 3