根据条件计算numpy数组元素的有效方法

2024-09-28 05:27:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试优化我的python代码。当 我试图根据每个元素的值对numpy数组应用一个函数。例如,我有一个包含数千个元素的数组,我对大于公差的值应用一个函数,对其余的值应用另一个函数(泰勒级数)。我做了掩蔽,但仍然很慢,至少我调用了以下函数6400万次。在

EPSILONZETA = 1.0e-6
ZETA1_12 = 1.0/12.0
ZETA1_720 = 1.0/720.0

def masked_condition_zero(array, tolerance):
    """ Return the indices where values are lesser (and greater) than tolerance
    """
    # search indices where array values < tolerance
    indzeros_ = np.where(np.abs(array) < tolerance)[0]

    # create mask
    mask_ = np.ones(np.shape(array), dtype=bool)

    mask_[[indzeros_]] = False

    return (~mask_, mask_) 

def bernoulli_function1(zeta):
    """ Returns the Bernoulli function of zeta, vector version
    """
    # get the indices according to condition
    zeros_, others_ = masked_condition_zero(zeta, EPSILONZETA)

    # create an array filled with zeros
    fb_ = np.zeros(np.shape(zeta))

    # Apply the original function to the values greater than EPSILONZETA
    fb_[others_] = zeta[others_]/(np.exp(zeta[others_])-1.0)  

    # computes series for zeta < eps
    zeta0_ = zeta[zeros_]
    zeta2_ = zeta0_ *  zeta0_
    zeta4_ =  zeta2_ * zeta2_
    fb_[zeros_] = 1.0 - 0.5*zeta0_ + ZETA1_12 * zeta2_ - ZETA1_720 * zeta4_
    return fb_

现在假设你有一个数组zeta,它有负的和正的浮点值,它在每个循环中都会发生变化,并且每次都要计算fbernoulli函数1(zeta)。在

有更好的解决办法吗?在


Tags: the函数fbnpzerosmask数组array
3条回答

问题的基本结构是:

def foo(zeta):
    result = np.empty_like(zeta)
    I = condition(zeta)
    nI = ~I
    result[I] = func1(zeta[I])
    result[nI] = func2(zeta[nI])

看起来你的多项式表达式根本可以求值zeta,但它是一个“例外”,即当zeta太接近0时的回退计算。在

如果这两个函数都可以对zeta求值,则可以在以下位置使用:

^{pr2}$

这是精简版:

def foo(zeta):
    result = np.empty_like(zeta)
    I = condition(zeta)
    nI = ~I
    v1 = func1(zeta)
    v2 = func2(zeta)
    result[I] = v1[I]
    result[nI] = v2[nI]

另一个选择是将一个函数应用于所有值,另一个仅应用于“异常”。在

def foo(zeta):
    result = func2(zeta)
    I = condition(zeta)
    result[I] = func1[zeta[I]]

当然还有反面-result = func1(zeta); result[nI]=func2[zeta]。在

在我简短的时间测试中,func1func2所用的时间差不多相同。在

masked_condition_zero也需要这段时间,但是更简单的np.abs(array) < tolerance(它是~J)可以将这个时间减半。在

让我们比较一下分配策略

def foo(zeta, J, nJ):
    result = np.empty_like(zeta)
    result[J] = fun1(zeta[J])
    result[nJ] = fun2(zeta[nJ])
    return result

对于zeta[J]是完整zeta的10%的示例,某些采样时间为:

In [127]: timeit foo(zeta, J, nJ)
10000 loops, best of 3: 55.7 µs per loop

In [128]: timeit result=fun2(zeta); result[J]=fun1(zeta[J])
10000 loops, best of 3: 49.2 µs per loop

In [129]: timeit np.where(J, fun1(zeta),fun2(zeta))
10000 loops, best of 3: 73.4 µs per loop

In [130]: timeit result=fun1(zeta); result[nJ]=fun2(zeta[nJ])
10000 loops, best of 3: 60.7 µs per loop

第二种情况最快,因为在较少的值上运行fun1可以补偿索引zeta[J]所增加的成本。在索引成本和功能评估成本之间有一个权衡。像这样的布尔索引比切片更昂贵。对于其他混合值,计时可能会朝另一个方向发展。在

这看起来是一个问题,你可以削减时间,但我看不到任何突破的战略,将削减时间一个数量级。在

您可以使用numba[1](如果您使用anaconda或类似的python发行版,则应安装numba[1]),这是一个旨在使用numpy的jit编译器。在

from numba import jit
@jit
def bernoulli_function_fill(zeta, fb_):
    for i in xrange(len(zeta)):
        if np.abs(zeta[i])>EPSILONZETA:
            fb_[i] = zeta[i]/(np.exp(zeta[i])-1.0)
        else:
            zeta0_ = zeta[i]
            zeta2_ = zeta0_ *  zeta0_
            zeta4_ =  zeta2_ * zeta2_
            fb_[i] = 1.0 - 0.5*zeta0_ + ZETA1_12 * zeta2_ - ZETA1_720 * zeta4_
def bernoulli_function_fast(zeta):
    fb_ = np.zeros_like(zeta)
    bernoulli_function_fill(zeta, fb_)
    return fb_

注意:如果您使用新版本的numba,您可以将这两者合并到同一个函数中。在

在我的机器上:

^{pr2}$

速度快4倍,易读。在

where命令在索引到一个数组中比较慢。这可能更快。在

fb_ = np.zeros_like(zeta)
nonZero= zeta > ZETA_TOLERANCE
zero = ~nonZero
fb_[zero] = function1(zeta[zero])
fb_[nonZero] = function2(zeta[nonZero])

编辑: 我意识到我的原始版本是复制同一个数组的两个副本。这个新版本应该快一点。在

相关问题 更多 >

    热门问题