Logarithmic grid for plot with 'ggplot2'

11,474

Solution 1

To build on Gabor's answer, it is unnecessary to define the ticks only in the exact range of the plot. You can instead define breaks for a large range of values that will cover pretty much everything you'd ever expect to see and use those to create nice grid lines for any plot. While perhaps not the most elegant solution, it's easy and more generalizable than having to manually figure out ranges every time.

breaks <- 10^(-10:10)
minor_breaks <- rep(1:9, 21)*(10^rep(-10:10, each=9))

d %>% 
    ggplot(aes(x, y)) +
        geom_point() +
        scale_x_log10(breaks = breaks, minor_breaks = minor_breaks) +
        scale_y_log10(breaks = breaks, minor_breaks = minor_breaks) +
        annotation_logticks() +
        coord_equal() +
        theme_bw()

Link to image of plot

Solution 2

Are you looking for diminishing spacing grid like this?

ggplot(d, aes(x=x, y=y)) + geom_point() + 
  coord_trans(y="log10", x="log10") +
  scale_y_continuous(trans = log10_trans(),
                     breaks = trans_breaks("log10", function(x) 10^x),
                     labels = trans_format("log10", math_format(10^.x))) +
  scale_x_continuous(trans = log10_trans(),
                     breaks = trans_breaks("log10", function(x) 10^x),
                     labels = trans_format("log10", math_format(10^.x)))

enter image description here

Solution 3

For logarithmically spaced grids using ggplot2, the answer provided by Samehmagd shines for its simplicity, requires no manual settings, and it answers the question.

On using it on my own data, I discovered something that seems to be a bug. In my hands at least, this strategy fails when the values to be plotted are under 1 (thus generating negative values when log10 is applied).

This is the example, as contributed by Samehmagd, plus my contribution towards the end:

library(ggplot2)
library(scales)

set.seed(5)
x <- rlnorm(1000, meanlog=3.5, sdlog=1)
y <- rlnorm(1000, meanlog=4.0, sdlog=1)
d <- data.frame(x, y)

# peek at d
head(d)
#            x         y
# 1  14.284064  12.74253
# 2 132.205740 189.53295
# 3   9.435773  35.44751
# 4  35.521664  54.97449
# 5 183.358064  61.84004
# 6  18.121372  36.24753

# Plot successfully (this figure is identical to Samehmagd's)
ggplot(d, aes(x=x, y=y)) + geom_point() + coord_trans(y="log10", x="log10") + scale_y_continuous(trans=log10_trans(), breaks=trans_breaks("log10", function(x) 10^x), labels=trans_format("log10", math_format(10^.x))) + scale_x_continuous(trans=log10_trans(), breaks=trans_breaks("log10", function(x) 10^x), labels=trans_format("log10", math_format(10^.x)))

# Now, here is when it breaks
f <- 1/d

# peek at f
head(f)
#             x           y
# 1 0.070008087 0.078477335
# 2 0.007563968 0.005276127
# 3 0.105979655 0.028210728
# 4 0.028151834 0.018190255
# 5 0.005453810 0.016170753
# 6 0.055183459 0.027588083

# Get the plotting to fail just by using f instead of d, no other change.
ggplot(f, aes(x=x, y=y)) + geom_point() + coord_trans(y="log10", x="log10") + scale_y_continuous(trans=log10_trans(), breaks=trans_breaks("log10", function(x) 10^x), labels=trans_format("log10", math_format(10^.x))) + scale_x_continuous(trans=log10_trans(), breaks=trans_breaks("log10", function(x) 10^x), labels=trans_format("log10", math_format(10^.x)))
Error in if (zero_range(range)) { : missing value where TRUE/FALSE needed
In addition: Warning message:
In trans$transform(out$range) : NaNs produced

Whether or not this failure is a bug or a feature, it is well worth documenting.

Solution 4

You can define the breaks manually.

> ticks <- 2:10
> ooms <- 10^(0:3)
> breaks <- as.vector(ticks %o% ooms)
> breaks
 [1]     2     3     4     5     6     7     8     9    10
[10]    20    30    40    50    60    70    80    90   100
[19]   200   300   400   500   600   700   800   900  1000
[28]  2000  3000  4000  5000  6000  7000  8000  9000 10000

This snippet defines the breaks, hides some of them, and generates the plot.

library(ggplot2)

set.seed(5)
x <- rlnorm(1000, meanlog=3.5, sdlog=1)
y <- rlnorm(1000, meanlog=4.0, sdlog=1)
d <- data.frame(x, y)

ticks <- 2:10
# define the OOMs (orders of magnitudes)
ooms <- 10^(0:3)
breaks <- as.vector(ticks %o% ooms)

# select the labels to show
show.labels <- c(T, F, F, T, F, F, F, F, T)
labels <- as.character(breaks * show.labels)
labels <- gsub("^0$", "", labels)

p <- ggplot(d, aes(x=x, y=y)) + geom_point() +
  scale_x_log10(limits = c(1, NA), labels = labels, breaks = breaks) +
  scale_y_log10(limits = c(1, NA), labels = labels, breaks = breaks) +
  theme_bw() + theme(panel.grid.minor = element_line(color="blue", linetype="dotted"), panel.grid.major = element_line(color="blue", linetype="dotted")) +
  annotation_logticks(base = 10)
p

Scatterplot with logaritmic axes and custom ticks

Share:
11,474
pravi
Author by

pravi

Updated on July 17, 2022

Comments

  • pravi
    pravi almost 2 years

    I am trying to create a plot with logarithmically spaced grids using ggplot2 just like in the below figure. I get equidistant grids, but not log spaced ones. I know I am missing some parameter which I don't seem to get as of now. I have seen a lot of questions on the topic like Pretty ticks for log normal scale using ggplot2 (dynamic not manual), but do not solve the problem I am looking for.

    set.seed(5)
    x <- rlnorm(1000, meanlog=3.5, sdlog=1)
    y <- rlnorm(1000, meanlog=4.0, sdlog=1)
    d <- data.frame(x, y)
    
    plot(x, y, log="xy", las=1)
    grid(nx=NULL, ny=NULL, col= "blue", lty="dotted", equilogs=FALSE)
    library(magicaxis)
    magaxis(side=1:2, ratio=0.5, unlog=FALSE, labels=FALSE)
    

    enter image description here

    library(ggplot2)
    library(MASS)
    library(scales)
    a <- ggplot(d, aes(x=x, y=y)) + geom_point() +
         scale_x_log10(limits = c(1, NA), 
                       labels = trans_format("log10", math_format(10^.x)),
                       breaks=trans_breaks("log10", function(x) 10^x, n=4)) +
         scale_y_log10(limits = c(1, NA),
                       labels = trans_format("log10", math_format(10^.x)),
                       breaks=trans_breaks("log10", function(x) 10^x, n=4)) +
         theme_bw() + theme(panel.grid.minor = element_line(color="blue", linetype="dotted"), panel.grid.major = element_line(color="blue", linetype="dotted"))
    a + annotation_logticks(base = 10)
    

    enter image description here