33

With the new version ggplot2 and scales, I can't figure out how to get axis label in scientific notation. For example:

x <- 1:4
y <- c(0, 0.0001, 0.0002, 0.0003)

dd <- data.frame(x, y)

ggplot(dd, aes(x, y)) + geom_point()

gives me

Example ggplot with scales

I'd like the axis labels to be 0, 5 x 10^-5, 1 x 10^-4, 1.5 x 10^-4, etc. I can't figure out the correct combination of scale_y_continuous() and math_format() (at least I think those are what I need).

scale_y_log10() log transforms the axis, which I don't want. scale_y_continuous(label = math_format()) just gives me 10^0, 10^5e-5, etc. I see why the latter gives that result, but it's not what I'm looking for.

I am using ggplot2_0.9.1 and scales_0.2.1

kmm
  • 6,045
  • 7
  • 43
  • 53
  • I'm confused; those values (0, 5^-5, 1^-4, 1.5^-4) don't really match up with the data ranges in your plot. – joran May 25 '12 at 23:09
  • Correct -- that wasn't clear. I've edited now. – kmm May 25 '12 at 23:12
  • Possible duplicate of http://stackoverflow.com/questions/9651903/ggplot2-y-axis-ticks-not-showing-up-on-a-log-scale ? – Ben Bolker May 25 '12 at 23:34
  • @BenBolker I don't think that this is really a duplicate of the one you link to, in that that question was about a logarithmic scale and labels formatted as a base to a power (such that the powers are then linearly increasing). This is about labels on a linear scale in scientific notation. – Brian Diggs May 26 '12 at 00:15

7 Answers7

51

I adapted Brian's answer and I think I got what you're after.

Simply by adding a parse() to the scientific_10() function (and changing 'x' to the correct 'times' symbol), you end up with this:

x <- 1:4
y <- c(0, 0.0001, 0.0002, 0.0003)

dd <- data.frame(x, y)

scientific_10 <- function(x) {
  parse(text=gsub("e", " %*% 10^", scales::scientific_format()(x)))
}

ggplot(dd, aes(x, y)) + geom_point()+scale_y_continuous(label=scientific_10)

enter image description here

You might still want to smarten up the function so it deals with 0 a little more elegantly, but I think that's it!

Tom Constant
  • 526
  • 5
  • 7
  • 1
    Thank you for this answer. This solution works but it prints out a number like 1e+03 as 10^+03. Is there a way to have it print out as 10^3 instead? – Berk U. Jan 28 '16 at 23:59
  • 1
    @BerkU. Yes. Use `scientific_10 <- function(x) { parse(text=gsub("e\\+*", " %*% 10^", scales::scientific_format()(x))) }` to ignore the "+" symbol. – Chang Ye Aug 12 '20 at 12:50
  • The [answer below](https://stackoverflow.com/a/18526649/2295964) looks more canonical than this accepted one! – Yan Foto Oct 29 '21 at 09:38
  • @YanFoto No it's not. The answer below is in log10 format, the question is not supposed that. – trilisser Dec 20 '22 at 02:48
37

As per the comments on the accepted solution, OP is looking to format exponents as exponents. This can be done with the trans_format and trans_breaks functions in the scales package:

    library(ggplot2)
    library(scales)

    x <- 1:4
    y <- c(0, 0.0001, 0.0002, 0.0003)
    dd <- data.frame(x, y)

    ggplot(dd, aes(x, y)) + geom_point() +
    scale_y_log10("y",
        breaks = trans_breaks("log10", function(x) 10^x),
        labels = trans_format("log10", math_format(10^.x)))

enter image description here

zx8754
  • 52,746
  • 12
  • 114
  • 209
computermacgyver
  • 802
  • 7
  • 15
  • Thanks for this answer. I tried it but I got the plot upside down. https://stackoverflow.com/questions/46639120/ggplot2-transform-y-axis-in-bar-plot-using-scale-y-log10. I will appreciate your suggestions. – shiny Oct 09 '17 at 07:57
  • @aelwan: I'll answer in the other thread, but a logarithmic scale does not really work with a barplot because there can be no zero mark. I would consider just use geom_errorbar() and not geom_bar() . – computermacgyver Oct 10 '17 at 10:16
13
scale_y_continuous(label=scientific_format())

gives labels with e instead of 10:

enter image description here

I suppose if you really want 10's in there, you could then wrap that in another function.

scientific_10 <- function(x) {
  gsub("e", " x 10^", scientific_format()(x))
}

ggplot(dd, aes(x, y)) + geom_point() + 
  scale_y_continuous(label=scientific_10)

enter image description here

Brian Diggs
  • 57,757
  • 13
  • 166
  • 188
  • I was trying to figure out a way to combine this with the strategy in `math_format()` to get the exponents formatted properly, but it was getting complicated. – joran May 25 '12 at 23:34
  • Is it possible to get the exponents as actual exponents? Otherwise, this is what I'm looking for. – kmm May 26 '12 at 00:13
  • @Kevin It might be. The general approach would be to make the labels plotmath expressions. I don't know if you can return expressions for a labels function or not. I tried a few things, but couldn't get anything to work easily. Hopefully someone else can chime in. – Brian Diggs May 26 '12 at 00:36
  • @joran I've added a solution to show how to format the exponents as you wanted (I know this is quite old, however). – computermacgyver Aug 30 '13 at 06:50
11

Riffing off of Tom's answer above, the following removes + signs, and handles 0 better:

scientific_10 = function(x) {
  ifelse(
    x==0, "0",
    parse(text = sub("e[+]?", " %*% 10^", scientific_format()(x)))
  )
} 

## used as
scale_y_continuous(label = scientific_10)
Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
sherifffruitfly
  • 415
  • 5
  • 14
6

I wrote a version of scientific_10 that avoids the scales package; it also removes leading zeroes in exponents (10^04 to 10^4, etc.). This was adapted from the helpful answers given above.

I've also included wrapper scale functions below.

scientific_10 <- function(x) {
    xout <- gsub("1e", "10^{", format(x),fixed=TRUE)
    xout <- gsub("{-0", "{-", xout,fixed=TRUE)
    xout <- gsub("{+", "{", xout,fixed=TRUE)
    xout <- gsub("{0", "{", xout,fixed=TRUE)
    xout <- paste(xout,"}",sep="")
    return(parse(text=xout))
}

scale_x_log10nice <- function(name=NULL,omag=seq(-10,20),...) {
    breaks10 <- 10^omag
    scale_x_log10(name,breaks=breaks10,labels=scientific_10(breaks10),...)
}

scale_y_log10nice <- function(name=NULL,omag=seq(-10,20),...) {
    breaks10 <- 10^omag
    scale_y_log10(name,breaks=breaks10,labels=scientific_10(breaks10),...)
}

scale_loglog <- function(...) {
    list(scale_x_log10nice(...),scale_y_log10nice(...))
}

qplot(x=exp(5*rnorm(100)),geom="density",kernel="rectangular") + 
    scale_x_log10nice()
ewallace
  • 213
  • 3
  • 5
3

I think this became really easy using the great ggtext-package. What I did was:

library(ggplot)
library(ggtext)
ggplot(mtcars, aes(x = log10(mpg), y = wt)) +
  geom_point() +
  scale_x_continuous(labels = function(x){return(paste0("10^", x))}) +
  theme(
    axis.text.x = element_markdown()
  )

enter image description here

Lenn
  • 1,283
  • 7
  • 20
1

Merging the previous answer I've created a function that can get arbitrary power ot tens for x and y as a multiply factor and then create the plot adding the factor near the axes

enter image description here

library(ggplot2)
x <- seq(1,25)
y <- rnorm(25)/10000
dd <- data.frame(x, y)

trim10 <- function(x) {
  parse(text=gsub(".*e", "x10^", scales::scientific_format()(x)))
}


ggplot_trim10 <- function(x,y,xfac,yfac,...){
        
    xnew    <- x*xfac 
    ynew    <- y*yfac 
    
    dd      <- data.frame(xnew,ynew)
    
    xunit   <- abs(max(xnew) - min(xnew))
    yunit   <- abs(max(ynew) - min(ynew))
    
    x_min_label <- min(xnew) - xunit*0.1
    x_max_label <- max(xnew) - xunit*0.1
    y_min_label <- min(ynew) - yunit*0.1
    y_max_label <- max(ynew) - yunit*0.1
    
    ggplot(data=dd, aes(x=xnew, y=ynew),...) +
    geom_line() +
    annotate("text", x = x_max_label, y = y_min_label, label = trim10(xfac)) +
    annotate("text", x = x_min_label, y = y_max_label, label = trim10(yfac)) +
    coord_cartesian(xlim = c(min(xnew), max(xnew)),ylim = c(min(ynew),max(ynew)), clip = "off")
    }

ggplot_trim10(x,y,10,10)

as a note I know that "x" is not the correct symbol but I was getting a bit crazy mixing expression, paste etc. if anyone will fix it it would be great

Jojostack
  • 181
  • 9