range() 用于浮点数

发布于 2024-12-02 13:36:40 字数 313 浏览 0 评论 0原文

Python 中是否有与浮点数等效的 range()

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

Is there a range() equivalent for floats 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(25

倾城月光淡如水﹏ 2024-12-09 13:36:41

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

Pylab has frange (a wrapper, actually, 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. ])
梦幻的心爱 2024-12-09 13:36:41

急切求值 (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.

Eagerly evaluated (2.x range):

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

Lazily evaluated (2.x xrange, 3.x range):

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

Alternately:

itertools.islice(itertools.imap(lambda x: x * .5, itertools.count()), 10)
# without applying the `islice`, we get an infinite stream of half-integers.
垂暮老矣 2024-12-09 13:36:41

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

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]
天煞孤星 2024-12-09 13:36:41

我不知道这个问题是否很老,但是 NumPy 库中有一个 arange 函数,它可以作为一个范围。

np.arange(0,1,0.1)

#out: 

array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

I do not know if the question is old but there is a arange function in the NumPy library, it could work as a range.

np.arange(0,1,0.1)

#out: 

array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
影子是时光的心 2024-12-09 13:36:41

我帮助将函数 numeric_range 添加到包 更多-itertools

more_itertools.numeric_range(start, stop, step) 的作用类似于内置函数 range,但可以处理浮点数、小数和分数类型。

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

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

more_itertools.numeric_range(start, stop, step) acts like the built in function range but can handle floats, Decimal, and Fraction types.

>>> from more_itertools import numeric_range
>>> tuple(numeric_range(.1, 5, 1))
(0.1, 1.1, 2.1, 3.1, 4.1)
我恋#小黄人 2024-12-09 13:36:41

正如 kichik 所写,这不应该太复杂。然而,这段代码

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 将按预期工作。要使此函数在这种情况下工作,您必须应用一个 hack,类似于以下内容:

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

通过此 hack,您可以使用负步骤使用这些函数:

>>>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 章:迭代器、迭代器和生成器)。 它不适用于递减的范围。您必须应用 hack,就像在之前的解决方案中一样。

您可以按如下方式使用此生成器,例如:

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

当然您可以将它与 floatint 一起使用强>也是如此。

小心

如果您想使用这些具有负步长的函数,您应该添加对步长符号的检查,例如:

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 标准库。

As kichik wrote, this shouldn't be too complicated. However this code:

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

Is inappropriate because of the cumulative effect of errors when working with floats.
That is why you receive something like:

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

While the expected behavior would be:

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

Solution 1

The cumulative error can simply be reduced by using an index variable. Here's the example:

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

This example works as expected.

Solution 2

No nested functions. Only a while and a counter variable:

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

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

This function will work well too, except for the cases when you want the reversed range. E.g:

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

Solution 1 in this case will work as expected. To make this function work in such situations, you must apply a hack, similar to the following:

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

With this hack you can use these functions with negative steps:

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

Solution 3

You can go even further with plain standard library and compose a range function for the most of numeric types:

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

This generator is adapted from the Fluent Python book (Chapter 14. Iterables, Iterators and generators). It will not work with decreasing ranges. You must apply a hack, like in the previous solution.

You can use this generator as follows, for example:

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

And of course you can use it with float and int as well.

Be careful

If you want to use these functions with negative steps, you should add a check for the step sign, e.g.:

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

The best option here is to raise StopIteration, if you want to mimic the range function itself.

Mimic range

If you would like to mimic the range function interface, you can provide some argument checks:

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

I think, you've got the point. You can go with any of these functions (except the very first one) and all you need for them is python standard library.

七色彩虹 2024-12-09 13:36:41

kichik 提供了 没有 numpy 等依赖项的解决方案,但由于 浮点运算,它的行为常常出乎意料。正如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
        if x < y:
          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

代码也可以作为 a GitHub Gist 提供。

A solution without numpy etc dependencies was provided by kichik but due to the floating point arithmetics, it often behaves unexpectedly. As noted by me and blubberdiblub, additional elements easily sneak into the result. For example naive_frange(0.0, 1.0, 0.1) would yield 0.999... as its last value and thus yield 11 values in total.

A bit more robust version is provided here:

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
        if x < y:
          yield x

Because the multiplication, the rounding errors do not accumulate. The use of epsilon takes care of possible rounding error of the multiplication, even though issues of course might rise in the 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 somewhat larger 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.

段念尘 2024-12-09 13:36:41

没有这样的内置函数,但您可以使用以下(Python 3 代码)来尽可能安全地完成这项工作。

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

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

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 it by running a few assertions:

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 available on GitHub

反话 2024-12-09 13:36:41

为什么标准库中没有浮点范围实现?

正如此处所有帖子所明确的那样,没有 range() 的浮点版本。也就是说,如果我们考虑到 range() 函数经常用作索引(当然,这意味着访问器)生成器,那么省略是有道理的。因此,当我们调用 range(0,40) 时,我们实际上是在说我们想要 40 个值,从 0 开始,一直到 40,但不包括 40 本身。

当我们认为索引生成与索引的数量和索引的值一样重要时,在标准库中使用 range() 的浮点实现就没有意义了。例如,如果我们调用函数 frange(0, 10, 0.25),我们期望同时包含 0 和 10,但这会产生一个具有 41 个值的生成器,而不是可能包含 40 个值的生成器。预计 10/0.25 开始。

因此,根据其用途,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.
])

索引用例< /strong>

对于索引的角度,我编写了一种稍微不同的方法,其中包含一些技巧性的字符串魔术,使我们能够指定小数位数。

# 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 整除时code> value,考虑到后一个问题,可能会出现巨大的差距:

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

Why Is There No Floating Point Range Implementation In The Standard Library?

As made clear by all the posts here, there is no floating point version of range(). That said, the omission makes sense if we consider that the range() function is often used as an index (and of course, that means an accessor) generator. So, when we call range(0,40), we're in effect saying we want 40 values starting at 0, up to 40, but non-inclusive of 40 itself.

When we consider that index generation is as much about the number of indices as it is their values, the use of a float implementation of range() in the standard library makes less sense. For example, if we called the function frange(0, 10, 0.25), we would expect both 0 and 10 to be included, but that would yield a generator with 41 values, not the 40 one might expect from 10/0.25.

Thus, depending on its use, an frange() function will always exhibit counter intuitive behavior; it either has too many values as perceived from the indexing perspective or is not inclusive of a number that reasonably should be returned from the mathematical perspective. In other words, it's easy to see how such a function would appear to conflate two very different use cases – the naming implies the indexing use case; the behavior implies a mathematical one.

The Mathematical Use Case

With that said, as discussed in other posts, numpy.linspace() performs the generation from the mathematical perspective nicely:

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

The Indexing Use Case

And for the indexing perspective, I've written a slightly different approach with some tricksy 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 decimals:

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

A Quick Comparison & Performance

Of course, given the above discussion, these functions have a fairly limited use case. Nonetheless, here's 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 some timings:

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

Looks like the string formatting method wins by a hair on my system.

The Limitations

And finally, a demonstration of the point from the discussion above and one last 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

Further, when the skip parameter is not divisible by the stop value, there can be a yawning gap given the latter issue:

# 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 address this issue, but at the end of the day, the best approach would probably be to just use Numpy.

我最亲爱的 2024-12-09 13:36:41

一个更简单的无库版本

哦,哎呀——我将扔进一个简单的无库版本。请随意改进它[*]:

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一样,您希望该系列具有精确的理性表示,您可以随时使用分数

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() 返回一个列表,而不是一个生成器。但已经足够满足我的需求了。

A simpler library-less version

Aw, heck -- I'll toss in a simple library-less version. Feel free to improve on it[*]:

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 core idea is that nsteps is the number of steps to get you from start to stop and range(nsteps) always emits integers so there's no loss of accuracy. The final step is to map [0..nsteps] linearly onto [start..stop].

edit

If, like alancalvitti you'd like the series to have exact rational representation, 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 it sufficed for my needs.

骑趴 2024-12-09 13:36:41

这可以通过 numpy.arange(start, stop, stepsize) 来完成

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 文档本身建议反对它)。按照 wim 的建议使用 numpy.linspace,或者中的其他建议之一这个答案”

注释 2:
我已经阅读了这里的一些评论中的讨论,但是在第三次回到这个问题后,我觉得这些信息应该放在更容易阅读的位置。

This can be done with numpy.arange(start, stop, stepsize)

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]

Note 1:
From the discussion in the comment section here, "never use numpy.arange() (the numpy documentation itself recommends against it). Use numpy.linspace as recommended by wim, or one of the other suggestions in this answer"

Note 2:
I have read the discussion in a few comments here, but after coming back to this question for the third time now, I feel this information should be placed in a more readable position.

小糖芽 2024-12-09 13:36:41

用法

# 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 的其他答案已被弃用。

如果有疑问,请尝试上面的四个测试用例。

Usage

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

To round each step to N decimal places

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]

Code

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

Why choose this answer?

  • Many other answers will hang when asked to count down.
  • Many other answers will give incorrectly rounded results.
  • Other answers based on np.linspace are hit-and-miss, they may or may not work due to difficulty in choosing the correct number of divisions. np.linspace really struggles with decimal increments of 0.1, and the order of divisions in the formula to convert the increment into a number of splits can result in either correct or broken code.
  • Other answers based on np.arange are deprecated.

If in doubt, try the four tests cases above.

最后的乘客 2024-12-09 13:36:41

我编写了一个函数,它返回一系列双精度浮点数的元组,没有任何超出百位的小数位。这只是像解析字符串一样解析范围值并分割掉多余的问题。我用它来显示范围以从 UI 中进行选择。我希望其他人发现它有用。

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

i wrote a function that returns a tuple of a range of double precision floating point numbers without any decimal places beyond the hundredths. it was simply a matter of parsing the range values like strings and splitting off the excess. I use it for displaying ranges to select from within a UI. I hope someone else finds it useful.

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
似狗非友 2024-12-09 13:36:41

虽然基于整数的范围被明确定义为“所见即所得”,但有些东西在浮点数中不容易看到,这会导致在所需范围内获得看似明确定义的行为时遇到麻烦。

可以采用两种方法:

  1. 将给定范围分割为一定数量的段:使用 linspace 方法,当您选择许多不能很好地划分范围的点时,您可以接受大量小数位数(例如,从 0 到 1,分 7 步,第一步值为 0.14285714285714285)

  2. 给出您已经知道应该起作用并希望它起作用的所需 WYSIWIG 步长大小。由于获得的值未达到您想要达到的终点,您的希望常常会破灭。

倍数可能高于或低于您的预期:

>>> 3*.1 > .3  # 0.30000000000000004
True

>>> 3*.3 < 0.9  # 0.8999999999999999
True

您将尝试通过添加步骤的倍数而不是递增来避免累积错误,但问题总是会出现,如果您手动执行,您将无法得到预期的结果在纸上——带有精确的小数。但你知道这应该是可能的,因为Python向你显示0.1而不是接近0.1的基础整数比率:

>>> (3*.1).as_integer_ratio()
(1351079888211149, 4503599627370496)

在作为答案提供的方法中,使用分数此处最好提供将输入处理为字符串的选项。我有一些建议可以让它变得更好:

  1. 让它处理类似范围的默认值,这样你就可以自动从 0 开始
  2. 让它处理递减的范围
  3. 如果你使用精确的算术,那么输出看起来就像你所期望的那样

我提供了一个例程来执行这些操作类似的事情,但不使用 Fraction 对象。相反,它使用 round 来创建与使用 python 打印的数字具有相同表观数字的数字,例如 1 位小数表示 0.1 等,3 位小数表示 0.004 等:

def frange(start, stop, step, n=None):
    """return a WYSIWYG series of float values that mimic range behavior
    by excluding the end point and not printing extraneous digits beyond
    the precision of the input numbers (controlled by n and automatically
    detected based on the string representation of the numbers passed).

    EXAMPLES
    ========

    non-WYSIWYS simple list-comprehension

    >>> [.11 + i*.1 for i in range(3)]
    [0.11, 0.21000000000000002, 0.31]

    WYSIWYG result for increasing sequence

    >>> list(frange(0.11, .33, .1))
    [0.11, 0.21, 0.31]

    and decreasing sequences

    >>> list(frange(.345, .1, -.1))
    [0.345, 0.245, 0.145]

    To hit the end point for a sequence that is divisibe by
    the step size, make the end point a little bigger by
    adding half the step size:

    >>> dx = .2
    >>> list(frange(0, 1 + dx/2, dx))
    [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

    """
    if step == 0:
        raise ValueError('step must not be 0')
    # how many decimal places are showing?
    if n is None:
        n = max([0 if '.' not in str(i) else len(str(i).split('.')[1])
                for i in (start, stop, step)])
    if step*(stop - start) > 0:  # a non-null incr/decr range
        if step < 0:
            for i in frange(-start, -stop, -step, n):
                yield -i
        else:
            steps = round((stop - start)/step)
            while round(step*steps + start, n) < stop:
                steps += 1
            for i in range(steps):
                yield round(start + i*step, n)

Whereas integer-based ranges are well defined in that "what you see is what you get", there are things that are not readily seen in floats that cause troubles in getting what appears to be a well defined behavior in a desired range.

There are two approaches that one can take:

  1. split a given range into a certain number of segment: the linspace approach in which you accept the large number of decimal digits when you select a number of points that does not divide the span well (e.g. 0 to 1 in 7 steps will give a first step value of 0.14285714285714285)

  2. give the desired WYSIWIG step size that you already know should work and wish that it would work. Your hopes will often be dashed by getting values that miss the end point that you wanted to hit.

Multiples can be higher or lower than you expect:

>>> 3*.1 > .3  # 0.30000000000000004
True

>>> 3*.3 < 0.9  # 0.8999999999999999
True

You will try to avoid accumulating errors by adding multiples of your step and not incrementing, but the problem will always present itself and you just won't get what you expect if you did it by hand on paper -- with exact decimals. But you know it should be possible since Python shows you 0.1 instead of the underlying integer ratio having a close approximation to 0.1:

>>> (3*.1).as_integer_ratio()
(1351079888211149, 4503599627370496)

In the methods offered as answers, the use of Fraction here with the option to handle input as strings is best. I have a few suggestions to make it better:

  1. make it handle range-like defaults so you can start from 0 automatically
  2. make it handle decreasing ranges
  3. make the output look like you would expect if you were using exact arithmetic

I offer a routine that does these same sort of thing but which does not use the Fraction object. Instead, it uses round to create numbers having the same apparent digits as the numbers would have if you printed them with python, e.g. 1 decimal for something like 0.1 and 3 decimals for something like 0.004:

def frange(start, stop, step, n=None):
    """return a WYSIWYG series of float values that mimic range behavior
    by excluding the end point and not printing extraneous digits beyond
    the precision of the input numbers (controlled by n and automatically
    detected based on the string representation of the numbers passed).

    EXAMPLES
    ========

    non-WYSIWYS simple list-comprehension

    >>> [.11 + i*.1 for i in range(3)]
    [0.11, 0.21000000000000002, 0.31]

    WYSIWYG result for increasing sequence

    >>> list(frange(0.11, .33, .1))
    [0.11, 0.21, 0.31]

    and decreasing sequences

    >>> list(frange(.345, .1, -.1))
    [0.345, 0.245, 0.145]

    To hit the end point for a sequence that is divisibe by
    the step size, make the end point a little bigger by
    adding half the step size:

    >>> dx = .2
    >>> list(frange(0, 1 + dx/2, dx))
    [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

    """
    if step == 0:
        raise ValueError('step must not be 0')
    # how many decimal places are showing?
    if n is None:
        n = max([0 if '.' not in str(i) else len(str(i).split('.')[1])
                for i in (start, stop, step)])
    if step*(stop - start) > 0:  # a non-null incr/decr range
        if step < 0:
            for i in frange(-start, -stop, -step, n):
                yield -i
        else:
            steps = round((stop - start)/step)
            while round(step*steps + start, n) < stop:
                steps += 1
            for i in range(steps):
                yield round(start + i*step, n)
甜柠檬 2024-12-09 13:36:41

我在 y 轴上绘制概率时遇到了同样的问题。我用过这个:

list(map(lambda x : x.round(1), np.linspace(0.0, 1.0, num=6)))

这会产生:

[0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

您可以使用round()来处理小数部分。

I was having the same issue to plot the probabilities on y-axis. I have used this:

list(map(lambda x : x.round(1), np.linspace(0.0, 1.0, num=6)))

This produces:

[0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

You can play with round() for the decimal part.

早茶月光 2024-12-09 13:36:41
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)

请注意 Range 的第一个字母是大写的。 Python 中的函数不鼓励使用这种命名方法。如果需要,您可以将 Range 更改为 drange 或 frange 等。 “范围”功能的行为正如您所希望的那样。您可以在此处查看其手册 [ http://reference.wolfram.com/language/ref /Range.html]。

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)

Please note the first letter of Range is capital. This naming method is not encouraged for functions in Python. You can change Range to something like drange or frange if you want. The "Range" function behaves just as you want it to. You can check it's manual here [ http://reference.wolfram.com/language/ref/Range.html ].

爱的故事 2024-12-09 13:36:41

我认为有一个非常简单的答案,它真正模拟了范围的所有功能,但对于浮点和整数。在此解决方案中,您只需假设默认近似值为 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)

I think that there is a very simple answer that really emulates all the features of range but for both float and integer. In this solution, you just suppose that your approximation by default 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)
决绝 2024-12-09 13:36:41

谈论小山变大山。
如果您放宽对 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 不限于浮点数。它可以处理定义了运算符 +-*/ 的任何数据类型,例如作为complexDecimalnumpy.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.]])]

Talk about making a mountain out of a mole hill.
If you relax the requirement to make a float analog of the range function, and just create a list of floats that is easy to use in a for loop, the coding is simple and robust.

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

The output will be

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

Note that the function super_range is not limited to floats. It can handle any data type for which the operators +, -, *, and / are defined, such as complex, Decimal, and 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))

The output will be:

[(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.]])]
清风挽心 2024-12-09 13:36:41

Python 中是否有与 float 等效的 range() ?

使用这个:

def f_range(start, end, step, coef=0.01):
    a = range(int(start/coef), int(end/coef), int(step/coef))
    var = []
    for item in a:
        var.append(item*coef)
    return var

Is there a range() equivalent for floats in Python?
NO
Use this:

def f_range(start, end, step, coef=0.01):
    a = range(int(start/coef), int(end/coef), int(step/coef))
    var = []
    for item in a:
        var.append(item*coef)
    return var
骷髅 2024-12-09 13:36:41

哇。
这件事你们真的是想多了。

你在干什么?
您是否真的需要一个范围内的步骤列表,或者您只是检查
如果一个浮点数在另外两个浮点数的范围内?

解析视频时,我经常需要检查 splice_point 是否在某个 PTS 范围内。
我只是这样做

    if start < splice_point < stop:

如果您需要检查步骤,

  def in_range_with_step(splice_point,start,stop,step):
     if start < splice_point < stop:
         if (splice_point -start) % step ==0:
             return True
     return False

:如果您只想要范围内的步骤:

    def a_range(start,stop,step):
        the_range=[]
        while start < stop:  
            the_range.append(start)
            start+=step
       return the_range   

Wow.
Y'all are really overthinking this.

What are you doing?
Do you really need a list of steps in a range, or are you just checking
if a float is in the range of two other floats?

Parsing video, I often need to check if a splice_point is in a certain PTS range.
I just do

    if start < splice_point < stop:

if you need to check with a step:

  def in_range_with_step(splice_point,start,stop,step):
     if start < splice_point < stop:
         if (splice_point -start) % step ==0:
             return True
     return False

if you just want the steps in the range:

    def a_range(start,stop,step):
        the_range=[]
        while start < stop:  
            the_range.append(start)
            start+=step
       return the_range   
歌枕肩 2024-12-09 13:36:41

当然会有一些舍入误差,所以这并不完美,但这是我通常用于不需要高精度的应用程序的情况。如果您想让其更准确,您可以添加一个额外的参数来指定如何处理舍入错误。也许传递舍入函数可能会使它可扩展,并允许程序员指定如何处理舍入错误。

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]

There will be of course some rounding errors, so this is not perfect, but this is what I use generally for applications, which don't require high precision. If you wanted to make this more accurate, you could add an extra argument to specify how to handle rounding errors. Perhaps passing a rounding function might make this extensible and allow the programmer to specify how to handle rounding errors.

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

If I write:

arange(0, 1, 0.1)

It will output:

[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]
情仇皆在手 2024-12-09 13:36:41

这里有几个答案不处理简单的边缘情况,如负步、错误开始、停止等。这是正确处理许多这些情况的版本,给出与本机 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,就像原生range一样。一个区别是本机范围返回可索引和可​​逆的对象,而上面则不然。

您可以使用此代码并在此处测试用例。

There several answers here that don't handle simple edge cases like negative step, wrong start, stop etc. Here's the version that handles many of these cases correctly giving same behaviour as native 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  

Note that this would error out step=0 just like native range. One difference is that native range returns object that is indexable and reversible while above doesn't.

You can play with this code and test cases here.

杀手六號 2024-12-09 13:36:40

您可以使用:

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

或使用 lambda / map:

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

You can either 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))
_蜘蛛 2024-12-09 13:36:40


I don't know a built-in function, but writing one like [this](https://stackoverflow.com/a/477610/623735) shouldn't be too complicated.

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

[编辑者不:如果您只使用正 jump 和整数开始和停止(xy),这有效美好的。有关更通用的解决方案,请参阅此处。]


I don't know a built-in function, but writing one like [this](https://stackoverflow.com/a/477610/623735) shouldn't be too complicated.

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


---

As the comments mention, this could produce unpredictable results like:

>>> 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. Make sure to initialize it with a string rather than a float.

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

[editor's not: if you only use positive jump and integer start and stop (x and y) , this works fine. For a more general solution see here.]

决绝 2024-12-09 13:36:40

我曾经使用 numpy.arange ,但由于浮点错误,在控制它返回的元素数量时遇到了一些复杂的情况。所以现在我使用linspace,例如:

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

I used to use numpy.arange but had some complications controlling the number of elements it returns, due to floating point errors. So now I use linspace, e.g.:

>>> import numpy
>>> numpy.linspace(0, 10, num=4)
array([  0.        ,   3.33333333,   6.66666667,  10.        ])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文