`seq` takes a very long time with `by=1`

Viewed 41

I noticed something strange today, in some cases adding by=1 to seq function introduces a large inefficiency.

> system.time(seq(from=936144000, to=1135987200))
   user  system elapsed 
      0       0       0 
> system.time(seq(from=936144000, to=1135987200, by=1))
   user  system elapsed 
   4.42    8.39   18.20 

At first glance the results are equivalent:

> all.equal(seq(from=936144000, to=1135987200),
+           seq(from=936144000, to=1135987200, by=1))
[1] TRUE
 TRUE

The difference seems to be that omitting by=1 causes the result to be numeric, even if by is explicitly integer.

> identical(seq(from=936144000, to=1135987200),
+           seq(from=936144000, to=1135987200, by=1))
[1] FALSE
> class(seq(from=936144000, to=1135987200))
[1] "integer"
> class(seq(from=936144000, to=1135987200, by=1L))
[1] "numeric"

Also, calling directly to seq.int (assuming that is what happens behind the scenes in seq) also takes much longer than the seq without any arguments:

> system.time(seq.int(from=936144000, to=1135987200, by=1L))
   user  system elapsed 
   0.25    1.68    2.81 

How do I properly specify by to avoid the inefficiency or to get the efficiency of omitting by?

> sessionInfo()
R version 4.1.0 (2021-05-18)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_4.1.0    RUnit_0.4.32      tools_4.1.0       geneorama_1.7.3   data.table_1.14.0
1 Answers

You don't need to assume what happens behind the scenes, you can run debug(seq) and see what the difference is. It's a generic function, and it calls seq.default.

In seq.default it turns out that if the by argument is missing (and some other conditions which hold in your example), seq(from, to) does from:to. This is extremely fast, because it doesn't even allocate the full vector: in recent versions of R, it is stored in a special format with just the limits of the range.

The other thing you can see if you look at seq.default is that the only way to get this output is to have missing(by) be TRUE. So the answer to your question is that you can't specify by to get the same speed.

@Baraliuh's advice is good: if you want seq(from, to, by=1), use from:to instead.

Related