是否有可能在Python中加速逐个元素的数组操作?

时间:2015-08-07 22:05:26

标签: python arrays performance

我有一个时间列表(在我的代码中称为时间,由我在线程astropy.io fits efficient element access of a large table中建议的代码生成)并且我想对周期性进行一些统计测试,使用Zn ^ 2和epoch折叠试验。代码中的一些步骤需要花费一段时间才能运行,我想知道是否有更快的方法来执行它。我已经尝试了等效的map和lambda函数,但这需要更长的时间。我的时间列表有几百或几千个元素,具体取决于数据集。这是我的代码:

phase=[(x-mintime)*testfreq[m]-int((x-mintime)*testfreq[m]) for x in times]
# the above step takes 3 seconds for the dataset I am using for testing
# testfreq[m] is just one of several hundred frequencies I am testing
# times is of type numpy.ndarray

phasebin=[int(ph*numbins)for ph in phase]
# 1 second (numbins is 20)

powerarray=[phasebin.count(n) for n in range(0,numbins-1)]
# 0.3 seconds

poweravg=np.mean(powerarray)
chisq[m]=sum([(pow-poweravg)**2/poweravg for pow in powerarray])
# the above 2 steps are very quick


for n in range(0,maxn):  # maxn is 3
    cosparam=sum([(np.cos(2*np.pi*(n+1)*ph)) for ph in phase])
    sinparam=sum([(np.sin(2*np.pi*(n+1)*ph)) for ph in phase])
    # these steps each take 4 seconds

    z2[m,n]=sum(z2[m,])+(cosparam**2+sinparam**2)/count
    # this is quick (count is the number of times)

当通过FFT搜索识别出频率两侧的数百个频率时,运行需要很长时间。低级语言中的相同功能运行得更快,但我需要一些Python模块用于绘图等。我希望Python可以被说服去做一些操作,特别是阶段,phasebin,powerarray,cosparam和sinparam计算,明显更快,但我不知道如何实现这一点。任何人都可以告诉我如何做到这一点,还是我必须用C或fortran编写和调用函数?我知道这可以在几分钟内完成,例如在fortran中,但是这个Python代码需要几个小时。

非常感谢。

2 个答案:

答案 0 :(得分:5)

您可以使用numpy库而不是Python列表,它对于线性代数类型操作来说要快得多。例如,以元素方式添加两个数组

>>> import numpy as np
>>> a = np.array([1,2,3,4,5])
>>> b = np.array([2,3,4,5,6])
>>> a + b
array([ 3,  5,  7,  9, 11])

同样,您可以通过标量乘以数组,这些标量会将每个元素与您期望的相乘

>>> 2 * a
array([ 2,  4,  6,  8, 10])

就速度而言,这里是Python列表相当于添加两个列表

>>> c = [1,2,3,4,5]
>>> d = [2,3,4,5,6]
>>> [i+j for i,j in zip(c,d)]
[3, 5, 7, 9, 11]

然后计时两个

>>> from timeit import timeit

>>> setup = '''
import numpy as np
a = np.array([1,2,3,4,5])
b = np.array([2,3,4,5,6])'''
>>> timeit('a+b', setup)
0.521275608325351

>>> setup = '''
c = [1,2,3,4,5]
d = [2,3,4,5,6]'''
>>> timeit('[i+j for i,j in zip(c,d)]', setup)
1.2781205834379108

在这个小例子中,numpy的速度提高了两倍多。

答案 1 :(得分:1)

for循环替换 - 在完整数组上运行

首先使用broadcasting

phase乘以2 * pi * n
phase = np.arange(10)
maxn = 3
ens = np.arange(1, maxn+1) # array([1, 2, 3])
two_pi_ens = 2*np.pi*ens
b = phase * two_pi_ens[:, np.newaxis]
每个b.shape

值的

range(1, maxn)是(3,10)一个

取余弦然后求和得到三个余弦参数

c = np.cos(b)
c_param = c.sum(axis = 1)   # c_param.shape is 3

取正弦然后求和得到三个正弦参数

s = np.sin(b)
s_param = s.sum(axis = 1)   # s_param.shape is 3

平方和除以计数

d = (np.square(c_param) + np.square(s_param)) / count
# d.shape is (3,)

分配到z2

for n in range(maxn):
    z2[m,n] = z2[m,:].sum() + d[n]

该循环正在进行累计求和。 numpy ndarrays有一个cumsum方法。 如果maxn很小(在您的情况下为3),则可能不会明显加快。

z2[m,:] += d
z2[m,:].cumsum(out = z2[m,:])

举例说明:

>>> a = np.ones((3,3))
>>> a
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
>>> m = 1
>>> d = (1,2,3)
>>> a[m,:] += d
>>> a
array([[ 1.,  1.,  1.],
       [ 2.,  3.,  4.],
       [ 1.,  1.,  1.]])
>>> a[m,:].cumsum(out = a[m,:])
array([ 2.,  5.,  9.])
>>> a
array([[ 1.,  1.,  1.],
       [ 2.,  5.,  9.],
       [ 1.,  1.,  1.]])
>>>