Количество итераторов в зависимости от переменной с np.nditer - PullRequest
0 голосов
/ 30 июня 2019

Мне приходится перебирать массивы, число которых может быть разным на каждой итерации программы. Цель состоит в том, чтобы вычислить дисперсию между одним и тем же элементом этих массивов. Вот код, который я написал:

for a,s,m  in np.nditer([cost_surfaceS[i].ravel() for i in range(0,len(cost_surfaceS),1)]):
arr = [a, s, m]
    if(float(arr[0]) != float("-inf")):
        variance = np.var(arr,dtype = np.float32)
        variances.append(variance)
    else:
        variances.append(float("-inf"))

где cost_surfaceS - это список, который содержит все мои массивы. Проблема заключается в том, что этот фрагмент кода работает для 3 созданных numy массивов, и я хочу изменить «a, s, m», чтобы иметь число итераторов, которое зависит от числа созданных numpy массивов (т. Е. len(cost_surfaceS)) , Это способ сделать это?

Ответы [ 2 ]

1 голос
/ 30 июня 2019

Я согласен с @Bickknght, что распаковка не нужна. Не используйте unpacking при работе с неизвестным или переменным числом элементов.

In [57]: alist = [np.arange(10), np.arange(10,20), np.arange(20,30)]                                            

Составление списка массивов, где нам не нужны ravel.

In [58]: for arr  in np.nditer(alist): 
    ...:     print(arr) 
    ...:                                                                                                        
(array(0), array(10), array(20))
(array(1), array(11), array(21))
(array(2), array(12), array(22))
(array(3), array(13), array(23))
(array(4), array(14), array(24))
(array(5), array(15), array(25))
(array(6), array(16), array(26))
(array(7), array(17), array(27))
(array(8), array(18), array(28))
(array(9), array(19), array(29))

Сравните это с прямой итерацией списка zip:

In [59]: for arr in zip(*alist): 
    ...:     print(arr) 
    ...:                                                                                                        
(0, 10, 20)
(1, 11, 21)
(2, 12, 22)
(3, 13, 23)
(4, 14, 24)
(5, 15, 25)
(6, 16, 26)
(7, 17, 27)
(8, 18, 28)
(9, 19, 29)

Разница в том, что nditer создает 0d-массивы, а не скаляры. Таким образом, элементы имеют форму ((0,)) и dtype. Или в некоторых случаях, когда вы хотите изменить массивы (но они должны быть определены как read/write. В противном случае nditer не дает никаких реальных преимуществ.

In [62]: %%timeit 
    ...: ll = [] 
    ...: for arr  in np.nditer(alist): 
    ...:     ll.append(np.var(arr)) 
    ...:                                                                                                        
539 µs ± 17.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [63]: %%timeit 
    ...: ll = [] 
    ...: for arr  in zip(*alist): 
    ...:     ll.append(np.var(arr)) 
    ...:                                                                                                        
524 µs ± 3.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Если вы сможете избежать петель уровня Python, все будет намного быстрее:

In [65]: np.stack(alist,1)                                                                                      
Out[65]: 
array([[ 0, 10, 20],
       [ 1, 11, 21],
       [ 2, 12, 22],
       [ 3, 13, 23],
       [ 4, 14, 24],
       [ 5, 15, 25],
       [ 6, 16, 26],
       [ 7, 17, 27],
       [ 8, 18, 28],
       [ 9, 19, 29]])
In [66]: np.var(np.stack(alist,1),axis=1)                                                                       
Out[66]: 
array([66.66666667, 66.66666667, 66.66666667, 66.66666667, 66.66666667,
       66.66666667, 66.66666667, 66.66666667, 66.66666667, 66.66666667])
In [67]: timeit np.var(np.stack(alist,1),axis=1)                                                                
66.7 µs ± 1.47 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Я не пытался проверить на -inf.

===

Еще одно важное отличие от nditer. Он выполняет итерацию по всем элементам в прямом смысле - в действительности это делает ravel:

Составьте список 2d массивов.

In [81]: alist = [np.arange(10.).reshape(2,5), np.arange(10,20.).reshape(2,5), np.arange(20,30.).reshape(2,5)]  

Итерация PLain работает с первым измерением - в данном случае это 2, поэтому элементы с молнией - это массивы:

In [82]: for arr in zip(*alist): 
    ...:     print(arr) 
    ...:                                                                                                        
(array([0., 1., 2., 3., 4.]), array([10., 11., 12., 13., 14.]), array([20., 21., 22., 23., 24.]))
(array([5., 6., 7., 8., 9.]), array([15., 16., 17., 18., 19.]), array([25., 26., 27., 28., 29.]))

nditer генерирует те же кортежи, что и в случае с массивом 1d. В некоторых случаях это нормально, но трудно избежать, если вы этого не хотите.

In [83]: for arr  in np.nditer(alist): 
    ...:     print(arr) 
    ...:                                                                                                        
(array(0.), array(10.), array(20.))
(array(1.), array(11.), array(21.))
(array(2.), array(12.), array(22.))
(array(3.), array(13.), array(23.))
(array(4.), array(14.), array(24.))
(array(5.), array(15.), array(25.))
(array(6.), array(16.), array(26.))
(array(7.), array(17.), array(27.))
(array(8.), array(18.), array(28.))
(array(9.), array(19.), array(29.))
0 голосов
/ 09 июля 2019

Функция zip здесь является решением, как объясняет @hpaulj.Для работы с 2d-массивами вместо 1d просто необходимо дважды использовать эту функцию, как показано в следующем коде:

variances = []
for arr in zip(*cost_surfaceS):
    for element in zip(*arr):
        if(float("-inf") not in element):
            variance = np.var(element, dtype=np.float32)
            variances.append(variance)
        else:
            variances.append(float("-inf"))

Значения -inf обрабатываются условием if, позволяющим избежать вычисления дисперсии массивов, содержащейхотя бы одно значение бесконечности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...