26

In order to set the custom break intervals for a log scale in a ggplot2 chart, I created the following vector from multiple sequences.

breaks <- c(seq(2000, 10000, by = 1000),
        seq(20000, 100000, by = 10000),
        seq (200000, 1000000, by = 100000),
        seq (2000000,10000000, by = 1000000),
        seq (20000000,100000000, by = 10000000))

This is quick and dirty but it gives me the desired breaks from 2,000 to 100,000,000 in log intervals.

> breaks
 [1] 2e+03 3e+03 4e+03 5e+03 6e+03 7e+03 8e+03 9e+03 1e+04 2e+04 3e+04 4e+04
[13] 5e+04 6e+04 7e+04 8e+04 9e+04 1e+05 2e+05 3e+05 4e+05 5e+05 6e+05 7e+05
[25] 8e+05 9e+05 1e+06 2e+06 3e+06 4e+06 5e+06 6e+06 7e+06 8e+06 9e+06 1e+07
[37] 2e+07 3e+07 4e+07 5e+07 6e+07 7e+07 8e+07 9e+07 1e+08

How do I clean this snippet of code up to make it more flexible? A search of the many log sequence solutions, existing libraries and my own trial and error was not very fruitful.

Ideally, I would like to input From, To and NumberOfMinorIntervals as parameters into a simpler expression.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Look Left
  • 1,305
  • 3
  • 15
  • 20

5 Answers5

67

I like this one:

c(2:10 %o% 10^(3:7))
flodel
  • 87,577
  • 21
  • 185
  • 223
  • 2
    Now that is short! I amended the first sequence to include the number of breaks: `c(seq(2,10,10/10) %o% 10^(3:7))` or something like this eventually... `c(seq(2,10, 10/NumBreaks) %o% 10^(From:To))` – Look Left May 28 '14 at 06:26
  • 1
    Just stumbled across this, it needs massive upvoting! – alexwhan May 25 '15 at 05:19
  • How does this work? What is this fancy `%o%` operator? – ivan-k Jun 29 '15 at 17:38
  • 3
    `%o%` is outer binary product, as indicated here: http://cran.r-project.org/doc/manuals/R-lang.html#Operators – ivan-k Jun 29 '15 at 17:51
25

There is also a lseq() function in library("emdbook"), that I just copy/paste into my code for log-ticks: http://artax.karlin.mff.cuni.cz/r-help/library/emdbook/html/lseq.html

I've written my own function to use it thusly:

lseq <- function(from=1, to=100000, length.out=6) {
  # logarithmic spaced sequence
  # blatantly stolen from library("emdbook"), because need only this
  exp(seq(log(from), log(to), length.out = length.out))
}

Running lseq() then returns 1 10 100 1000 10000 100000

Christoph Safferling
  • 1,116
  • 11
  • 10
  • Thanks! This is more self-explanatory than the current most-upvoted answer. Also, that answer more precisely recreates the asker's example, but this solution is more elegant, since each element of the sequence is the previous element times a constant. – Brad Knox Sep 23 '20 at 19:47
12

@flodel is close, but (as @look-left notes) since initial sequence is not log you get a patterned distribution:

plot(c(2:10 %o% 10^(3:7)), log = 'y')

enter image description here

Instead just seq the exponent:

plot(10^(seq(1,10,.2)), log = 'y')

enter image description here

geotheory
  • 22,624
  • 29
  • 119
  • 196
1

Not sure if this is general enough for you, but this function should at least be a start

logbreaks<-function(from=3, to=7, m=10) {
    unlist(lapply(3:7, function(x) seq(2*10^x, 10*10^x, length.out=m-1)), )
}

#generate your data
logbreaks(3,7,10)

The to/from parameters as exponents of 10. And the number of minor breaks is one more than the actual length of the break vector.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • I should have been slightly clearer. I needed to specify the Number Of MinorI ntervals between each Major. But the idea gives me something to play with and learn from. Thanks. – Look Left May 28 '14 at 06:36
1

This one automatically calculates the length.out and also supports by to determine the incrementation somewhat analog to seq()

The answer combines the solution of @Christoph Safferling and https://stackoverflow.com/a/5237650/2480079

lseqBy <- function(from=1, to=100000, by=1, length.out=log10(to/from)+1) {
  tmp <- exp(seq(log(from), log(to), length.out = length.out))
  tmp[seq(1, length(tmp), by)]  
}

Examples:

> lseqBy(from = 10, to = 100000, by = 1)
[1] 1e+01 1e+02 1e+03 1e+04 1e+05
> lseqBy(from = 10, to = 100000, by = 2)
[1] 1e+01 1e+03 1e+05
> lseqBy(from = 10, to = 100000, by = 3)
[1]    10 10000
Community
  • 1
  • 1
user4786271
  • 1,555
  • 13
  • 18