我在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
答案 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。