Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Recently I was comparing implementation of sequence generation functions in R, Python (numpy) and Julia. Interestingly even such basic functions have slight differences in implementation. In my opinion Julia provides the best solution and Python the worst.

The test case will be a 6 element sequence from 0.1 to 1.1 namely [0.1, 0.3, 0.5, 0.7, 0.9, 1.1]. In all languages we can generate it in three ways: by direct specification, by sequence giving its length and by sequence giving its step (there actually three flavors here: with exact and inexact right bound).

The test codes follow but let me first give the conclusions:

R:

• seq will generate the same sequence in all cases (even if to parameter is a bit too small – see seq help for details about ‘just beyond’, but then the last value in the sequence is corrected);
• the sequence is not guaranteed to be equal to manually entered literals that represent the sequence most exactly;
• if you generate the same sequence in the reversed order the result does not have to be equal.

Python:

• it matters if we generate sequence using linspace or arange;
• arange excludes right end of the range specification; this actually can result in unexpected results; check numpy.arange(0.2, 0.6, 0.4) vs numpy.arange(0.2, 1.6, 1.4);
• the sequence is not guaranteed to be equal to manually entered literals that represent the sequence most exactly;
• if you generate the same sequence in the reversed order the result does not have to be equal.

Julia:

• it may be important if we supply exact value of the last element of the sequence or it is inexact when we specify step (equivalent of by in R), check out function colon in range.jl;
• sequence generated by step and by number of elements can differ even if they have the same endpoints;
• Julia actually is able to generate sequence equal to sequence that is equal to manually entered literals (sequence using step with exact right bound);
• generating the sequence in the reversed order gives you the same result.

Below you can check the codes I used. Here are the results for R:

x <- c(0.1, 0.3, 0.5, 0.7, 0.9, 1.1)
y1 <- seq(from = 0.1, to = 1.1, length.out = 6)
y2 <- seq(from = 0.1, to = 1.1, by = 0.2)
y3 <- seq(from = 0.1, to = 1.2, by = 0.2)
y4 <- seq(from = 0.1, to = 1.099999999999, by = 0.2)

print(x == y1)
#   TRUE FALSE  TRUE FALSE  TRUE  TRUE

print(x == y2)
#   TRUE FALSE  TRUE FALSE  TRUE  TRUE

print(y1 == y2)
#  TRUE TRUE TRUE TRUE TRUE TRUE

print(y2 == y3)
#  TRUE TRUE TRUE TRUE TRUE TRUE

print(y2 == y4)
#   TRUE  TRUE  TRUE  TRUE  TRUE FALSE

print(rev(seq(1.1,0.1, len=6)) == seq(0.1, 1.1, len=6))
#   TRUE  TRUE  TRUE  TRUE FALSE  TRUE

Now comes Python:

import numpy

x = numpy.array([0.1, 0.3, 0.5, 0.7, 0.9, 1.1])
y1 = numpy.linspace(0.1, 1.1, 6)
y2 = numpy.arange(0.1, 1.1, 0.2)
y3 = numpy.arange(0.1, 1.11, 0.2)

print(x == y1)
# [ True False  True False  True  True]

print(y2)
# [ 0.1  0.3  0.5  0.7  0.9]

print(x == y3)
# [ True False False False False False]

print(y1 == y3)
# [ True  True False  True False False]

print(list(reversed(numpy.linspace(1.1, 0.1, 6))) == y1)
# [ True  True  True  True False  True]
And finally Julia:
x = [0.1, 0.3, 0.5, 0.7, 0.9, 1.1]
y1 = linspace(0.1, 1.1, 6)
y2 = [0.1:0.2:1.1]
y3 = [0.1:0.2:nextfloat(1.1)]
y4 = [0.1:0.2:prevfloat(1.1)]

println(x .== y1)
# Bool[true,false,true,false,false,true]

println(x .== y2)
# Bool[true,true,true,true,true,true]

println(y1 .== y3)
# Bool[true,true,true,true,false,true]

println(y2 .== y3)
# Bool[true,false,true,false,true,true]

println(y4)
# [0.1,0.30000000000000004,0.5,0.7000000000000001,0.9]

println(linspace(0.1, 1.1, 6) .== reverse(linspace(1.1, 0.1, 6)))
# Bool[true,true,true,true,true,true]