[Numpy-discussion] step paramter for linspace

Nathaniel Smith njs at pobox.com
Fri Mar 1 08:34:48 EST 2013


On Fri, Mar 1, 2013 at 12:33 PM, Henry Gomersall <heng at cantab.net> wrote:
> On Fri, 2013-03-01 at 13:25 +0100, Sebastian Berg wrote:
>> there has been a request on the issue tracker for a step parameter to
>> linspace. This is of course tricky with the imprecision of floating
>> point numbers.
>
> How is that different to arange? Either you specify the number of points
> with linspace, or you specify the step with arange. Is there a third
> option?

arange is designed for ints and gives you a half-open interval,
linspace is designed for floats and gives you a closed interval. This
means that when arange is used on floats, it does weird things that
linspace doesn't:

In [11]: eps = np.finfo(float).eps

In [12]: np.arange(0, 1, step=0.2)
Out[12]: array([ 0. ,  0.2,  0.4,  0.6,  0.8])

In [13]: np.arange(0, 1 + eps, step=0.2)
Out[13]: array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])

In [14]: np.linspace(0, 1, 6)
Out[14]: array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])

In [15]: np.linspace(0, 1 + eps, 6)
Out[15]: array([ 0. ,  0.2,  0.4,  0.6,  0.8,  1. ])

The half-open/closed thing also has effects on what kind of api is
reasonable. arange(0, 1, step=0.8) makes perfect sense (it acts like
python range(0, 10, step=8)). linspace(0, 1, step=0.8) is just
incoherent, though, because linspace guarantees that both the start
and end points are included.

> My usual hack to deal with the numerical bounds issue is to add/subtract
> half the step.

Right. Which is exactly the sort of annoying, content-free code that a
library is supposed to handle for you, so you can save mental energy
for more important things :-).

The problem is to figure out exactly how strict we should be. Like,
presumably linspace(0, 1, step=0.8) should fail, rather than round 0.8
to 0.5 or 1. That would clearly violate "in the face of ambiguity,
refuse the temptation to guess".

OTOH, as Sebastian points out, requiring that the step be *exactly* a
divisor of the value (stop - start), within 1 ULP, is probably
obnoxious.

Would anything bad happen if we just required that, say, (stop -
start)/step had to be within "np.allclose" of an integer, i.e., to
some reasonable relative and absolute precision, and then rounded the
number of steps to match that integer exactly?

-n



More information about the NumPy-Discussion mailing list