Более питонический способ перебора в Numpy - PullRequest
3 голосов
/ 26 декабря 2011

Я студент инженерного факультета, и я привык писать код на Фортране, но сейчас я пытаюсь больше узнать о Python для своих числовых рецептов, используя Numpy.

Если мне нужно было выполнить вычисленияПри многократном использовании элементов из нескольких массивов непосредственный перевод того, что я написал бы на Фортране, был бы

k = np.zeros(N, dtype=np.float)
u = ...
M = ...
r = ...
for i in xrange(N):
  k[i] = ... # Something with u[i], M[i], r[i] and r[i - 1], for example

Но мне было интересно, является ли этот способ более питоническим или предпочтительным в любом случае:

for i, (k_i, u_i, M_i, r_i) in enumerate(zip(k, u, M, r)):
  k_i = ... # Something with u_i, M_i, r_i and r[i - 1]

Благодаря перечислению у меня есть индекс, в противном случае, если он мне не нужен, я могу использовать просто zip или itertools.izip.

Есть идеи?Как код влияет на производительность?Есть ли другой способ сделать это?

Ответы [ 2 ]

7 голосов
/ 26 декабря 2011

Почти все операции с клочками выполняются поэлементно.Поэтому вместо написания явного цикла попробуйте определить k, используя формулу на основе массива:

r_shifted = np.roll(x, shift = 1)
k = ... # some formula in terms of u, M, r, r_shifted

Например, вместо

import numpy as np

N=5
k = np.zeros(N, dtype=np.float)
u = np.ones(N, dtype=np.float)
M = np.ones(N, dtype=np.float)
r = np.ones(N, dtype=np.float)
for i in xrange(N):
  k[i] = u[i] + M[i] + r[i] + r[i-1]
print(k)  
# [ 4.  4.  4.  4.  4.]

используйте:

r_shifted = np.roll(r, shift = 1)
k = u + M + r + r_shifted
print(k)
# [ 4.  4.  4.  4.  4.]

np.roll (r, shift = 1) возвращает новый массив того же размера, что и r, с r_shifted[i] = r[i-1] для i = 0, ..., N-1.

In [31]: x = np.arange(5)

In [32]: x
Out[32]: array([0, 1, 2, 3, 4])

In [33]: np.roll(x, shift = 1)
Out[33]: array([4, 0, 1, 2, 3])

Для создания копии, подобной этой, требуется больше памяти (того же размера, что и r), но она позволяет выполнять быстрые операции с пустыми текстами вместо медленного цикла Python.


Иногда формулавместо k может быть определено в терминах r[:-1] и r[1:].Примечание r[:-1] и r[1:] - это кусочки r и одинаковой формы.В этом случае вам не нужна дополнительная память, поскольку базовые фрагменты r - это так называемые представления из r, а не копии.

Я не определил k таким образом в приведенном выше примере, потому что тогда k имел бы длину N-1 вместо N, так что это немного отличалось бы от того, что мог бы создать ваш исходный код.

0 голосов
/ 26 декабря 2011

Мне нравятся списочные понимания

k = [ x ** y for x, y in zip(some_array, some_other_array) ]

другие, как map

map( lambda x, y : x*y , zip(some_array, some_other_array) )

умножит два массива и вернет вам список или генератор. (Конечно, у вас есть другие способы выполнения этой конкретной задачи в numpy.) Если вы хотите преобразовать ее обратно в массив, вы можете сделать

k = массив ([x ** y для x, y в zip (some_array, some_other_array)])

...