is there any ceil() function based on abs value like trunc vs floor or round-away-from-zero function?

狂风中的少年 提交于 2021-02-19 05:57:22

问题


numpy.trunc is a floor function based on abs value:

a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0])
np.floor(a)
Out[122]: array([-2., -2., -1.,  0.,  1.,  1.,  2.])
np.trunc(a)
Out[123]: array([-1., -1., -0.,  0.,  1.,  1.,  2.])

The ceil output is this:

np.ceil(a)
Out[124]: array([-1., -1., -0.,  1.,  2.,  2.,  2.])

But I want an output: array([-2., -2., -1., 1., 2., 2., 2.]) What is the numpy function for this?

edit: Unfortunately there is no build-in round away from zero function. Based on @Mitch and @Divakar answers I did some test and try to optimize the speed and memory usage.

def rawzero1(a): #@Mitch
    return np.sign(a)*np.ceil(np.abs(a))


def rawzero2(a): #@Divakar
    return np.where(a<0, np.floor(a), np.ceil(a))


def rawzero3(a):
    _a = np.abs(a)
    np.ceil(_a, _a) # inplace
    np.copysign(_a, a, out = _a)
    return _a


def rawzero4(a):
    _a = np.ceil(np.abs(a))
    np.copysign(_a, a, out = _a)
    return _a

array size: 762.94 MB

| func     |   t per call (ms) |   max mem use (MB) |   mem to array size (MB) |
|:---------|------------------:|-------------------:|-------------------------:|
| rawzero1 |           1071.34 |            3082.51 |                     4.04 |
| rawzero2 |           1237.74 |            3183.39 |                     4.17 |
| rawzero3 |            543.71 |            1576.41 |                     2.07 |
| rawzero4 |            583.83 |            2328.77 |                     3.05 |

so the best is rawzero3, which use inplace operations to reduce memory usage and copysign to speed up.

update according to @Divakar which requires numexpr >=2.6.4. Any version before that does not have ceil().

rawzero1(a) == rawzero2(a)
rawzero1(a) == rawzero3(a)
rawzero1(a) == rawzero4(a)
rawzero1(a) == numexpr_1(a)
array size: 762.94 MB
| func      |   t per call (ms) |   max mem use (MB) |   mem to array size (MB) |
|:----------|------------------:|-------------------:|-------------------------:|
| rawzero1  |           1108.68 |            3828.35 |                     5.02 |
| rawzero2  |           1226.78 |            3940.69 |                     5.17 |
| rawzero3  |            531.54 |            2323.55 |                     3.05 |
| rawzero4  |            579.55 |            3082.49 |                     4.04 |
| numexpr_1 |            228.00 |            2323.57 |                     3.05 |

There is no surprise at all. numexpr version will give the best speed and same memory footprint as rawzero3. The weird part is rawzero3's memory footprint is kind of unstable. Theoretically it should only use 2x array size instead of 3x.


回答1:


Another option (though not a built-in) could be taking the ceiling of the absolute values.

np.sign(a) * np.ceil(np.abs(a))



回答2:


Approach #1 : Use np.where to do the choosing between floor and ceil based on the positivity/negativity -

np.where(a<0, np.floor(a), np.ceil(a))

Sample run -

In [61]: a
Out[61]: array([-1.7, -1.5, -0.2,  0.2,  1.5,  1.7,  2. ])

In [62]: np.where(a<0, np.floor(a), np.ceil(a))
Out[62]: array([-2., -2., -1.,  1.,  2.,  2.,  2.])

Approach #2 : We could extend the sign trick used in @Mitch's post by using a comparison against zero and scaling to get the sign equivalent and also leverages numexpr module to perform the ceil on abs values. Now, we should keep in mind that numexpr performs better with large datasets. Thus, the implementation would be -

import numexpr as ne

ne.evaluate('(2*(a>0)-1)*ceil(abs(a))')

Runtime test

All approaches -

def rawzero1(a):
    return np.sign(a)*np.ceil(np.abs(a))

def rawzero2(a):
    return np.where(a<0, np.floor(a), np.ceil(a))

def rawzero3(a):
    _a = np.abs(a)
    np.ceil(_a, _a) # inplace
    np.copysign(_a, a, out = _a)
    return _a

def rawzero4(a):
    _a = np.ceil(np.abs(a))
    np.copysign(_a, a, out = _a)
    return _a

def numexpr_1(a):
    return ne.evaluate('(2*(a>0)-1)*ceil(abs(a))')

Timings -

In [52]: a = np.random.randn(1000000)

In [53]: %timeit rawzero1(a)
    ...: %timeit rawzero2(a)
    ...: %timeit rawzero3(a)
    ...: %timeit rawzero4(a)
    ...: %timeit numexpr_1(a)
    ...: 
100 loops, best of 3: 11.6 ms per loop
100 loops, best of 3: 13.2 ms per loop
100 loops, best of 3: 4.9 ms per loop
100 loops, best of 3: 6.54 ms per loop
1000 loops, best of 3: 1.65 ms per loop

In [54]: np.allclose(numexpr_1(a), rawzero1(a))
Out[54]: True



回答3:


You can use this syntax [ np.ceil(x) if x>0 else np.floor(x) for x in lst) ]

to do it manually. Not sure about the function.

Like this post One-line list comprehension: if-else variants



来源:https://stackoverflow.com/questions/46572699/is-there-any-ceil-function-based-on-abs-value-like-trunc-vs-floor-or-round-awa

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!