Я согласен с @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.))