49

I'd like to make a plot with a reversed, log10 x scale using ggplot2:

require(ggplot2)
df <- data.frame(x=1:10, y=runif(10))
p <- ggplot(data=df, aes(x=x, y=y)) + geom_point() 

However, it seems that I can either a log10 scale or a reversed scale:

p + scale_x_reverse() + scale_x_log10() 

log10 scale, but not reversed

p + scale_x_reverse()

reversed scale, but not log10

I guess this is logical, if a layer can only have one scale. And certainly I could hack it by doing the log transform on the dataframe myself, df$xLog <- log10(df$x) but that solution is a seems contrary to the spirit of ggplot. Is there a way to get this kind of plot without doing data transformations external to the ggplot call?

Drew Steen
  • 15,225
  • 12
  • 59
  • 90
  • 2
    I expected this to work as well, but apparently its a bit complicated. There was a [work-around](https://groups.google.com/forum/?fromgroups#!searchin/ggplot2/reverse$20log$20scale/ggplot2/AfMf9L9y9fc/Qu-FnJLJaagJ) that appears to be broken in the most recent version. If @kohske or someone can't come up with another solution, might make a good feature request. – joran Jun 15 '12 at 15:57

3 Answers3

64

The link that @joran gave in his comment gives the right idea (build your own transform), but is outdated with regard to the new scales package that ggplot2 uses now. Looking at log_trans and reverse_trans in the scales package for guidance and inspiration, a reverselog_trans function can be made:

library("scales")
reverselog_trans <- function(base = exp(1)) {
    trans <- function(x) -log(x, base)
    inv <- function(x) base^(-x)
    trans_new(paste0("reverselog-", format(base)), trans, inv, 
              log_breaks(base = base), 
              domain = c(1e-100, Inf))
}

This can be used simply as:

p + scale_x_continuous(trans=reverselog_trans(10))

which gives the plot:

enter image description here

Using a slightly different data set to show that the axis is definitely reversed:

DF <- data.frame(x=1:10,  y=1:10)
ggplot(DF, aes(x=x,y=y)) + 
  geom_point() +
  scale_x_continuous(trans=reverselog_trans(10))

enter image description here

Brian Diggs
  • 55,682
  • 13
  • 158
  • 183
  • I'm afraid that this solution no longer works: Error in reverselog_trans(10) : could not find function "trans_new" Explicitly adding scales::trans_new only results in new errors, the scales function seems to have been updated :( – Richard Jan 17 '15 at 08:42
  • 2
    @Richard I just checked it and it works, but the `scales` package must be attached to the search path (`library("scales")`). That is not clear in the answer (and may not have been necessary at the time). Updating. – Brian Diggs Jan 18 '15 at 04:23
  • That easy huh?! Thank you very much!! – Richard Jan 18 '15 at 06:07
  • 1
    Alternatively use the :: notation to explicitly call functions from the scales package. --- ```reverselog_trans – Andrew Mar 10 '17 at 17:45
  • NB: the `reverselog_trans` function has appeared in CRAN https://cran.r-project.org/web/packages/ACSNMineR/index.html (found in ACSNMineR/R/plots.R). – flies Jan 09 '19 at 15:45
  • @flies If that is exported from the package, you should put together an answer using that. It would be a good addition. – Brian Diggs Jan 15 '19 at 23:15
11

ggforce package has trans_reverser() function for this task.

library(ggplot2)
library(ggforce)

p <- ggplot() +
  geom_line(aes(x = 1:100, y = 1:100))

p +
  scale_x_continuous(trans = trans_reverser('log10')) +
  annotation_logticks(sides = 'tb') +
  theme_bw()

Edit: starting from v1.2.0 of the scales package, this will also work

library(scales)

p +
  scale_x_continuous(
    trans  = compose_trans("log10", "reverse"),
    breaks = c(100, 10, 1)
  ) +
  annotation_logticks(sides = 'tb') +
  theme_bw()

p +
  scale_x_continuous(
    trans  = compose_trans("log10", "reverse"),
    labels = label_log()
  ) +
  annotation_logticks(sides = 'tb') +
  theme_bw()

Created on 2020-11-14 by the reprex package (v0.3.0)

Tung
  • 23,290
  • 6
  • 81
  • 97
3

You can apply the logarithm directly inside the ggplot function, in the aes() specification:

require(ggplot2)
df <- data.frame(x=1:10, y=runif(10))
p <- ggplot(data=df, aes(x = log10(x), y=y)) + geom_point() 

and then reverse the x axis

p + scale_x_reverse()

in this way your data is not altered, but you can scale the graph

Joaquín L
  • 142
  • 12
  • 1
    I like this answer and it works. Instead of `scale_x_reverse()` one could also write `-log10(x)` above. – Tapper Nov 02 '21 at 23:20
  • 1
    Thank you! I think the solution `-log10(x)` is good, but have to be careful because you end up with the X-axis in negative values. – Joaquín L Nov 04 '21 at 00:17