Range或numpy Arange,带结束限制,包括

ki1q1bka  于 2023-10-19  发布在  其他
关注(0)|答案(7)|浏览(94)

我期待得到:
输入:

arange(0.0,0.6,0.2)

产出:

0.,0.4

我想

0.,0.2,0.4,0.6

如何使用range或arange。如果不是,什么是替代品?

tf7tbtn2

tf7tbtn21#

一种获得所需输出的更简单方法是在上限中添加步长。比如说,

np.arange(start, end + step, step)

也会让你把终点也包括进去在您的案例中:

np.arange(0.0, 0.6 + 0.2, 0.2)

将导致

array([0. , 0.2, 0.4, 0.6]).
vqlkdk9b

vqlkdk9b2#

更新2023-04-21

修复了stop-startstep s =>的非整数的代码中的错误

简而言之/ TLDR

意外行为:

>>> np.arange(1, 1.3, .1)  # UNEXPECTED
array([1. , 1.1, 1.2, 1.3])

修复:

>>> from arange_cust import *
>>> np_arange_closed(1, 1.3, .1)
array([1. , 1.1, 1.2, 1.3])
>>> np_arange_open(1, 1.3, .1)
array([1. , 1.1, 1.2])

背景信息

你的问题我也遇到过几次。我通常通过添加一个小值来快速修复它。正如Kasrâmvd在评论中提到的,这个问题有点复杂,因为浮点舍入错误可能发生在numpy.arange中(参见herehere)。

意外行为可以在这个例子中找到:

>>> np.arange(1, 1.3, 0.1)
array([1. , 1.1, 1.2, 1.3])

为了让我自己清楚一点,我决定对np.arange非常小心。

代码

arange_cust.py

import numpy as np

def np_arange_cust(
        *args, rtol: float=1e-05, atol: float=1e-08, include_start: bool=True, include_stop: bool = False, **kwargs
):
    """
    Combines numpy.arange and numpy.isclose to mimic open, half-open and closed intervals.

    Avoids also floating point rounding errors as with
    >>> np.arange(1, 1.3, 0.1)
    array([1., 1.1, 1.2, 1.3])

    Parameters
    ----------
    *args : float
        passed to np.arange
    rtol : float
        if last element of array is within this relative tolerance to stop and include[0]==False, it is skipped
    atol : float
        if last element of array is within this relative tolerance to stop and include[1]==False, it is skipped
    include_start: bool
        if first element is included in the returned array
    include_stop: bool
        if last elements are included in the returned array if stop equals last element
    kwargs :
        passed to np.arange

    Returns
    -------
    np.ndarray :
        as np.arange but eventually with first and last element stripped/added
    """
    # process arguments
    if len(args) == 1:
        start = 0
        stop = args[0]
        step = 1
    elif len(args) == 2:
        start, stop = args
        step = 1
    else:
        assert len(args) == 3
        start, stop, step = tuple(args)

    arr = np.arange(start, stop, step, **kwargs)
    if not include_start:
        arr = np.delete(arr, 0)

    if include_stop:
        if np.isclose(arr[-1] + step, stop, rtol=rtol, atol=atol):
            arr = np.c_[arr, arr[-1] + step]
    else:
        if np.isclose(arr[-1], stop, rtol=rtol, atol=atol):
            arr = np.delete(arr, -1)
    return arr

def np_arange_closed(*args, **kwargs):
    return np_arange_cust(*args, **kwargs, include_start=True, include_stop=True)

def np_arange_open(*args, **kwargs):
    return np_arange_cust(*args, **kwargs, include_start=True, include_stop=False)

Pytests

为了避免将来的错误,这里有一个测试模块。如果我们再次找到一些东西,让我们添加一个测试用例。test_arange_cust.py

import numpy as np
from arange_cust import np_arange_cust, np_arange_closed, np_arange_open
import pytest

class Test_np_arange_cust:
    paras_minimal_working_example = {
        "arange simple": {
            "start": 0, "stop": 7, "step": 1, "include_start": True, "include_stop": False,
            "res_exp": np.array([0, 1, 2, 3, 4, 5, 6])
        },
        "stop not on grid": {
            "start": 0, "stop": 6.5, "step": 1, "include_start": True, "include_stop": False,
            "res_exp": np.array([0, 1, 2, 3, 4, 5, 6])
        },
        "arange failing example: stop excl": {
            "start": 1, "stop": 1.3, "step": .1, "include_start": True, "include_stop": False,
            "res_exp": np.array([1., 1.1, 1.2])
        },
        "arange failing example: stop incl": {
            "start": 1, "stop": 1.3, "step": .1, "include_start": True, "include_stop": True,
            "res_exp": np.array([1., 1.1, 1.2, 1.3])
        },
        "arange failing example: stop excl + start excl": {
            "start": 1, "stop": 1.3, "step": .1, "include_start": False, "include_stop": False,
            "res_exp": np.array([1.1, 1.2])
        },
        "arange failing example: stop incl + start excl": {
            "start": 1, "stop": 1.3, "step": .1, "include_start": False, "include_stop": True,
            "res_exp": np.array([1.1, 1.2, 1.3])
        },

    }

    @pytest.mark.parametrize(
        argnames=next(iter(paras_minimal_working_example.values())).keys(),
        argvalues=[tuple(paras.values()) for paras in paras_minimal_working_example.values()],
        ids=paras_minimal_working_example.keys(),
    )
    def test_minimal_working_example(self, start, stop, step, include_start, include_stop, res_exp):
        res = np_arange_cust(start, stop, step, include_start=include_start, include_stop=include_stop)
        assert np.allclose(res, res_exp), f"Unexpected result: {res=}, {res_exp=}"
mrzz3bfm

mrzz3bfm3#

有趣的是,你得到了这样的输出。运行arange(0.0,0.6,0.2)得到:

array([0. , 0.2, 0.4])

numpy.arange文档来看:* 值是在半开区间[开始,停止](即包含开始但不包含停止的区间)内生成的
也来自docs:
当使用非整数步长(如0.1)时,结果往往不一致。对于这些情况,最好使用numpy.linspace *
我能建议的唯一一件事就是修改stop参数,增加一个非常小的量,例如

np.arange(0.0, 0.6 + 0.001 ,0.2)

返回

array([0. , 0.2, 0.4, 0.6])

这是你想要的输出。
无论如何,最好使用numpy.linspace并设置endpoint=True

f5emj3cl

f5emj3cl4#

老问题,但它可以做得更容易。

def arange(start, stop, step=1, endpoint=True):
    arr = np.arange(start, stop, step)

    if endpoint and arr[-1]+step==stop:
        arr = np.concatenate([arr,[end]])

    return arr

print(arange(0, 4, 0.5, endpoint=True))
print(arange(0, 4, 0.5, endpoint=False))

这给

[0.  0.5 1.  1.5 2.  2.5 3.  3.5 4. ]
[0.  0.5 1.  1.5 2.  2.5 3.  3.5]
i34xakig

i34xakig5#

一个使用np.linspace的简单示例(在其他答案中多次提到,但没有简单的示例):

import numpy as np

start = 0.0
stop = 0.6
step = 0.2

num = round((stop - start) / step) + 1   # i.e. length of resulting array
np.linspace(start, stop, num)

>>> array([0.0, 0.2, 0.4, 0.6])

假设:stopstep的倍数。round是纠正浮点错误所必需的。

o4tp2gmn

o4tp2gmn6#

好吧,我把这个解决方案留在这里。第一步是计算给定边界[a,b]step数量的项目数的小数部分。接下来,计算一个适当的量添加到末尾,这不会影响结果numpy数组的大小,然后调用np.arrange()

import numpy as np

def np_arange_fix(a, b, step):
    nf = (lambda n: n-int(n))((b - a)/step+1)
    bb = (lambda x: step*max(0.1, x) if x < 0.5 else 0)(nf)
    arr = np.arange(a, b+bb, step)

    if int((b-a)/step+1) != len(arr):
        print('I failed, expected {} items, got {} items, arr-out{}'.format(int((b-a)/step), len(arr), arr))
        raise

    return arr

print(np_arange_fix(1.0, 4.4999999999999999, 1.0))
print(np_arange_fix(1.0, 4 + 1/3, 1/3))
print(np_arange_fix(1.0, 4 + 1/3, 1/3 + 0.1))
print(np_arange_fix(1.0, 6.0, 1.0))
print(np_arange_fix(0.1, 6.1, 1.0))

图纸:

[1. 2. 3. 4.]
[1.         1.33333333 1.66666667 2.         2.33333333 2.66666667
 3.         3.33333333 3.66666667 4.         4.33333333]
[1.         1.43333333 1.86666667 2.3        2.73333333 3.16666667
 3.6        4.03333333]
[1. 2. 3. 4. 5. 6.]
[0.1 1.1 2.1 3.1 4.1 5.1 6.1]

如果你想把它压缩成一个函数:

def np_arange_fix(a, b, step):
    b += (lambda x: step*max(0.1, x) if x < 0.5 else 0)((lambda n: n-int(n))((b - a)/step+1))
    return np.arange(a, b, step)
cwtwac6a

cwtwac6a7#

当输入结束值比输入步骤更容易时,我这样做:

np.arange(0, 100e3+1)/100e3

相关问题