Jack Armstrong
Jack Armstrong

Reputation: 1249

Superscripts within ggplot2's axis text

I would like to create a graph that has superscripts on the axis instead of displaying unformatted numbers using ggplot2. I know that there are a lot of answers which change the axis label, but not the axis text. I am not trying to change the label of the graph, but the text on the axis.

Example:

x<-c('2^-5','2^-3','2^-1','2^1','2^2','2^3','2^5','2^7','2^9','2^11','2^13')
y<-c('2^-5','2^-3','2^-1','2^1','2^2','2^3','2^5','2^7','2^9','2^11','2^13')
df<-data.frame(x,y)
p<-ggplot()+
  geom_point(data=df,aes(x=x,y=y),size=4)
p

So I would like the x-axis to display the same numbers but without the carrot.

enter image description here

Upvotes: 4

Views: 1820

Answers (3)

eipi10
eipi10

Reputation: 93761

You can provide a function to the labels argument of the scale_x_*** and scale_y_*** functions to generate labels with superscripts (or other formatting). See examples below.

library(jrnoldmisc)
library(ggplot2)

df<-data.frame(x=2^seq(-5,5,2),
               y=2^seq(-5,5,2))

ggplot(df) +
  geom_point(aes(x=x,y=y),size=2)  +
  scale_x_log2(breaks=2^seq(-5,5,2),
               labels=function(x) parse(text=paste("2^",round(log2(x),2))))

enter image description here

ggplot(df) +
  geom_point(aes(x=x,y=y),size=2)  +
  scale_x_continuous(breaks=c(2^-5, 2^seq(1,5,2)),
                     labels=function(x) parse(text=paste("2^",round(log2(x),2))))

enter image description here

ggplot(df) +
  geom_point(aes(x=x,y=y),size=2)  +
  scale_x_log10(breaks=10^seq(-1,1,1),
                labels=function(x) parse(text=paste("10^",round(log10(x),2))))

enter image description here

Upvotes: 3

Rui Barradas
Rui Barradas

Reputation: 76402

This can be done with functions scale_x_log2 and scale_y_log2 that can be found in GitHub package jrnoldmisc.

First, install the package.

devtools::install_github("jrnold/rubbish")

Then, coerce the variables to numeric. I wil work with a copy of the original dataframe.

df1 <- df
df1[] <- lapply(df1, function(x){
  x <- as.character(x)
  sapply(x, function(.x)eval(parse(text = .x)))
})

Now, graph it.

library(jrnoldmisc)
library(ggplot2)
library(MASS)
library(scales)

a <- ggplot(df1, aes(x = x, y = y, size = 4)) + 
  geom_point(show.legend = FALSE) +
  scale_x_log2(limits = c(0.01, NA), 
                labels = trans_format("log2", math_format(2^.x)),
                breaks = trans_breaks("log2", function(x) 2^x, n = 10)) +
  scale_y_log2(limits = c(0.01, NA),
                labels = trans_format("log2", math_format(2^.x)),
                breaks = trans_breaks("log2", function(x) 2^x, n = 10))
a + annotation_logticks(base = 2)

enter image description here

Edit.

Following the discussion in the comments, here are the two other ways that were seen to give different axis labels.

  1. Axis labels every tick mark. Set limits = c(1.01, NA) and function argument n = 11, an odd number.
  2. Axis labels on odd number exponents. Keep limits = c(0.01, NA), change to function(x) 2^(x - 1), n = 11.

Just the instructions, no plots.

The first.

a <- ggplot(df1, aes(x = x, y = y, size = 4)) + 
  geom_point(show.legend = FALSE) +
  scale_x_log2(limits = c(1.01, NA), 
                labels = trans_format("log2", math_format(2^.x)),
                breaks = trans_breaks("log2", function(x) 2^(x), n = 11)) +
  scale_y_log2(limits = c(1.01, NA),
                labels = trans_format("log2", math_format(2^.x)),
                breaks = trans_breaks("log2", function(x) 2^(x), n = 11))
a + annotation_logticks(base = 2)

And the second.

a <- ggplot(df1, aes(x = x, y = y, size = 4)) + 
  geom_point(show.legend = FALSE) +
  scale_x_log2(limits = c(0.01, NA), 
               labels = trans_format("log2", math_format(2^.x)),
               breaks = trans_breaks("log2", function(x) 2^(x - 1), n = 11)) +
  scale_y_log2(limits = c(0.01, NA),
               labels = trans_format("log2", math_format(2^.x)),
               breaks = trans_breaks("log2", function(x) 2^(x - 1), n = 11))
a + annotation_logticks(base = 2)

Upvotes: 2

NelsonGon
NelsonGon

Reputation: 13309

EDIT: A purely base approach:

df %>% 
  mutate_all(as.character)->new_df
res<-unlist(Map(function(x) eval(parse(text=x)),new_df$x))#replace with y for y
to_use<-unlist(lapply(res,as.expression))
split_text<-strsplit(gsub("\\^"," ",names(to_use))," ")
join_1<-as.numeric(sapply(split_text,"[[",1)) #tidyr::separate might help, less robust for numeric(I think)
join_2<-as.numeric(sapply(split_text,"[[",2))
to_use_1<-sapply(seq_along(join_1),function(x) parse(text=paste(join_1[x],"^",
                                                join_2[x])))

The above can be reduced to less step, I posted the stepwise approach I took. The result for only x, the same can be done for y:

new_df %>%   
ggplot()+
  geom_point(aes(x=x,y=y),size=4)+
 scale_x_discrete(breaks=df$x,labels=to_use_1)#replace with y and scale_y_discrete for y

Plot: enter image description here

Original and erroneous answer:

I have deviated from standard tidyverse practice by using $, you can replace it with . and it might work although in this case it's not really important since the focus is on labels.:

    library(dplyr)
   df %>% 
          mutate(new_x=gsub("\\^"," ",x),
                 new_y=gsub("\\^"," ",y))->new_df
        new_df %>%   
        ggplot()+
          geom_point(aes(x=x,y=y),size=4)+
         scale_x_discrete(breaks=x,labels=new_df$new_x)+
          scale_y_discrete(breaks=y,labels=new_df$new_y)

Upvotes: 3

Related Questions