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

146

Есть ли range()в 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
Это не дроби, а числа с плавающей запятой. А поплавки ... ну, скорее всего, дадут другие результаты, чем вы ожидаете.
6
Быстрый обходной путь - рассматривать целые числа как десятичные, например:, range(5, 50, 5)а затем просто делить каждое число на 10.
NullUserException 01
@delnan - обновлено. Я готов принять мелкие неточности ради удобства диапазона с плавающей запятой,
Джонатан
2
возможный дубликат значения шага Python decimal range ()
Джонатан
@NullUserException - это всего лишь пример - настоящий код, конечно, параметрический :)
Джонатан

Ответы:

99

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

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
кичик
источник
35
Девиз Python: на самом деле должен быть один - и желательно только один - очевидный способ сделать это . Но Python в любом случае классный :)
Джонатан
3
>>> print list(frange(0,100,0.1))[-1]==100.0будетFalse
Володимир Копей
1
frangeможет работать неожиданно. Из-за проклятия арифметики с плавающей запятой , например, frange(0.0, 1.0, 0.1)дает 11 значений, где находится последнее значение 0.9999999999999999. Практическое улучшение было бы, while x + sys.float_info.epsilon < y:хотя даже это, вероятно, может потерпеть неудачу с большим числом .
Аксели Пален
11
-1 Пожалуйста, не используйте этот код , по крайней мере, в программах, которые могут когда-либо повлиять на мою жизнь. Нет никакого способа заставить его работать надежно. Не используйте ответ Аксели Палена. Используйте ответ Xaerxess или wim (за исключением того, что игнорируйте часть об arange).
Benrg
3
это отлично работает, если вы используетеdecimal.Decimal как step вместо float.
Tadhg McDonald-Jensen
116

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

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

или используйте лямбда / карту:

map(lambda x: x/10.0, range(5, 50, 15))
Xaerxess
источник
1
И array (range (5,50,15)) / 10.0, поскольку массивы numpy имеют операторы для обработки деления, умножения и т. Д.
edvaldig 01
2
@edvaldig: ты прав, я не знал об этом ... Тем не менее я думаю, что arange(0.5, 5, 1.5)ИМО более читабельно.
Xaerxess 01
2
Я предпочитаю этот ответ принятому, потому что первые два представленных решения основаны на переборе целых чисел и получении окончательных чисел с плавающей запятой из целых чисел. Это более надежно. Если вы сделаете это напрямую с помощью чисел с плавающей запятой, вы рискуете получить странные разовые ошибки из-за того, как числа с плавающей запятой представлены внутри. Например, если вы попытаетесь list(frange(0, 1, 0.5)), он работает нормально, и 1 исключен, но если вы попытаетесь list(frange(0, 1, 0.1)), последнее полученное значение будет близко к 1,0, что, вероятно, не то, что вам нужно. Представленные здесь решения не имеют этой проблемы.
blubberdiblub
3
Никогда не используйте numpy.arange (сама документация по numpy не рекомендует этого). Используйте numpy.linspace, как рекомендовано wim, или одно из других предложений в этом ответе.
Benrg
82

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

>>> import numpy
>>> numpy.linspace(0, 10, num=4)
array([  0.        ,   3.33333333,   6.66666667,  10.        ])
слабак
источник
Тем не менее, ошибки с плавающей запятой все еще существуют, без использования decimal, например:np.linspace(-.1,10,num=5050)[0]
TNT
2
@TNT Нет, это не ошибка. Вы обнаружите, что np.linspace(-.1,10,num=5050)[0] == -.1это правда. Просто repr(np.float64('-0.1'))цифра показывает больше.
wim
1
Хотя этот конкретный пример не показывает избыточной ошибки округления, есть случаи сбоя. Например, print(numpy.linspace(0, 3, 148)[49])распечатывает, 0.9999999999999999когда будет идеальный результат 1.0. linspaceработает намного лучше, чем arange, но не гарантирует минимально возможную ошибку округления.
user2357112 поддерживает Монику
Это будет гарантированно выполнять правильную обработку конечной точки, и всегда производит именно запрошенное количество элементов.
user2357112 поддерживает Монику
40

У 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. ])
Пэт
источник
4
Frange устарел, так как версия matplotlib 2.2. numpy.arange следует использовать.
kuzavas
14

С нетерпением оценил (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.
Карл Кнехтель
источник
4
+1; но почему не (x * .5 for x in range(10))в качестве генератора выражения для ленивых вычислений?
Тим Пицкер 01
2
Думаю, это было бы слишком просто? :)
Карл Кнехтель
11

использование 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]
Аюш
источник
3
+1 за использование itertools.takewhile. Однако itertools.count(start, step)страдает от накопленных ошибок с плавающей запятой. (Оцените, takewhile(lambda x: x < 100, count(0, 0.1))например.) Я бы написал takewhile(lambda x: x < stop, (start + i * step for i in count()))вместо этого.
musiphil
7

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

more_itertools.numeric_range(start, stop, step) действует как встроенный диапазон функций, но может обрабатывать типы с плавающей запятой, десятичные числа и дроби.

>>> from more_itertools import numeric_range
>>> tuple(numeric_range(.1, 5, 1))
(0.1, 1.1, 2.1, 3.1, 4.1)
Уильям Руснак
источник
4

Такой встроенной функции нет, но вы можете использовать следующий код (код 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

Marcotama
источник
3

Раствор без Numpy и т.д. зависимостей был предоставлен Kichik но из - за точечную арифметику с плавающей , она часто ведет себя неожиданно. Как отметили я и 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 .

Аксели Пален
источник
Это не работает с frange (2.0, 17.0 / 6.0, 1.0 / 6.0). Невозможно сделать его прочным.
Benrg
@benrg Спасибо, что указали на это! Это заставило меня понять, что эпсилон должен зависеть от прыжка, поэтому я просмотрел алгоритм и исправил проблему. Эта новая версия намного надежнее, не так ли?
Аксели Пален,
3

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

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

Если учесть, что создание индекса зависит от количества индексов и их значений, использование реализации типа float 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.

Greenstick
источник
Это довольно запутанный аргумент. range () следует просто рассматривать в генераторе итераций, и вопрос о том, используется ли он в цикле for или для индексации чего-либо, следует оставить вызывающим. Люди тысячелетиями использовали float в цикле for, и приведенные выше оправдания бессмысленны. Люди в комитетах Python облажались здесь много раз, и хороший аргумент, вероятно, был заглушен некоторыми извращенными оправданиями, такими как выше. Это так просто и понятно. Сейчас слишком много решений, подобных вышеизложенному, закреплено на языке Python.
Шитал Шах
3

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

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

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это количество шагов, которые нужно пройти от начала до конца, и range(nsteps)всегда выдает целые числа, поэтому нет потери точности. Последний шаг - линейно отобразить [0..nsteps] на [start..stop].

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

Если вы, как и alancalvitti , хотите, чтобы серия имела точное рациональное представление, вы всегда можете использовать 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)]

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

бесстрашный_фолл
источник
Если вы хотите включить значение стопа в вывод, добавив стоп + прыжок, этот метод затем вернется к наивному результату с плохими плавающими точками в середине, попробуйте frange(0,1.1,0.1)и даже больше тех, у кого есть выбор, напримерfrange(0,1.05,0.1)
alancalvitti
@alancalvitti: Как вы определяете «плохую» плавающую точку? Да, результаты могут быть нечеткими, но frange () предоставляет ближайший набор равномерно распределенных значений в пределах представления с плавающей запятой. Как бы вы его улучшили?
fearless_fool
Хороший момент, я настолько привык к языку высокого уровня, где для такой задачи вы можете использовать рациональные числа, что Py чувствует себя как ассемблер.
alancalvitti
Сборка? Ура! ;) Конечно , Python может обеспечить точное представление с дробями: docs.python.org/3/library/fractions.html
fearless_fool
Хорошо, спасибо, но, например, язык, который мне нравится, автоматически преобразует эти типы, поэтому 1/2 является рациональным, а 1 / 2.0 - плавающим, нет необходимости объявлять их как таковые - оставьте объявления для Java, что еще больше ниже / сборка, чем Py.
alancalvitti
2

Это можно сделать с помощью numpy.arange (start, stop, stepize)

import numpy as np

np.arange(0.5,5,1.5)
>> [0.5, 2.0, 3.5, 5.0]

# OBS you will sometimes see stuff like this happening, 
# so you need to decide whether that's not an issue for you, or how you are going to catch it.
>> [0.50000001, 2.0, 3.5, 5.0]

Примечание 1. Из обсуждения в разделе комментариев здесь, «никогда не используйте numpy.arange()(сама документация numpy не рекомендует этого). Используйте numpy.linspace, как рекомендовано wim, или одно из других предложений в этом ответе»

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

MRK
источник
2

Как писал Кичик , это не должно быть слишком сложно. Однако этот код:

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

Неуместен из-за кумулятивного эффекта ошибок при работе с поплавками. Вот почему вы получаете что-то вроде:

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

Хотя ожидаемое поведение будет:

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

Решение 1

Кумулятивную ошибку можно просто уменьшить, используя индексную переменную. Вот пример:

from math import ceil

    def frange2(start, stop, step):
        n_items = int(ceil((stop - start) / step))
        return (start + i*step for i in range(n_items))

Этот пример работает должным образом.

Решение 2

Нет вложенных функций. Только время и переменная счетчика:

def frange3(start, stop, step):
    res, n = start, 1

    while res < stop:
        yield res
        res = start + n * step
        n += 1

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

>>>list(frange3(1, 0, -.1))
[]

Решение 1 в этом случае будет работать должным образом. Чтобы эта функция работала в таких ситуациях, вы должны применить хак, подобный следующему:

from operator import gt, lt

def frange3(start, stop, step):
    res, n = start, 0.
    predicate = lt if start < stop else gt
    while predicate(res, stop):
        yield res
        res = start + n * step
        n += 1

С помощью этого хака вы можете использовать эти функции с отрицательными шагами:

>>>list(frange3(1, 0, -.1))
[1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.3999999999999999, 0.29999999999999993, 0.19999999999999996, 0.09999999999999998]

Решение 3

Вы можете пойти еще дальше с простой стандартной библиотекой и составить функцию диапазона для большинства числовых типов:

from itertools import count
from itertools import takewhile

def any_range(start, stop, step):
    start = type(start + step)(start)
    return takewhile(lambda n: n < stop, count(start, step))

Этот генератор адаптирован из книги Fluent Python (Глава 14. Итерационные объекты, итераторы и генераторы). Это не сработает с уменьшающимися диапазонами. Вы должны применить хак, как в предыдущем решении.

Вы можете использовать этот генератор, например, следующим образом:

>>>list(any_range(Fraction(2, 1), Fraction(100, 1), Fraction(1, 3)))[-1]
299/3
>>>list(any_range(Decimal('2.'), Decimal('4.'), Decimal('.3')))
[Decimal('2'), Decimal('2.3'), Decimal('2.6'), Decimal('2.9'), Decimal('3.2'), Decimal('3.5'), Decimal('3.8')]

И, конечно, вы также можете использовать его с float и int .

Быть осторожен

Если вы хотите использовать эти функции с отрицательными шагами, вы должны добавить отметку о шаге, например:

no_proceed = (start < stop and step < 0) or (start > stop and step > 0)
if no_proceed: raise StopIteration

Лучше всего поднять StopIteration, если вы хотите имитировать rangeсаму функцию.

Мимический диапазон

Если вы хотите имитировать rangeинтерфейс функции, вы можете предоставить несколько проверок аргументов:

def any_range2(*args):
    if len(args) == 1:
        start, stop, step = 0, args[0], 1.
    elif len(args) == 2:
        start, stop, step = args[0], args[1], 1.
    elif len(args) == 3:
        start, stop, step = args
    else:
        raise TypeError('any_range2() requires 1-3 numeric arguments')

    # here you can check for isinstance numbers.Real or use more specific ABC or whatever ...

    start = type(start + step)(start)
    return takewhile(lambda n: n < stop, count(start, step))

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

столпа4
источник
1

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

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

Применение

# Counting up
drange(0, 0.4, 0.1)
[0, 0.1, 0.2, 0.30000000000000004, 0.4]

# Counting down
drange(0, -0.4, -0.1)
[0, -0.1, -0.2, -0.30000000000000004, -0.4]

Чтобы округлить каждый шаг до N десятичных знаков

drange(0, 0.4, 0.1, round_decimal_places=4)
[0, 0.1, 0.2, 0.3, 0.4]

drange(0, -0.4, -0.1, round_decimal_places=4)
[0, -0.1, -0.2, -0.3, -0.4]

Код

def drange(start, end, increment, round_decimal_places=None):
    result = []
    if start < end:
        # Counting up, e.g. 0 to 0.4 in 0.1 increments.
        if increment < 0:
            raise Exception("Error: When counting up, increment must be positive.")
        while start <= end:
            result.append(start)
            start += increment
            if round_decimal_places is not None:
                start = round(start, round_decimal_places)
    else:
        # Counting down, e.g. 0 to -0.4 in -0.1 increments.
        if increment > 0:
            raise Exception("Error: When counting down, increment must be negative.")
        while start >= end:
            result.append(start)
            start += increment
            if round_decimal_places is not None:
                start = round(start, round_decimal_places)
    return result

Почему выбрали этот ответ?

  • Многие другие ответы будут зависать, когда их попросят отсчитать.
  • Многие другие ответы дадут неверно округленные результаты.
  • Другие ответы, основанные на результатах, np.linspaceявляются случайными, они могут работать или не работать из-за трудности выбора правильного количества делений. np.linspaceдействительно борется с десятичными приращениями 0,1, и порядок деления в формуле для преобразования приращения в количество разбиений может привести либо к правильному, либо к неверному коду.
  • Другие ответы, основанные на np.arange, устарели.

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

Контанго
источник
0
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 ].

Хуа Конги
источник
0

Я думаю, что есть очень простой ответ, который действительно имитирует все функции диапазона, но как для чисел с плавающей точкой, так и для целых чисел. В этом решении вы просто предполагаете, что ваше приближение по умолчанию равно 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)
Хосе Алаведра
источник
0

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

arange = lambda start, stop, step: [i + step * i for i in range(int((stop - start) / step))]

Если я напишу:

arange(0, 1, 0.1)

Он выведет:

[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]
MikeyE
источник
0

Поговорим о том, чтобы сделать гору из кротовины. Если вы ослабите требование сделать аналог rangeфункции с плавающей запятой и просто создадите список с плавающей запятой, который легко использовать в forцикле, кодирование будет простым и надежным.

def super_range(first_value, last_value, number_steps):
    if not isinstance(number_steps, int):
        raise TypeError("The value of 'number_steps' is not an integer.")
    if number_steps < 1:
        raise ValueError("Your 'number_steps' is less than 1.")

    step_size = (last_value-first_value)/(number_steps-1)

    output_list = []
    for i in range(number_steps):
        output_list.append(first_value + step_size*i)
    return output_list

first = 20.0
last = -50.0
steps = 5

print(super_range(first, last, steps))

На выходе будет

[20.0, 2.5, -15.0, -32.5, -50.0]

Обратите внимание, что функция super_rangeне ограничивается поплавками. Он может обрабатывать любой тип данных , для которых операторы +, -, *, и /определены, например complex, Decimalи numpy.array:

import cmath
first = complex(1,2)
last = complex(5,6)
steps = 5

print(super_range(first, last, steps))

from decimal import *
first = Decimal(20)
last = Decimal(-50)
steps = 5

print(super_range(first, last, steps))

import numpy as np
first = np.array([[1, 2],[3, 4]])
last = np.array([[5, 6],[7, 8]])
steps = 5

print(super_range(first, last, steps))

Результат будет:

[(1+2j), (2+3j), (3+4j), (4+5j), (5+6j)]
[Decimal('20.0'), Decimal('2.5'), Decimal('-15.0'), Decimal('-32.5'), Decimal('-50.0')]
[array([[1., 2.],[3., 4.]]),
 array([[2., 3.],[4., 5.]]),
 array([[3., 4.],[5., 6.]]),
 array([[4., 5.],[6., 7.]]),
 array([[5., 6.],[7., 8.]])]
Дэвид Ламберт
источник
-1

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

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
Григор Колев
источник
3
Довольно плохое решение, попробуйте f_range(0.01,0.02,0.001)... Для большинства практических целей arangeот Numpy - простое, безопасное и быстрое решение.
Барт
Ты прав. С numpy на 1,8 быстрее, чем мой код.
Григор Колев
Ты прав. С numpy на 1,8 быстрее, чем мой код. Но система, в которой я работаю, полностью закрыта. Только Python и pyserial больше нет.
Григор Колев
-2

Здесь есть несколько ответов, которые не обрабатывают простые крайние случаи, такие как отрицательный шаг, неправильный запуск, остановка и т. Д. Вот версия, которая обрабатывает многие из этих случаев правильно, давая такое же поведение, как родное range():

def frange(start, stop=None, step=1):
  if stop is None:
    start, stop = 0, start
  steps = int((stop-start)/step)
  for i in range(steps):
    yield start
    start += step  

Обратите внимание, что это приведет к ошибке step = 0, как и native range. Одно отличие состоит в том, что собственный диапазон возвращает объект, который является индексируемым и обратимым, а указанный выше - нет.

Вы можете поиграть с этим кодом и тестовыми примерами здесь.

Шитал Шах
источник