Python sequence of decimal numbers

Rather than using a decimal step directly, it's much safer to express this in terms of how many points you want. Otherwise, floating-point rounding error is likely to give you a wrong result.

Use the linspace function from the NumPy library [which isn't part of the standard library but is relatively easy to obtain]. linspace takes a number of points to return, and also lets you specify whether or not to include the right endpoint:

>>> np.linspace[0,1,11]
array[[ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9,  1. ]]
>>> np.linspace[0,1,10,endpoint=False]
array[[ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9]]

If you really want to use a floating-point step value, use numpy.arange:

>>> import numpy as np
>>> np.arange[0.0, 1.0, 0.1]
array[[ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9]]

Floating-point rounding error will cause problems, though. Here's a simple case where rounding error causes arange to produce a length-4 array when it should only produce 3 numbers:

>>> numpy.arange[1, 1.3, 0.1]
array[[1. , 1.1, 1.2, 1.3]]

Mateen Ulhaq

22.2k16 gold badges86 silver badges127 bronze badges

answered Jan 25, 2009 at 12:26

Andrew JaffeAndrew Jaffe

25.8k4 gold badges49 silver badges59 bronze badges

8

range[] can only do integers, not floating point.

Use a list comprehension instead to obtain a list of steps:

[x * 0.1 for x in range[0, 10]]

More generally, a generator comprehension minimizes memory allocations:

xs = [x * 0.1 for x in range[0, 10]]
for x in xs:
    print[x]

Mateen Ulhaq

22.2k16 gold badges86 silver badges127 bronze badges

answered Jan 25, 2009 at 10:35

8

Building on 'xrange[[start], stop[, step]]', you can define a generator that accepts and produces any type you choose [stick to types supporting + and >> def drange[start, stop, step]: ... r = start ... while r < stop: ... yield r ... r += step ... >>> i0=drange[0.0, 1.0, 0.1] >>> ["%g" % x for x in i0] ['0', '0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1'] >>>

answered Jan 25, 2009 at 11:57

gimelgimel

80.2k10 gold badges74 silver badges104 bronze badges

6

Increase the magnitude of i for the loop and then reduce it when you need it.

for i * 100 in range[0, 100, 10]:
    print i / 100.0

EDIT: I honestly cannot remember why I thought that would work syntactically

for i in range[0, 11, 1]:
    print i / 10.0

That should have the desired output.

answered Jan 25, 2009 at 10:32

cmsjrcmsjr

54.4k10 gold badges70 silver badges62 bronze badges

3

NumPy is a bit overkill, I think.

[p/10 for p in range[0, 10]]
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]

Generally speaking, to do a step-by-1/x up to y you would do

x=100
y=2
[p/x for p in range[0, int[x*y]]]
[0.0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99]

[1/x produced less rounding noise when I tested].

answered Feb 28, 2017 at 2:06

KalleKalle

13.1k7 gold badges61 silver badges76 bronze badges

1

scipy has a built in function arange which generalizes Python's range[] constructor to satisfy your requirement of float handling.

from scipy import arange

answered May 17, 2014 at 20:50

1

Similar to R's seq function, this one returns a sequence in any order given the correct step value. The last value is equal to the stop value.

def seq[start, stop, step=1]:
    n = int[round[[stop - start]/float[step]]]
    if n > 1:
        return[[start + step*i for i in range[n+1]]]
    elif n == 1:
        return[[start]]
    else:
        return[[]]

Results

seq[1, 5, 0.5]

[1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]

seq[10, 0, -1]

[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

seq[10, 0, -2]

[10, 8, 6, 4, 2, 0]

seq[1, 1]

[ 1 ]

answered Jun 11, 2012 at 19:10

zeferinozeferino

5001 gold badge5 silver badges11 bronze badges

4

The range[] built-in function returns a sequence of integer values, I'm afraid, so you can't use it to do a decimal step.

I'd say just use a while loop:

i = 0.0
while i  1']
        self.start, self.stop, self.num = start, stop, num
        self.step = [stop-start]/[num-1]
    def __len__[self]:
        return self.num
    def __getitem__[self, i]:
        if isinstance[i, slice]:
            return [self[x] for x in range[*i.indices[len[self]]]]
        if i < 0:
            i = self.num + i
        if i >= self.num:
            raise IndexError['linspace object index out of range']
        if i == self.num-1:
            return self.stop
        return self.start + i*self.step
    def __repr__[self]:
        return '{}[{}, {}, {}]'.format[type[self].__name__,
                                       self.start, self.stop, self.num]
    def __eq__[self, other]:
        if not isinstance[other, linspace]:
            return False
        return [[self.start, self.stop, self.num] ==
                [other.start, other.stop, other.num]]
    def __ne__[self, other]:
        return not self==other
    def __hash__[self]:
        return hash[[type[self], self.start, self.stop, self.num]]

answered Mar 2, 2018 at 15:00

Chris_RandsChris_Rands

36.4k12 gold badges79 silver badges110 bronze badges

This is my solution to get ranges with float steps.
Using this function it's not necessary to import numpy, nor install it.
I'm pretty sure that it could be improved and optimized. Feel free to do it and post it here.

from __future__ import division
from math import log

def xfrange[start, stop, step]:

    old_start = start #backup this value

    digits = int[round[log[10000, 10]]]+1 #get number of digits
    magnitude = 10**digits
    stop = int[magnitude * stop] #convert from 
    step = int[magnitude * step] #0.1 to 10 [e.g.]

    if start == 0:
        start = 10**[digits-1]
    else:
        start = 10**[digits]*start

    data = []   #create array

    #calc number of iterations
    end_loop = int[[stop-start]//step]
    if old_start == 0:
        end_loop += 1

    acc = start

    for i in xrange[0, end_loop]:
        data.append[acc/magnitude]
        acc += step

    return data

print xfrange[1, 2.1, 0.1]
print xfrange[0, 1.1, 0.1]
print xfrange[-1, 0.1, 0.1]

The output is:

[1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1]
[-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0]

answered Dec 12, 2013 at 17:04

Carlos VegaCarlos Vega

1,3312 gold badges13 silver badges35 bronze badges

3

For completeness of boutique, a functional solution:

def frange[a,b,s]:
  return [] if s > 0 and a > b or s < 0 and a < b or s==0 else [a]+frange[a+s,b,s]

answered Sep 12, 2015 at 7:44

Bijou TrouvailleBijou Trouvaille

8,1264 gold badges38 silver badges42 bronze badges

You can use this function:

def frange[start,end,step]:
    return map[lambda x: x*step, range[int[start*1./step],int[end*1./step]]]

answered Jun 25, 2010 at 17:57

3

It can be done using Numpy library. arange[] function allows steps in float. But, it returns a numpy array which can be converted to list using tolist[] for our convenience.

for i in np.arange[0, 1, 0.1].tolist[]:
   print i

answered Feb 12, 2016 at 12:18

start and stop are inclusive rather than one or the other [usually stop is excluded] and without imports, and using generators

def rangef[start, stop, step, fround=5]:
    """
    Yields sequence of numbers from start [inclusive] to stop [inclusive]
    by step [increment] with rounding set to n digits.

    :param start: start of sequence
    :param stop: end of sequence
    :param step: int or float increment [e.g. 1 or 0.001]
    :param fround: float rounding, n decimal places
    :return:
    """
    try:
        i = 0
        while stop >= start and step > 0:
            if i==0:
                yield start
            elif start >= stop:
                yield stop
            elif start < stop:
                if start == 0:
                    yield 0
                if start != 0:
                    yield start
            i += 1
            start += step
            start = round[start, fround]
        else:
            pass
    except TypeError as e:
        yield "type-error[{}]".format[e]
    else:
        pass


# passing
print[list[rangef[-100.0,10.0,1]]]
print[list[rangef[-100,0,0.5]]]
print[list[rangef[-1,1,0.2]]]
print[list[rangef[-1,1,0.1]]]
print[list[rangef[-1,1,0.05]]]
print[list[rangef[-1,1,0.02]]]
print[list[rangef[-1,1,0.01]]]
print[list[rangef[-1,1,0.005]]]
# failing: type-error:
print[list[rangef["1","10","1"]]]
print[list[rangef[1,10,"1"]]]

Python 3.6.2 [v3.6.2:5fd33b5, Jul 8 2017, 04:57:36] [MSC v.1900 64 bit [AMD64]]

answered Mar 1, 2018 at 21:53

Goran B.Goran B.

5344 silver badges13 bronze badges

I know I'm late to the party here, but here's a trivial generator solution that's working in 3.6:

def floatRange[*args]:
    start, step = 0, 1
    if len[args] == 1:
        stop = args[0]
    elif len[args] == 2:
        start, stop = args[0], args[1]
    elif len[args] == 3:
        start, stop, step = args[0], args[1], args[2]
    else:
        raise TypeError["floatRange accepts 1, 2, or 3 arguments. [{0} given]".format[len[args]]]
    for num in start, step, stop:
        if not isinstance[num, [int, float]]:
            raise TypeError["floatRange only accepts float and integer arguments. [{0} : {1} given]".format[type[num], str[num]]]
    for x in range[int[[stop-start]/step]]:
        yield start + [x * step]
    return

then you can call it just like the original range[]... there's no error handling, but let me know if there is an error that can be reasonably caught, and I'll update. or you can update it. this is StackOverflow.

answered Jan 2, 2019 at 21:07

1

To counter the float precision issues, you could use the Decimal module.

This demands an extra effort of converting to Decimal from int or float while writing the code, but you can instead pass str and modify the function if that sort of convenience is indeed necessary.

from decimal import Decimal


def decimal_range[*args]:

    zero, one = Decimal['0'], Decimal['1']

    if len[args] == 1:
        start, stop, step = zero, args[0], one
    elif len[args] == 2:
        start, stop, step = args + [one,]
    elif len[args] == 3:
        start, stop, step = args
    else:
        raise ValueError['Expected 1 or 2 arguments, got %s' % len[args]]

    if not all[[type[arg] == Decimal for arg in [start, stop, step]]]:
        raise ValueError['Arguments must be passed as ']

    # neglect bad cases
    if [start == stop] or [start > stop and step >= zero] or \
                          [start < stop and step start]^[step

Chủ Đề