如何在numpy

时间:2017-04-10 11:19:36

标签: python numpy fft vectorization series

给定具有周期a[n]b[n]的函数的傅立叶级数系数​​Tt(分别对于余弦和正弦),等距间隔,以下代码将评估区间t中所有点的部分和(abt都是numpy个数组。澄清len(t)<> LEN(a)所示。

yn=ones(len(t))*a[0]
for n in range(1,len(a)):
    yn=yn+(a[n]*cos(2*pi*n*t/T)-b[n]*sin(2*pi*n*t/T))

我的问题是:这个for循环可以被矢量化吗?

3 个答案:

答案 0 :(得分:2)

这是一种矢量化方法,使用broadcasting创建余弦/正弦输入的2D数组版本:2*pi*n*t/T,然后使用matrix-multiplication和{sum-reduction 3}}用于r = np.arange(1,len(a)) S = 2*np.pi*r[:,None]*t/T cS = np.cos(S) sS = np.sin(S) out = a[1:].dot(cS) - b[1:].dot(sS) + a[0] -

import numexpr as ne
cS = ne.evaluate('cos(S)')
sS = ne.evaluate('sin(S)')

进一步提升绩效

为了进一步提升,我们可以利用np.dot来计算这些三角形步骤 -

def original_app(t,a,b,T):
    yn=np.ones(len(t))*a[0]
    for n in range(1,len(a)):
        yn=yn+(a[n]*np.cos(2*np.pi*n*t/T)-b[n]*np.sin(2*np.pi*n*t/T))
    return yn

def vectorized_app(t,a,b,T):    
    r = np.arange(1,len(a))
    S = (2*np.pi/T)*r[:,None]*t
    cS = np.cos(S)
    sS = np.sin(S)
    return a[1:].dot(cS) - b[1:].dot(sS) + a[0]

def vectorized_app_v2(t,a,b,T):    
    r = np.arange(1,len(a))
    S = (2*np.pi/T)*r[:,None]*t
    cS = ne.evaluate('cos(S)')
    sS = ne.evaluate('sin(S)')
    return a[1:].dot(cS) - b[1:].dot(sS) + a[0]

运行时测试 -

方法 -

PP

此外,还包括来自@Paul Panzer的帖子中的函数In [22]: # Setup inputs ...: n = 10000 ...: t = np.random.randint(0,9,(n)) ...: a = np.random.randint(0,9,(n)) ...: b = np.random.randint(0,9,(n)) ...: T = 3.45 ...: In [23]: print np.allclose(original_app(t,a,b,T), vectorized_app(t,a,b,T)) ...: print np.allclose(original_app(t,a,b,T), vectorized_app_v2(t,a,b,T)) ...: print np.allclose(original_app(t,a,b,T), PP(t,a,b,T)) ...: True True True In [25]: %timeit original_app(t,a,b,T) ...: %timeit vectorized_app(t,a,b,T) ...: %timeit vectorized_app_v2(t,a,b,T) ...: %timeit PP(t,a,b,T) ...: 1 loops, best of 3: 6.49 s per loop 1 loops, best of 3: 6.24 s per loop 1 loops, best of 3: 1.54 s per loop 1 loops, best of 3: 1.96 s per loop

计时 -

{{1}}

答案 1 :(得分:2)

无法击败numexpr,但如果它不可用,我们可以节省超越性(基于@Divakar的代码严重测试和基准测试代码,以防您没有注意到;-)):

import numpy as np
from timeit import timeit

def PP(t,a,b,T):
    CS = np.empty((len(t), len(a)-1), np.complex)
    CS[...] = np.exp(2j*np.pi*(t[:, None])/T)
    np.cumprod(CS, axis=-1, out=CS)
    return a[1:].dot(CS.T.real) - b[1:].dot(CS.T.imag) + a[0]

def original_app(t,a,b,T):
    yn=np.ones(len(t))*a[0]
    for n in range(1,len(a)):
        yn=yn+(a[n]*np.cos(2*np.pi*n*t/T)-b[n]*np.sin(2*np.pi*n*t/T))
    return yn

def vectorized_app(t,a,b,T):    
    r = np.arange(1,len(a))
    S = 2*np.pi*r[:,None]*t/T
    cS = np.cos(S)
    sS = np.sin(S)
    return a[1:].dot(cS) - b[1:].dot(sS) + a[0]

n = 1000
t = 2000
t = np.random.randint(0,9,(t))
a = np.random.randint(0,9,(n))
b = np.random.randint(0,9,(n))
T = 3.45


print(np.allclose(original_app(t,a,b,T), vectorized_app(t,a,b,T)))
print(np.allclose(original_app(t,a,b,T), PP(t,a,b,T)))

print('{:18s} {:9.6f}'.format('orig', timeit(lambda: original_app(t,a,b,T), number=10)/10))
print('{:18s} {:9.6f}'.format('Divakar no numexpr', timeit(lambda: vectorized_app(t,a,b,T), number=10)/10))
print('{:18s} {:9.6f}'.format('PP', timeit(lambda: PP(t,a,b,T), number=10)/10))

打印:

True
True
orig                0.166903
Divakar no numexpr  0.179617
PP                  0.060817

顺便说一下。如果delta t除以T,则可以节省更多,甚至可以运行完整的fft并丢弃过多的内容。

答案 2 :(得分:0)

这不是另一个答案,而是对@Paul Panzer的评论,作为答案而写,因为我需要发布一些代码。如果有办法在评论中发布格式正确的代码,请提供建议。

受到@Paul Panzer cumprod想法的启发,我想出了以下内容:

an = ones((len(a)-1,len(te)))*2j*pi*te/T
CS = exp(cumsum(an,axis=0))
out = (a[1:].dot(CS.real) - b[1:].dot(CS.imag)) + a[0]

虽然它似乎适当地矢量化并产生正确的结果,但其性能很糟糕。它不仅比cumprod慢得多,len(a)-1预计会更多地进行SELECT LAST_INSERT_ID() as last_id 指数化,但比原始非计算版本慢50%。造成这种糟糕表现的原因是什么?