4
$\begingroup$

I am trying to rewrite some MATLAB/Octave code in Python, and I don't know what would be the nicest or most intuitive way of writing

octave:10> dt = 0.1;
octave:12> T = 0:dt:1
T =

    0.00000    0.10000    0.20000    0.30000    0.40000    0.50000    0.60000    0.70000    0.80000    0.90000    1.00000
octave:15> dt = 0.17;
octave:16> T = 0:dt:1
T =

    0.00000    0.17000    0.34000    0.51000    0.68000    0.85000

which creates a discretization of the interval [0, 1] with step 0.1, as it's seen. I referred to the NumPy/MATLAB mathesaurus and it uses arange function, but it's not suitable for non-integer values as it's stated in the documentation and shown in this SO question. On the other hand, playing with linspace is not appealing to me because it takes care of endpoints, not spacing.

Which would be a straight-forward, one-line way of doing this in Python?

$\endgroup$
3
  • $\begingroup$ Relevant document: mathworks.es/support/solutions/en/data/1-4FLI96/… it's published on April's Fools though... :P $\endgroup$
    – user782
    Commented Jun 8, 2012 at 11:54
  • $\begingroup$ Previous link exists no more, here goes Google cache copy webcache.googleusercontent.com/… $\endgroup$
    – user782
    Commented Apr 15, 2013 at 10:14
  • $\begingroup$ Also typing in MATLAB doc colon and help colon gives further insight. This is a tricky matter, as already noted by Walking Randomly walkingrandomly.com/?p=789 $\endgroup$
    – user782
    Commented Apr 15, 2013 at 10:18

1 Answer 1

3
$\begingroup$

MATLAB and Octave are susceptible to the same subtle floating-point issues that Python is, where you can get a slightly unexpected result if you do not anticipate rounding issues. On the other hand, this is quite convenient syntax to have! You can easily "bake" a simple function to do this from the existing functionality in numpy.linspace, which is a very Pythonic way to do things!

# use Python3 syntax in Python2 to get clean, consistent integer division  
from __future__ import division   
import numpy as np

def lrange(r1, inc, r2):
    """Provide spacing as an input and receive samples back by wrapping `numpy.linspace`"""
    n = ((r2-r1)+2*np.spacing(r2-r1))//inc
    return np.linspace(r1,r1+inc*n,n+1)

> lrange(0,0.1,1)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9,  1. ])

> lrange(0,0.17,1)
array([ 0.  ,  0.17,  0.34,  0.51,  0.68,  0.85])

Edit: I've added a floating point epsilon to the computation that will handle small floating-point errors in the operands/representation per Juanlu001's comment.

$\endgroup$
6
  • $\begingroup$ Ah, didn't know about the existence of the integer division operator! In fact you do not need the __future__ import statement. Thank you! $\endgroup$
    – user782
    Commented May 25, 2012 at 17:41
  • $\begingroup$ You're absolutely right, an earlier version of the function used "true division". I'm going to leave the import statement in there because it's a reasonably important piece of the language to know about, even if it's not used here. There are more details on division in Python in PEP 238. $\endgroup$ Commented May 26, 2012 at 7:26
  • $\begingroup$ Well, after some days I went back to this and discovered that lrange(0, 0.1, 1) is actually [0., ..., 0.8, 0.9]. This is because 0.1 is actually 0.100000000000000006. Here and there is mentioned that there are roundoff errors and the like, but then I don't know how it is implemented in MATLAB / Octave. $\endgroup$
    – user782
    Commented Jun 8, 2012 at 11:46
  • $\begingroup$ --I mean, at least in my computer. $\endgroup$
    – user782
    Commented Jun 8, 2012 at 12:04
  • $\begingroup$ @Juanlu001 - That's annoying, I think the only fix would be to do something like: ((r2-r1)+np.spacing(r2-r1))//inc $\endgroup$ Commented Jun 8, 2012 at 13:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.