Range () for floats

Is there an equivalent range() for float in Python?

 >>> range(0.5,5,1.5) [0, 1, 2, 3, 4] >>> range(0.5,5,0.5) Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> range(0.5,5,0.5) ValueError: range() step argument must not be zero 
+108
python decimal range fractions
Sep 01 '11 at 7:30
source share
15 answers

I do not know the built-in function, but writing it, for example, should not be too difficult.

 def frange(x, y, jump): while x < y: yield x x += jump 



As the comments note, this can lead to unpredictable results, for example:

 >>> list(frange(0, 100, 0.1))[-1] 99.9999999999986 

To get the expected result, you can use one of the other answers in this question or, as @Tadhg mentioned, you can use decimal.Decimal as the jump argument. Be sure to initialize it with a string, not a floating point.

 >>> import decimal >>> list(frange(0, 100, decimal.Decimal('0.1')))[-1] Decimal('99.9') 

Or even:

 import decimal def drange(x, y, jump): while x < y: yield float(x) x += decimal.Decimal(jump) 

And then:

 >>> list(drange(0, 100, '0.1'))[-1] 99.9 
+86
Sep 01 2018-11-11T00:
source share

You can use:

 [x / 10.0 for x in range(5, 50, 15)] 

or use lambda / map:

 map(lambda x: x/10.0, range(5, 50, 15)) 
+87
Sep 01 2018-11-11T00:
source share

I used numpy.arange , but had some difficulties controlling the number of returned items due to floating point errors. So now I use linspace , for example:

 >>> import numpy >>> numpy.linspace(0, 10, num=4) array([ 0. , 3.33333333, 6.66666667, 10. ]) 
+62
Sep 01 '11 at 8:30
source share

Pylab has frange (wrapper, in fact, for matplotlib.mlab.frange ):

 >>> import pylab as pl >>> pl.frange(0.5,5,0.5) array([ 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. ]) 
+37
01 Feb '13 at 19:07
source share

Expected rating (2.x range ):

 [x * .5 for x in range(10)] 

Flying rating (2.x xrange , 3.x range ):

 itertools.imap(lambda x: x * .5, xrange(10)) # or range(10) as appropriate 

As an alternative:

 itertools.islice(itertools.imap(lambda x: x * .5, itertools.count()), 10) # without applying the `islice`, we get an infinite stream of half-integers. 
+10
Sep 01 '11 at 7:40
source share

using itertools : lazily evaluated floating point range:

 >>> from itertools import count, takewhile >>> def frange(start, stop, step): return takewhile(lambda x: x< stop, count(start, step)) >>> list(frange(0.5, 5, 1.5)) # [0.5, 2.0, 3.5] 
+9
Dec 06 '15 at 7:07
source share

I helped add the numeric_range function to the more-itertools package .

more_itertools.numeric_range(start, stop, step) acts as a built-in range of functions, but can handle the types float, Decimal, and Fraction.

 >>> from more_itertools import numeric_range >>> tuple(numeric_range(.1, 5, 1)) (0.1, 1.1, 2.1, 3.1, 4.1) 
+4
Apr 22 '17 at 18:28
source share

A solution without numpy dependencies, etc. was provided by kichik, but due to floating point arithmetic , it often behaves unexpectedly. As noted by me and blubberdiblub , additional elements easily penetrate the result. For example, naive_frange(0.0, 1.0, 0.1) will give 0.999... as the last value, and thus give only 11 values.

Here is a reliable version:

 def frange(x, y, jump=1.0): '''Range for floats.''' i = 0.0 x = float(x) # Prevent yielding integers. x0 = x epsilon = jump / 2.0 yield x # yield always first value while x + epsilon < y: i += 1.0 x = x0 + i * jump yield x 

Since multiplication, rounding errors do not accumulate. Using epsilon takes care of a possible multiplication rounding error, although problems, of course, can increase at very small and very large ends. Now, as expected:

 > a = list(frange(0.0, 1.0, 0.1)) > a[-1] 0.9 > len(a) 10 

And with a few large numbers:

 > b = list(frange(0.0, 1000000.0, 0.1)) > b[-1] 999999.9 > len(b) 10000000 

The code is also available as a GitHub Gist .

+3
Mar 18 '16 at 18:19
source share

There is no such built-in function, but you can use the following (Python 3 code) to make it work as safe as Python allows.

 from fractions import Fraction def frange(start, stop, jump, end=False, via_str=False): """ Equivalent of Python 3 range for decimal numbers. Notice that, because of arithmetic errors, it is safest to pass the arguments as strings, so they can be interpreted to exact fractions. >>> assert Fraction('1.1') - Fraction(11, 10) == 0.0 >>> assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840) Parameter `via_str` can be set to True to transform inputs in strings and then to fractions. When inputs are all non-periodic (in base 10), even if decimal, this method is safe as long as approximation happens beyond the decimal digits that Python uses for printing. For example, in the case of 0.1, this is the case: >>> assert str(0.1) == '0.1' >>> assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410' If you are not sure whether your decimal inputs all have this property, you are better off passing them as strings. String representations can be in integer, decimal, exponential or even fraction notation. >>> assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0 >>> assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0 >>> assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0 >>> assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0 >>> assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0 >>> assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0 """ if via_str: start = str(start) stop = str(stop) jump = str(jump) start = Fraction(start) stop = Fraction(stop) jump = Fraction(jump) while start < stop: yield float(start) start += jump if end and start == stop: yield(float(start)) 

You can verify all of this by doing a few things:

 assert Fraction('1.1') - Fraction(11, 10) == 0.0 assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840) assert str(0.1) == '0.1' assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410' assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0 assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0 assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0 assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0 assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0 assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0 assert list(frange(2, 3, '1/6', end=True))[-1] == 3.0 assert list(frange(0, 100, '1/3', end=True))[-1] == 100.0 

Code is available on GitHub.

+3
Dec 22 '16 at 2:17
source share

Why is there no floating point implementation in the standard library?

As is clear from all the posts here, there is no floating point version of range() . However, the omission makes sense when you consider that the range() function is often used as an index generator (and, of course, this means an access method). So, when we call range(0,40) , we actually say that we want 40 values, starting from 0 to 40, but not including 40 themselves.

Given that index generation depends on both the number of indexes and their values, using the range() floating-point implementation in the standard library makes less sense. For example, if we called the frange(0, 10, 0.25) function, we would expect that 0 and 10 would be included, but this would lead to a vector with 41 values.

Thus, depending on its use, frange() will always exhibit conflicting intuitive behavior; it either has too many values ​​perceived from the point of view of indexing, or does not contain a number that should reasonably be returned from a mathematical point of view.

Mathematical use case

With that said, as already mentioned, numpy.linspace() performs the generation from a mathematical point of view:

 numpy.linspace(0, 10, 41) array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. , 2.25, 2.5 , 2.75, 3. , 3.25, 3.5 , 3.75, 4. , 4.25, 4.5 , 4.75, 5. , 5.25, 5.5 , 5.75, 6. , 6.25, 6.5 , 6.75, 7. , 7.25, 7.5 , 7.75, 8. , 8.25, 8.5 , 8.75, 9. , 9.25, 9.5 , 9.75, 10. ]) 

Indexing Use Case

And for the prospect of indexing, I wrote a slightly different approach with some tricky string magic that allows us to specify the number of decimal places.

 # Float range function - string formatting method def frange_S (start, stop, skip = 1.0, decimals = 2): for i in range(int(start / skip), int(stop / skip)): yield float(("%0." + str(decimals) + "f") % (i * skip)) 

Similarly, we can also use the built-in round function and specify the number of decimal places:

 # Float range function - rounding method def frange_R (start, stop, skip = 1.0, decimals = 2): for i in range(int(start / skip), int(stop / skip)): yield round(i * skip, ndigits = decimals) 

Quick comparison and performance

Of course, given the discussion above, these functions have a rather limited use case. However, here is a quick comparison:

 def compare_methods (start, stop, skip): string_test = frange_S(start, stop, skip) round_test = frange_R(start, stop, skip) for s, r in zip(string_test, round_test): print(s, r) compare_methods(-2, 10, 1/3) 

The results are identical for each:

 -2.0 -2.0 -1.67 -1.67 -1.33 -1.33 -1.0 -1.0 -0.67 -0.67 -0.33 -0.33 0.0 0.0 ... 8.0 8.0 8.33 8.33 8.67 8.67 9.0 9.0 9.33 9.33 9.67 9.67 

And a little time:

 >>> import timeit >>> setup = """ ... def frange_s (start, stop, skip = 1.0, decimals = 2): ... for i in range(int(start / skip), int(stop / skip)): ... yield float(("%0." + str(decimals) + "f") % (i * skip)) ... def frange_r (start, stop, skip = 1.0, decimals = 2): ... for i in range(int(start / skip), int(stop / skip)): ... yield round(i * skip, ndigits = decimals) ... start, stop, skip = -1, 8, 1/3 ... """ >>> min(timeit.Timer('string_test = frange_s(start, stop, skip); [x for x in string_test]', setup=setup).repeat(30, 1000)) 0.024284090992296115 >>> min(timeit.Timer('round_test = frange_r(start, stop, skip); [x for x in round_test]', setup=setup).repeat(30, 1000)) 0.025324633985292166 

It looks like the string formatting method wins on my system.

Limitations

And finally, a demonstration of the essence of the discussion above and another limitation:

 # "Missing" the last value (10.0) for x in frange_R(0, 10, 0.25): print(x) 0.25 0.5 0.75 1.0 ... 9.0 9.25 9.5 9.75 

In addition, when the skip parameter is not divided by the stop value, a gaping gap may occur, taking into account the last problem:

 # Clearly we know that 10 - 9.43 is equal to 0.57 for x in frange_R(0, 10, 3/7): print(x) 0.0 0.43 0.86 1.29 ... 8.14 8.57 9.0 9.43 

There are ways to solve this problem, but in the end, the best approach is probably to just use Numpy.

+3
Dec 18 '17 at 23:04 on
source share

I wrote a function that returns a tuple from a range of double precision floating point numbers without any decimal places in hundredths. it is simply a matter of parsing range values ​​such as strings and breaking off the excess. I use it to display ranges for selection from the user interface. I hope someone finds this helpful.

 def drange(start,stop,step): double_value_range = [] while start<stop: a = str(start) a.split('.')[1].split('0')[0] start = float(str(a)) double_value_range.append(start) start = start+step double_value_range_tuple = tuple(double_value_range) #print double_value_range_tuple return double_value_range_tuple 
+1
Nov 19 '15 at 4:53 on
source share
 def Range(*argSequence): if len(argSequence) == 3: imin = argSequence[0]; imax = argSequence[1]; di = argSequence[2] i = imin; iList = [] while i <= imax: iList.append(i) i += di return iList if len(argSequence) == 2: return Range(argSequence[0], argSequence[1], 1) if len(argSequence) == 1: return Range(1, argSequence[0], 1) 

Note that the first letter of the range is capital. This naming method is not recommended for functions in Python. You can change Range to something like drange or frange if you want. The Range function behaves the way you want it. Here you can check it here [ http://reference.wolfram.com/language/ref/Range.html ].

0
Jan 12 '17 at 9:17 on
source share

I think there is a very simple answer that really emulates all the functions of the range, but for both float and integer. In this solution, you simply assume that your default approximation is 1e-7 (or the one you choose), and you can change it when you call the function.

 def drange(start,stop=None,jump=1,approx=7): # Approx to 1e-7 by default ''' This function is equivalent to range but for both float and integer ''' if not stop: # If there is no y value: range(x) stop= start start= 0 valor= round(start,approx) while valor < stop: if valor==int(valor): yield int(round(valor,approx)) else: yield float(round(valor,approx)) valor += jump for i in drange(12): print(i) 
0
Oct. 14 '17 at 12:51 on
source share

A simpler non-lib version

Oh hell, I will add a simple version without libraries. Feel free to improve this [*]:

 def frange(start=0, stop=1, jump=0.1): nsteps = int((stop-start)/jump) dy = stop-start # f(i) goes from start to stop as i goes from 0 to nsteps return [start + float(i)*dy/nsteps for i in range(nsteps)] 

The main idea is that nsteps is the number of steps that nsteps you from the beginning to range(nsteps) and range(nsteps) always gives integers, so there is no loss of precision. The final step is to map [0..nsteps] linearly to [start..stop].

edit

If, like alancalvitti, you want the series to have an accurate rational presentation, you can always use fractions :

 from fractions import Fraction def rrange(start=0, stop=1, jump=0.1): nsteps = int((stop-start)/jump) return [Fraction(i, nsteps) for i in range(nsteps)] 

[*] In particular, frange() returns a list, not a generator. But that was enough for my needs.

0
Nov 14 '18 at 0:57
source share

Is there an equivalent range () for float in Python? NO Use this:

 def f_range(start, end, step): a = range(int(start/0.01), int(end/0.01), int(step/0.01)) var = [] for item in a: var.append(item*0.01) return var 
-one
Jan 12 '17 at 10:00
source share



All Articles