2015-08-07 2 views
0

У меня есть список раз (называемый временем в моем коде, созданный кодом, предложенным мне в потоке astropy.io fits efficient element access of a large table), и я хочу сделать некоторые статистические тесты для периодичности, используя тесты Zn^2 и epok folding. Некоторые шаги в коде требуют довольно много времени для запуска, и мне интересно, есть ли более быстрый способ сделать это. Я пробовал эквивалентную карту и лямбда-функции, но это занимает еще больше времени. Мой список раз содержит несколько сотен или, может быть, тысячи элементов, в зависимости от набора данных. Вот мой код:Возможно ли ускорить работу элементарного элемента в Python?

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 можно убедить выполнить некоторые операции, в частности фазу, фазу, powerarray, cosparam , и вычисления sinparam, значительно быстрее, но я не уверен, как это сделать. Может ли кто-нибудь сказать мне, как это можно сделать, или мне нужно писать и вызывать функции в C или fortran? Я знаю, что это можно сделать за несколько минут, например. в fortran, но этот код Python занимает часы, как есть.

Большое спасибо.

+0

Похоже, '' 'phase''' может быть сделано в два этапа:' '' фаза = (раз-mintime) * testfreq [м]; phase = phase-phase.astype (np.intxx) '' '. '' 'phase''' тогда будет ndarray с той же формой, что и' '' times'''. – wwii

+0

Каждая итерация цикла for присваивает новое значение: '' 'cosparam''' и' '' sinparam''' - это не может быть то, что вы намеревались - они отражают только последнее значение '' 'n''' , – wwii

+0

Да, параметр z2 является важным в этом случае, и он правильно вычисляется из cosparam и sinparam и сохраняется в массиве, чтобы он мог быть сопоставлен с частотой (для каждого значения n) в конце скрипта. Параметры cosparam и sinparam должны быть рассчитаны для каждого значения n и для каждой частоты. Код корректен для расчетов. Я просто хотел бы знать, как заставить его работать быстрее. Благодарю. – dragoncat16

ответ

5

Вместо списков Python вы можете использовать библиотеку numpy, это намного быстрее для операций линейной алгебры. Например, чтобы добавить два массива в поэлементного моды

>>> 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 был более чем в два раза быстрее.петля может заменить

+0

Извините, я слишком часто использовал слово «список». «Список» раз имеет тип numpy.ndarray. Поэтому все производные объекты также относятся к этому типу. Интересно, есть ли код, который может ускорить его еще больше. – dragoncat16

+1

В этом случае, если у вас есть рабочий код, который вы хотите настроить для скорости, я бы рекомендовал перейти на [Обзор кода] (http://codereview.stackexchange.com), поскольку это больше их вид вопрос. – CoryKramer

+1

Я думаю (большинство) ваш код на самом деле использует списки, а не массивы numpy, потому что вы используете списки, которые составляют промежуточные списки. Чтобы получить максимальную отдачу от numpy, вам нужно использовать методы массива и векторизованные операции вместо циклов и понятий. – jpkotta

1

for - работает на полных массивах

phase Первый умножить на 2 * пи * п используя broadcasting

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 является (3,10) один строку для каждого значения range(1, maxn)

Возьмите косинус, затем суммируйте, чтобы получить три параметра косинуса

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.]]) 
>>> 
+0

Большое вам спасибо. Мне просто нужно было изменить последний бит, чтобы он работал как раньше: 'z2 [m, n] = sum (z2 [m,]) + d [n]' вместо добавления суммы в d (z2 начинается как все нули и второе значение z2 [m, 1] должно содержать z2 [m, 0] и т. д.). – dragoncat16

+0

Я должен добавить, что ваш код работает в 20 раз быстрее. Единственный недостаток - теперь у меня больше нет времени на перерыв, пока работает мой скрипт. – dragoncat16

+0

@MirandaJackson являются '' 'z2 [m, 0]' '', '' 'z2 [m, 1]' '' и '' 'z2 [m, 2]' '' изначально все ноль? – wwii

Смежные вопросы