4

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

NelsonGon
  • 13,015
  • 7
  • 27
  • 57
Jack Armstrong
  • 1,182
  • 4
  • 26
  • 59

3 Answers3

3

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)
NelsonGon
  • 13,015
  • 7
  • 27
  • 57
2

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)
Rui Barradas
  • 70,273
  • 8
  • 34
  • 66
  • Is it possible to have the tick marks on the odd numbers or every tick mark instead of only the even ones? – Jack Armstrong Jun 12 '19 at 16:34
  • @JackArmstrong Yes, it is, I have just tested `limits = c(1.01, NA)` on both `sclae_*_log2` calls and it now prints on every tick mark. – Rui Barradas Jun 12 '19 at 17:16
  • Instead of using limits, within breaks, = trans_breaks("log2", function(x) 2^(x-1), n = 10)) – Jack Armstrong Jun 12 '19 at 19:08
  • @JackArmstrong You are absolutely right, didn't think of that. But it will give the labels on odd exponents only, with me `limits` gives them on every tick mark. – Rui Barradas Jun 12 '19 at 19:16
  • @JackArmstrong OK, got it. I had changed argument `n` in `function(x) 2^(x), n = 11` to an odd number. Plus `limits = c(1.01, NA)` it gives labels on every tick mark. Should I edit the answer with this? – Rui Barradas Jun 12 '19 at 19:20
  • If you want to with other possible ways, sure. No need for pictures I think. People should be able to read. – Jack Armstrong Jun 12 '19 at 20:16
2

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

eipi10
  • 91,525
  • 24
  • 209
  • 285