How to get a reversed, log10 scale in ggplot2?
Asked Answered
D

5

56

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?

Doting answered 15/6, 2012 at 15:37 Comment(1)
I expected this to work as well, but apparently its a bit complicated. There was a work-around 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.Waterworks
B
67

[See @user236321's answer for a more modern (post April 2022) answer.]

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

Burtburta answered 15/6, 2012 at 16:32 Comment(6)
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 :(Spelt
@Spelt 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.Burtburta
That easy huh?! Thank you very much!!Spelt
Alternatively use the :: notation to explicitly call functions from the scales package. --- reverselog_trans <- function(base = exp(1)) { trans <- function(x) -log(x, base) inv <- function(x) base^(-x) scales::trans_new(paste0("reverselog-", format(base)), trans, inv, scales::log_breaks(base = base), domain = c(1e-100, Inf)) }Tabling
NB: the reverselog_trans function has appeared in CRAN cran.r-project.org/web/packages/ACSNMineR/index.html (found in ACSNMineR/R/plots.R).Hoad
@Hoad If that is exported from the package, you should put together an answer using that. It would be a good addition.Burtburta
E
14

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)

Enshrine answered 15/11, 2020 at 0:26 Comment(1)
The logticks are not correct. The ticks should be spaced further apart near each power of ten (1, 10, 100) and then get closer.Ossifrage
B
6

Just thought I put updated answer to this question that does not require writing your own transformation. As of scales version 1.2.0 (released in April 2022), transformation composition is handled by the scales package directly. Use scale_x_continuous(), with the trans argument as a vector with both log10 and reverse. You need to put log10 before reverse or you'll get an error; the transformations are applied in the order specified.

p + scale_x_continuous(trans = c("log10", "reverse"))

The documentation for scales::compose_trans even has this usage as an example.

Barreto answered 5/6, 2022 at 11:28 Comment(0)
L
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

Lipophilic answered 30/10, 2021 at 22:26 Comment(2)
I like this answer and it works. Instead of scale_x_reverse() one could also write -log10(x) above.Roughandtumble
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.Imperfect
O
0

I think scales::transform_compose may help you. This function can be used to combine multiple transformations.

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

p +   scale_x_continuous(trans = scales::transform_compose("log10","reverse"))

result pic

Oversleep answered 18/8 at 7:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.