Menu Home

R Tip: Use seq_len() to Avoid The Backwards Sequence Trap

Another R tip. Use seq_len() to avoid the backwards sequence trap.

Many R users use the “colon sequence” notation to build sequences. For example:

for(i in 1:5) {
  print(paste(i, i*i))
}
#> [1] "1 1"
#> [1] "2 4"
#> [1] "3 9"
#> [1] "4 16"
#> [1] "5 25"

However, the colon notation can be unsafe as it does not properly handle the empty sequence case:

n <- 0

1:n
#> [1] 1 0

Notice the above example built a reversed sequence, instead of an empty sequence.

This leads to the backwards sequence trap: writing code of the form “1:length(x)” is often wrong. For example “for(i in 1:length(x)) { statements involving x[[i]] }“, which will fail for length-zero x.

To avoid this use seq_len() or seq_along():

seq_len(5)
#> [1] 1 2 3 4 5

n <- 0
seq_len(n)
#> integer(0)

integer(0)” is a length zero sequence of integers (not a sequence containing the value zero).

Categories: Coding Tutorials

Tagged as:

jmount

Data Scientist and trainer at Win Vector LLC. One of the authors of Practical Data Science with R.

8 replies

  1. I may be missing something, but I don’t understand why you call the result of 1:0 a bug. That produces exactly what I would expect it to.

    The “:” operator can be thought of as shorthand for a call to the seq() function. This shorthand only accepts 2 of the arguments that seq() accepts—“from” and “to”—default values are used for the others. Note that seq(1,0) returns the same result that 1:0 does. To get an empty string, just issue seq(length.out=0).

    Two R results that I mentioned:

    seq(1,0)
    [1] 1 0

    seq(length.out=0)
    integer(0)

    1. Thanks for your comment. I left a bit out which I have now put at the end of the article with an edit. Sorry about that.

      The behavior does match help(`:`), so it isn’t a bug on its own. But it is a key part of buggy for-loops such as this:

      From https://www.r-bloggers.com/writing-a-for-loop-in-r/:

       for (i in 1:length(foo)) {
        #stuff to do the number of times that foo is long
       }
      

      The above errors out if foo has length 0. And the above form is unfortunately taught a lot. The fix I suggest is use seq_len(). Yes, one can use vapply() in this case.

      I wish I had said it in the original article, but code of the form 1:length(x) is a very common (so common I forgot I had not mentioned it), and this the use of “:” and is often wrong (not what the programmer actually intended).

    2. Agree that bug in the headline is misleading. Maybe replace “bug” with “situation”.

  2. for(i in 1:5) {
    + print(paste(i, i*i))
    + }

    produces-
    [1] “1 1”
    [1] “2 4”
    [1] “3 9”
    [1] “4 16”
    [1] “5 25”

    omitting the last line makes me worry that you wrote this in python :)

  3. Turns out we are all in good company:

    Brian D. Ripley: Add to package utils in R-devel, after correction. I was surprised you had fallen into the 1:0 trap.
    Patrick Burns: I’m surprised too – good catch.
    —Brian D. Ripley and Patrick Burns (after adding Patrick Burns’ head() to the utils package) R-devel (January 2004)

    (Quoted from the fortunes package.)

%d