使用Python,Numpy更快的方法吗?

时间:2013-10-24 20:31:26

标签: python numpy

我在Python中有一个函数。我想让它快得多吗?有人有任何提示吗?

def garchModel(e2, omega=0.01, beta=0.1, gamma=0.8 ):

    sigma    = np.empty( len( e2 ) )
    sigma[0] = omega

    for i in np.arange(  1, len(e2) ):

        sigma[i] = omega + beta * sigma[ i-1 ] + gamma * e2[ i-1 ]

    return sigma

5 个答案:

答案 0 :(得分:5)

以下代码有效,但是有太多的诡计在继续,我不确定它是否依赖于最终可能崩溃的一些未记录的实现细节:

from numpy.lib.stride_tricks import as_strided
from numpy.core.umath_tests import inner1d

def garch_model(e2, omega=0.01, beta=0.1, gamma=0.8):
    n = len(e2)
    sigma = np.empty((n,))
    sigma[:] = omega
    sigma[1:] += gamma * e2[:-1]
    sigma_view = as_strided(sigma, shape=(n-1, 2), strides=sigma.strides*2)
    inner1d(sigma_view, [beta, 1], out=sigma[1:])
    return sigma

In [75]: e2 = np.random.rand(1e6)

In [76]: np.allclose(garchModel(e2), garch_model(e2))
Out[76]: True

In [77]: %timeit garchModel(e2)
1 loops, best of 3: 6.93 s per loop

In [78]: %timeit garch_model(e2)
100 loops, best of 3: 17.5 ms per loop

答案 1 :(得分:3)

我尝试过使用Numba,我的数据集提供了200倍的改进!

感谢上述所有建议,但我不能让他们给我正确答案。我会尝试阅读有关线性滤波器的信息,但现在是星期五晚上,我有点太累了,不能再接收信息了。

from numba import autojit
@autojit
def garchModel2(e2, beta=0.1, gamma=0.8, omega=0.01, ):

    sigma    = np.empty( len( e2 ) )
    sigma[0] = omega

    for i in range(  1, len(e2) ):
        sigma[i] = omega + beta * sigma[ i-1 ] + gamma * e2[ i-1 ]

    return sigma

答案 2 :(得分:2)

这是基于@ stx2的想法的解决方案。一个潜在的问题是当beta**N变大时N可能导致浮点溢出(与cumprod相同)。

>>> def garchModel2(e2, omega=0.01, beta=0.1, gamma=0.8):
    wt0=cumprod(array([beta,]*(len(e2)-1)))
    wt1=cumsum(hstack((0.,wt0)))+1
    wt2=hstack((wt0[::-1], 1.))*gamma
    wt3=hstack((1, wt0))[::-1]*beta
    pt1=hstack((0.,(array(e2)*wt2)[:-1]))
    pt2=wt1*omega
    return cumsum(pt1)/wt3+pt2

>>> garchModel([1,2,3,4,5])
array([ 0.01    ,  0.811   ,  1.6911  ,  2.57911 ,  3.467911])  
>>> garchModel2([1,2,3,4,5])
array([ 0.01    ,  0.811   ,  1.6911  ,  2.57911 ,  3.467911])
>>> f1=lambda: garchModel2(range(5)) 
>>> f=lambda: garchModel(range(5))
>>> T=timeit.Timer('f()', 'from __main__ import f')
>>> T1=timeit.Timer('f1()', 'from __main__ import f1')
>>> T.timeit(1000)
0.01588106868331031
>>> T1.timeit(1000) #When e2 dimension is samll, garchModel2 is slower
0.04536693909403766
>>> f1=lambda: garchModel2(range(10000))
>>> f=lambda: garchModel(range(10000))
>>> T.timeit(1000)
35.745981961394534
>>> T1.timeit(1000) #When e2 dimension is large, garchModel2 is faster
1.7330512676890066
>>> f1=lambda: garchModel2(range(1000000))
>>> f=lambda: garchModel(range(1000000))
>>> T.timeit(50)
167.33835501439427
>>> T1.timeit(50) #The difference is even bigger.
8.587259274572716

我没有使用beta**N而是使用cumprod**可能会减慢它的速度。

答案 3 :(得分:2)

您的计算是序列omega + gamma*e2的线性过滤器,因此您可以使用scipy.signal.lfilter。这是计算的一个版本,通过适当调整初始条件和过滤器输入来生成与garchModel相同的输出:

import numpy as np
from scipy.signal import lfilter

def garch_lfilter(e2, omega=0.01, beta=0.1, gamma=0.8):
    # Linear filter coefficients:
    b = [1]
    a = [1, -beta]

    # Initial condition for the filter:
    zi = np.array([beta*omega])

    # Preallocate the output array, and set the first value to omega:
    sigma = np.empty(len(e2))
    sigma[0] = omega

    # Apply the filter to omega + gamma*e2[:-1]
    sigma[1:], zo = lfilter(b, a, omega + gamma*e2[:-1], zi=zi)

    return sigma

验证它与@ Jaime函数的结果相同:

In [6]: e2 = np.random.rand(1e6)

In [7]: np.allclose(garch_model(e2), garch_lfilter(e2))
Out[7]: True

它比garchModel快很多,但没有@ Jaime的功能快。

@ Jaime garch_model的时间安排:

In [8]: %timeit garch_model(e2)
10 loops, best of 3: 21.6 ms per loop

garch_lfilter的时间安排:

In [9]: %timeit garch_lfilter(e2)
10 loops, best of 3: 26.8 ms per loop

答案 4 :(得分:1)

正如@jaime所示,有一种方法。但是,我不知道是否有办法重写函数,使其更快,保持简单。

另一种方法是使用优化“魔法”,例如cython or numba