range() 用于浮点数
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(25)
Pylab 有
frange
(实际上是matplotlib.mlab.frange
的包装器):Pylab has
frange
(a wrapper, actually, formatplotlib.mlab.frange
):急切求值 (2.x
range
):懒惰求值 (2.x
xrange
, 3.xrange
):或者:
Eagerly evaluated (2.x
range
):Lazily evaluated (2.x
xrange
, 3.xrange
):Alternately:
使用itertools:延迟计算浮点范围:
using
itertools
: lazily evaluated floating point range:我不知道这个问题是否很老,但是 NumPy 库中有一个 arange 函数,它可以作为一个范围。
I do not know if the question is old but there is a
arange
function in theNumPy
library, it could work as a range.我帮助将函数 numeric_range 添加到包 更多-itertools。
more_itertools.numeric_range(start, stop, step)
的作用类似于内置函数 range,但可以处理浮点数、小数和分数类型。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.正如 kichik 所写,这不应该太复杂。然而,这段代码
是不合适的,因为使用浮点数时会产生错误的累积效应。
这就是为什么您会收到类似以下内容的原因:
虽然预期的行为是:
解决方案 1
通过使用索引变量可以简单地减少累积误差。这是示例:
此示例按预期工作。
解决方案 2
无嵌套函数。只有一段时间和一个计数器变量:
这个函数也可以很好地工作,除了您想要反转范围的情况。例如:
本例中的解决方案 1 将按预期工作。要使此函数在这种情况下工作,您必须应用一个 hack,类似于以下内容:
通过此 hack,您可以使用负步骤使用这些函数:
解决方案 3
您可以使用普通标准库更进一步,并为大多数数字类型:
该生成器改编自 Fluent Python 书(第 14 章:迭代器、迭代器和生成器)。 它不适用于递减的范围。您必须应用 hack,就像在之前的解决方案中一样。
您可以按如下方式使用此生成器,例如:
当然您可以将它与 float 和 int 一起使用强>也是如此。
小心
如果您想使用这些具有负步长的函数,您应该添加对步长符号的检查,例如:
如果您想模仿
,这里最好的选择是引发
函数本身。StopIteration
range模仿范围
如果您想模仿
range
函数接口,您可以提供一些参数检查:我想,您已经明白了。您可以使用这些函数中的任何一个(除了第一个函数),并且您所需要的所有就是 python 标准库。
As kichik wrote, this shouldn't be too complicated. However this code:
Is inappropriate because of the cumulative effect of errors when working with floats.
That is why you receive something like:
While the expected behavior would be:
Solution 1
The cumulative error can simply be reduced by using an index variable. Here's the example:
This example works as expected.
Solution 2
No nested functions. Only a while and a counter variable:
This function will work well too, except for the cases when you want the reversed range. E.g:
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:
With this hack you can use these functions with negative steps:
Solution 3
You can go even further with plain standard library and compose a range function for the most of numeric types:
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:
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.:
The best option here is to raise
StopIteration
, if you want to mimic therange
function itself.Mimic range
If you would like to mimic the
range
function interface, you can provide some argument checks: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.
kichik 提供了 没有 numpy 等依赖项的解决方案,但由于 浮点运算,它的行为常常出乎意料。正如我和blubberdiblub,其他元素很容易潜入结果中。例如,
naive_frange(0.0, 1.0, 0.1)
将生成0.999...
作为其最后一个值,从而总共生成 11 个值。这里提供了一个更强大的版本:
因为乘法,舍入误差不会累积。使用 epsilon 可以解决乘法可能出现的舍入误差,即使在非常小和非常大的末端可能会出现问题。现在,正如预期的那样:
对于更大的数字:
代码也可以作为 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 yield0.999...
as its last value and thus yield 11 values in total.A bit more robust version is provided here:
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:And with somewhat larger numbers:
The code is also available as a GitHub Gist.
没有这样的内置函数,但您可以使用以下(Python 3 代码)来尽可能安全地完成这项工作。
您可以通过运行一些断言来验证所有内容:
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.
You can verify all of it by running a few assertions:
Code available on GitHub
为什么标准库中没有浮点范围实现?
正如此处所有帖子所明确的那样,没有
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()
从数学角度很好地执行了生成:索引用例< /strong>
对于索引的角度,我编写了一种稍微不同的方法,其中包含一些技巧性的字符串魔术,使我们能够指定小数位数。
同样,我们也可以使用内置的
round
函数并指定小数位数:快速比较&性能
当然,考虑到上述讨论,这些函数的用例相当有限。尽管如此,这里有一个快速比较:
每个结果都是相同的:
以及一些时间:
看起来字符串格式化方法在我的系统上以微弱优势获胜。
限制
最后,演示上述讨论的要点和最后一个限制:
此外,当
skip
参数不能被stop
整除时code> value,考虑到后一个问题,可能会出现巨大的差距:有很多方法可以解决这个问题,但归根结底,最好的方法可能是只使用 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 therange()
function is often used as an index (and of course, that means an accessor) generator. So, when we callrange(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 functionfrange(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 from10/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: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.
Similarly, we can also use the built-in
round
function and specify the number of 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:
The results are identical for each:
And some timings:
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:
Further, when the
skip
parameter is not divisible by thestop
value, there can be a yawning gap given the latter issue:There are ways to address this issue, but at the end of the day, the best approach would probably be to just use Numpy.
一个更简单的无库版本
哦,哎呀——我将扔进一个简单的无库版本。请随意改进它[*]:
核心思想是
nsteps
是从开始到停止的步数,并且range(nsteps)
始终发出整数所以不会损失准确性。最后一步是将 [0..nsteps] 线性映射到 [start..stop]。编辑
如果像alancalvitti一样,您希望该系列具有精确的理性表示,您可以随时使用分数:
[*] 特别是,
frange()
返回一个列表,而不是一个生成器。但已经足够满足我的需求了。A simpler library-less version
Aw, heck -- I'll toss in a simple library-less version. Feel free to improve on it[*]:
The core idea is that
nsteps
is the number of steps to get you from start to stop andrange(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:
[*] In particular,
frange()
returns a list, not a generator. But it sufficed for my needs.注释 1:
从这里评论部分的讨论来看,“永远不要使用 numpy.arange() (numpy 文档本身建议反对它)。按照 wim 的建议使用 numpy.linspace,或者中的其他建议之一这个答案”
注释 2:
我已经阅读了这里的一些评论中的讨论,但是在第三次回到这个问题后,我觉得这些信息应该放在更容易阅读的位置。
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.
用法
将每一步四舍五入到小数点后 N 位
代码
为什么选择这个答案?
np.arange
的其他答案已被弃用。如果有疑问,请尝试上面的四个测试用例。
Usage
To round each step to N decimal places
Code
Why choose this answer?
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.np.arange
are deprecated.If in doubt, try the four tests cases above.
我编写了一个函数,它返回一系列双精度浮点数的元组,没有任何超出百位的小数位。这只是像解析字符串一样解析范围值并分割掉多余的问题。我用它来显示范围以从 UI 中进行选择。我希望其他人发现它有用。
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.
虽然基于整数的范围被明确定义为“所见即所得”,但有些东西在浮点数中不容易看到,这会导致在所需范围内获得看似明确定义的行为时遇到麻烦。
可以采用两种方法:
将给定范围分割为一定数量的段:使用 linspace 方法,当您选择许多不能很好地划分范围的点时,您可以接受大量小数位数(例如,从 0 到 1,分 7 步,第一步值为 0.14285714285714285)
给出您已经知道应该起作用并希望它起作用的所需 WYSIWIG 步长大小。由于获得的值未达到您想要达到的终点,您的希望常常会破灭。
倍数可能高于或低于您的预期:
您将尝试通过添加步骤的倍数而不是递增来避免累积错误,但问题总是会出现,如果您手动执行,您将无法得到预期的结果在纸上——带有精确的小数。但你知道这应该是可能的,因为Python向你显示
0.1
而不是接近0.1的基础整数比率:在作为答案提供的方法中,使用分数此处最好提供将输入处理为字符串的选项。我有一些建议可以让它变得更好:
我提供了一个例程来执行这些操作类似的事情,但不使用 Fraction 对象。相反,它使用 round 来创建与使用 python 打印的数字具有相同表观数字的数字,例如 1 位小数表示 0.1 等,3 位小数表示 0.004 等:
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:
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)
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:
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: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:
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:我在 y 轴上绘制概率时遇到了同样的问题。我用过这个:
这会产生:
您可以使用
round()
来处理小数部分。I was having the same issue to plot the probabilities on y-axis. I have used this:
This produces:
You can play with
round()
for the decimal part.请注意 Range 的第一个字母是大写的。 Python 中的函数不鼓励使用这种命名方法。如果需要,您可以将 Range 更改为 drange 或 frange 等。 “范围”功能的行为正如您所希望的那样。您可以在此处查看其手册 [ http://reference.wolfram.com/language/ref /Range.html]。
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 ].
我认为有一个非常简单的答案,它真正模拟了范围的所有功能,但对于浮点和整数。在此解决方案中,您只需假设默认近似值为 1e-7(或您选择的近似值),并且可以在调用函数时更改它。
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.
谈论小山变大山。
如果您放宽对
range
函数进行浮点模拟的要求,而只需创建一个易于在for
循环中使用的浮点列表,则编码会很简单且坚固耐用。输出将为 请
注意,函数
super_range
不限于浮点数。它可以处理定义了运算符+
、-
、*
和/
的任何数据类型,例如作为complex
、Decimal
和numpy.array
:输出将是:
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 afor
loop, the coding is simple and robust.The output will be
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 ascomplex
,Decimal
, andnumpy.array
:The output will be:
Python 中是否有与 float 等效的 range() ?
不
使用这个:
Is there a range() equivalent for floats in Python?
NO
Use this:
哇。
这件事你们真的是想多了。
你在干什么?
您是否真的需要一个范围内的步骤列表,或者您只是检查
如果一个浮点数在另外两个浮点数的范围内?
解析视频时,我经常需要检查 splice_point 是否在某个 PTS 范围内。
我只是这样做
如果您需要检查步骤,
:如果您只想要范围内的步骤:
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 you need to check with a step:
if you just want the steps in the range:
当然会有一些舍入误差,所以这并不完美,但这是我通常用于不需要高精度的应用程序的情况。如果您想让其更准确,您可以添加一个额外的参数来指定如何处理舍入错误。也许传递舍入函数可能会使它可扩展,并允许程序员指定如何处理舍入错误。
如果我写:
它将输出:
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.
If I write:
It will output:
这里有几个答案不处理简单的边缘情况,如负步、错误开始、停止等。这是正确处理许多这些情况的版本,给出与本机
range()
相同的行为:请注意这会错误出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()
: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.
您可以使用:
或使用 lambda / map:
You can either use:
or use lambda / map:
I don't know a built-in function, but writing one like [this](https://stackoverflow.com/a/477610/623735) shouldn't be too complicated.
---
正如评论中提到的,这可能会产生不可预测的结果,例如:
要获得预期结果,您可以使用此问题中的其他答案之一,或者正如 @Tadhg 提到的,您可以使用
decimal.Decimal
作为jump
参数。确保使用字符串而不是浮点数对其进行初始化。甚至:
然后:
[编辑者不:如果您只使用正
jump
和整数开始和停止(x
和y
),这有效美好的。有关更通用的解决方案,请参阅此处。]I don't know a built-in function, but writing one like [this](https://stackoverflow.com/a/477610/623735) shouldn't be too complicated.
---
As the comments mention, this could produce unpredictable results like:
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 thejump
argument. Make sure to initialize it with a string rather than a float.Or even:
And then:
[editor's not: if you only use positive
jump
and integer start and stop (x
andy
) , this works fine. For a more general solution see here.]我曾经使用 numpy.arange ,但由于浮点错误,在控制它返回的元素数量时遇到了一些复杂的情况。所以现在我使用
linspace
,例如: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 uselinspace
, e.g.: