28
votes

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

7
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
Correct -- that wasn't clear. I've edited now. - kmm
@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

7 Answers

45
votes

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!

29
votes

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

13
votes
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

9
votes

Riffing off of Tom's answer above, the following removes + signs, and handles 0 better (the function is anonymously inlined as well):

scale_y_continuous(label= function(x) {ifelse(x==0, "0", parse(text=gsub("[+]", "", gsub("e", " %*% 10^", scientific_format()(x)))))} ) +
6
votes

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()
3
votes

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

1
votes

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