Range() для поплавков

Есть ли эквивалент range() для float в 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

Ответ 1

Я не знаю встроенной функции, но писать ее, например это не должно быть слишком сложно.

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

Как отмечают комментарии, это может привести к непредсказуемым результатам, например:

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

Чтобы получить ожидаемый результат, вы можете использовать один из других ответов в этом вопросе или, как упоминалось в @Tadhg, вы можете использовать decimal.Decimal как аргумент jump. Обязательно инициализируйте его строкой, а не плавающей точкой.

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

Или даже:

import decimal

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

И затем:

>>> list(drange(0, 100, '0.1'))[-1]
99.9

Ответ 2

Вы можете использовать:

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

или используйте lambda/map:

map(lambda x: x/10.0, range(5, 50, 15))

Ответ 3

Я использовал numpy.arange, но имел некоторые сложности, контролирующие количество возвращаемых элементов из-за ошибок с плавающей запятой. Поэтому теперь я использую linspace, например:

>>> import numpy
>>> numpy.linspace(0, 10, num=4)
array([  0.        ,   3.33333333,   6.66666667,  10.        ])

Ответ 4

Pylab имеет frange (обертка, фактически, для 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. ])

Ответ 5

Ожидаемая оценка (2.x range):

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

Летильно оценивается (2.x xrange, 3.x range):

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

В качестве альтернативы:

itertools.islice(itertools.imap(lambda x: x * .5, itertools.count()), 10)
# without applying the `islice`, we get an infinite stream of half-integers.

Ответ 6

с использованием itertools: лениво оцененный диапазон с плавающей запятой:

>>> 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]

Ответ 7

Я помог добавить функцию numeric_range в пакет more-itertools.

more_itertools.numeric_range(start, stop, step) действует как встроенный диапазон функций, но может обрабатывать типы float, Decimal и Fraction.

>>> from more_itertools import numeric_range
>>> tuple(numeric_range(.1, 5, 1))
(0.1, 1.1, 2.1, 3.1, 4.1)

Ответ 8

A решение без зависимостей numpy и т.д. было предоставлено kichik, но из-за арифметики с плавающей запятой, он часто ведет себя неожиданно. Как отмечено me и blubberdiblub, дополнительные элементы легко проникают в результат. Например, naive_frange(0.0, 1.0, 0.1) даст 0.999... в качестве последнего значения и, таким образом, даст всего 11 значений.

Здесь приведена надежная версия:

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

Поскольку умножение, ошибки округления не накапливаются. Использование epsilon заботится о возможной ошибке округления умножения, хотя проблемы, конечно, могут возрастать в очень малых и очень больших концах. Теперь, как и ожидалось:

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

И с несколько большими номерами:

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

Код также доступен как GitHub Gist.

Ответ 9

Нет такой встроенной функции, но вы можете использовать следующий (код Python 3), чтобы сделать работу такой же безопасной, как позволяет Python.

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))

Вы можете проверить все это, выполнив несколько утверждений:

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

Код доступен на GitHub

Ответ 10

Почему в стандартной библиотеке нет реализации с плавающей запятой?

Как ясно из всех постов здесь, не существует версии range() плавающей запятой. Тем не менее, упущение имеет смысл, если учесть, что функция range() часто используется в качестве генератора индекса (и, конечно, это означает метод доступа). Итак, когда мы вызываем range(0,40), мы фактически говорим, что хотим 40 значений, начиная с 0, до 40, но не включая сами 40.

Если учесть, что генерация индексов зависит как от количества индексов, так и от их значений, использование реализации range() с плавающей точкой в стандартной библиотеке имеет меньше смысла. Например, если бы мы вызвали функцию frange(0, 10, 0.25), мы ожидали бы, что будут включены и 0, и 10, но это приведет к вектору с 41 значением.

Таким образом, frange() зависимости от ее использования всегда будет демонстрировать противоречивое интуитивное поведение; он либо имеет слишком много значений, воспринимаемых с точки зрения индексации, либо не содержит числа, которое разумно следует возвращать с математической точки зрения.

Математический пример использования

С учетом сказанного, как уже говорилось, numpy.linspace() выполняет генерацию с математической точки зрения:

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.
])

Вариант использования индексации

И для перспективы индексации я написал немного другой подход с некоторой хитрой магией строк, которая позволяет нам указывать количество десятичных знаков.

# 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))

Точно так же мы можем также использовать встроенную функцию round и указать количество десятичных знаков:

# 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)

Быстрое сравнение и производительность

Конечно, учитывая вышеприведенное обсуждение, эти функции имеют довольно ограниченный вариант использования. Тем не менее, вот краткое сравнение:

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)

Результаты идентичны для каждого:

-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

И немного времени:

>>> 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

Похоже, метод форматирования строк выигрывает в моей системе.

Ограничения

И, наконец, демонстрация сути из обсуждения выше и еще одного ограничения:

# "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

Кроме того, когда параметр skip не делится на значение stop, может возникать зияющая щель с учетом последней проблемы:

# 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

Есть способы решения этой проблемы, но, в конце концов, лучшим подходом, вероятно, будет просто использовать Numpy.

Ответ 11

Я написал функцию, которая возвращает кортеж из диапазона чисел с плавающей запятой двойной точности без каких-либо десятичных знаков за сотые. это просто вопрос разбора значений диапазона, таких как строки, и отрыва избытка. Я использую его для отображения диапазонов для выбора из пользовательского интерфейса. Я надеюсь, что кто-то найдет это полезным.

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

Ответ 12

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)

Обратите внимание, что первая буква диапазона - это капитал. Этот метод именования не рекомендуется для функций в Python. Вы можете изменить Range на что-то вроде drange или frange, если хотите. Функция "Диапазон" ведет себя так, как вы этого хотите. Здесь вы можете проверить его здесь [http://reference.wolfram.com/language/ref/Range.html].

Ответ 13

Я думаю, что есть очень простой ответ, который действительно эмулирует все функции диапазона, но как для float, так и для integer. В этом решении вы просто предполагаете, что ваше приближение по умолчанию - 1e-7 (или тот, который вы выберете), и вы можете изменить его при вызове функции.

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)

Ответ 14

Более простая безбиблиотечная версия

О, черт, я добавлю простую версию без библиотек. Не стесняйтесь улучшать это [*]:

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)]

Основная идея заключается в том, что nsteps - это количество шагов, которые nsteps вам пройти от начала до range(nsteps) а range(nsteps) всегда выдает целые числа, поэтому потери точности не происходит. Последний шаг - отобразить [0..nsteps] линейно на [start..stop].

редактировать

Если, как и alancalvitti, вы хотите, чтобы серия имела точное рациональное представление, вы всегда можете использовать дроби:

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)]

[*] В частности, frange() возвращает список, а не генератор. Но этого хватило для моих нужд.

Ответ 15

Есть ли эквивалент range() для float в Python? НЕТ Используйте это:

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