4

I'm building a range between two numbers (floats) and I'd like this range to be of an exact fixed length (no more, no less). range and arange work with steps, instead. To put things into pseudo Python, this is what I'd like to achieve:

start_value = -7.5
end_value = 0.1
my_range = my_range_function(star_value, end_value, length=6)

print my_range
[-7.50,-5.98,-4.46,-2.94,-1.42,0.10]

This is essentially equivalent to the R function seq which can specify a sequence of a given length. Is this possible in Python?

Thanks.

Einar
  • 4,727
  • 7
  • 49
  • 64
  • Using floats doesn't make it easier to compute the step accurately. Is it important that the range be exactly those values, and whether the step is exactly the same, or is a rounding error OK? – extraneon Mar 18 '10 at 11:42
  • I use floats because the values come from another calculation. The end point of the range can be an integer, however (such as 0). And the values need to be exact or I'd get other kinds of errors down the way. – Einar Mar 18 '10 at 12:00

5 Answers5

6

Use linspace() from NumPy.

>>> from numpy import linspace
>>> linspace(-7.5, 0.1, 6)
array([-7.5 , -5.98, -4.46, -2.94, -1.42,  0.1])
>>> linspace(-7.5, 0.1, 6).tolist()
[-7.5, -5.9800000000000004, -4.46, -2.9399999999999995, -1.4199999999999999, 0.10000000000000001]

It should be the most efficient and accurate.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Leonid Shvechikov
  • 3,927
  • 2
  • 17
  • 14
4

See Recipe 66472: frange(), a range function with float increments (Python) with various float implementations, their pros and cons.

Alternatively, if precision is important to you, work with decimal.Decimal instead of float (convert to and then back) as answered in Python decimal range() step value.

Community
  • 1
  • 1
van
  • 74,297
  • 13
  • 168
  • 171
  • `numpy.arange` does the trick for that. The problem is that those functions use steps, while I am requiring an exact length beforehand. – Einar Mar 18 '10 at 12:04
  • does that really stop you: `inc = (end-start)/length` before using the function, or have another function that does that and calls real implementation? Still, if you want any EXACTness, you should try to to work with Decimal instead, then convert back to float. – van Mar 18 '10 at 12:17
2
def my_function(start, end, length):
    len = length - 1
    incr = (end-start) / len
    r = [ start ]
    for i in range(len):
        r.append ( r[i] + incr )
    return r
Richard
  • 2,994
  • 1
  • 19
  • 31
1

How about this:

def my_range_function(start, end, length):
    if length <= 1: return [ start ]
    step = (end - start) / (length - 1)
    return [(start + i * step) for i in xrange(length)]

For your sample range, it returns:

[-7.5, -5.9800000000000004, -4.46,
 -2.9399999999999995, -1.4199999999999999, 0.099999999999999645]

Of course it's full of round errors, but that's what you get when working with floats.

unwind
  • 391,730
  • 64
  • 469
  • 606
1

In order to handle the rounding errors, the following code utilizes Python's decimal module. You can set the rounding; for this sample I've set it to two decimal points via round_setting = '.01'. In order to handle any rounding errors, the last step is adjusted to the remainder.

Code

#!/usr/bin/env python
# encoding: utf-8
from __future__ import print_function
import math
import decimal


start_value = -7.5
end_value = 0.1
num_of_steps = 6

def my_range(start_value, end_value, num_of_steps):
    round_setting = '.01'
    start_decimal = decimal.Decimal(str(start_value)).quantize(
        decimal.Decimal(round_setting))
    end_decimal = decimal.Decimal(str(end_value)).quantize(
        decimal.Decimal(round_setting))
    num_of_steps_decimal = decimal.Decimal(str(num_of_steps)).quantize(
        decimal.Decimal(round_setting))
    step_decimal = ((end_decimal - start_decimal) / 
        num_of_steps_decimal).quantize(decimal.Decimal(round_setting))
    # Change the last step in case there are rounding errors
    last_step_decimal = (end_decimal - ((num_of_steps - 1) * step_decimal) -
            start_decimal).quantize(decimal.Decimal(round_setting))
    print('Start value = ', start_decimal)
    print('End value = ', end_decimal)
    print('Number of steps = ', num_of_steps)
    print('Normal step for range = ', step_decimal)
    print('Last step used for range = ', last_step_decimal)

my_range(start_value, end_value, num_of_steps)

Output

$ ./fixed_range.py 
Start value =  -7.50
End value =  0.10
Number of steps =  6
Normal step for range =  1.27
Last step used for range =  1.25

From there you can use the normal step and the last step to create your list.

Matthew Rankin
  • 457,139
  • 39
  • 126
  • 163