web-dev-qa-db-fra.com

Itération sur une dimension arbitraire de numpy.array

Existe-t-il une fonction pour obtenir un itérateur sur une dimension arbitraire d'un tableau numpy?

Itérer sur la première dimension est facile ...

In [63]: c = numpy.arange(24).reshape(2,3,4)

In [64]: for r in c :
   ....:     print r
   ....: 
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]

Mais itérer sur d'autres dimensions est plus difficile. Par exemple, la dernière dimension:

In [73]: for r in c.swapaxes(2,0).swapaxes(1,2) :
   ....:     print r
   ....: 
[[ 0  4  8]
 [12 16 20]]
[[ 1  5  9]
 [13 17 21]]
[[ 2  6 10]
 [14 18 22]]
[[ 3  7 11]
 [15 19 23]]

Je crée un générateur pour le faire moi-même, mais je suis surpris qu'il n'y ait pas de fonction nommée quelque chose comme numpy.ndarray.iterdim (axis = 0) pour le faire automatiquement.

53
AFoglia

Ce que vous proposez est assez rapide, mais la lisibilité peut être améliorée avec des formes plus claires:

for i in range(c.shape[-1]):
    print c[:,:,i]

ou mieux (plus rapide, plus général et plus explicite):

for i in range(c.shape[-1]):
    print c[...,i]

Cependant, la première approche ci-dessus semble être environ deux fois plus lente que l'approche swapaxes():

python -m timeit -s 'import numpy; c = numpy.arange(24).reshape(2,3,4)' \
    'for r in c.swapaxes(2,0).swapaxes(1,2): u = r'
100000 loops, best of 3: 3.69 usec per loop

python -m timeit -s 'import numpy; c = numpy.arange(24).reshape(2,3,4)' \
    'for i in range(c.shape[-1]): u = c[:,:,i]'
100000 loops, best of 3: 6.08 usec per loop

python -m timeit -s 'import numpy; c = numpy.arange(24).reshape(2,3,4)' \
    'for r in numpy.rollaxis(c, 2): u = r'
100000 loops, best of 3: 6.46 usec per loop

Je suppose que cela est dû au fait que swapaxes() ne copie aucune donnée, et parce que la gestion de c[:,:,i] Peut être effectuée via du code général (qui gère le cas où : Est remplacé par une tranche plus compliquée).

Notez cependant que la deuxième solution plus explicite c[...,i] Est à la fois assez lisible et assez rapide:

python -m timeit -s 'import numpy; c = numpy.arange(24).reshape(2,3,4)' \
    'for i in range(c.shape[-1]): u = c[...,i]'
100000 loops, best of 3: 4.74 usec per loop
49
Eric O Lebigot

J'utiliserais ce qui suit:

c = numpy.arange(2 * 3 * 4)
c.shape = (2, 3, 4)

for r in numpy.rollaxis(c, 2):
    print(r)

La fonction rollaxis crée une nouvelle vue sur le tableau. Dans ce cas, il déplace l'axe 2 vers l'avant, équivalent à l'opération c.transpose(2, 0, 1).

24
Eryk Sun

Ainsi, on peut facilement parcourir la première dimension, comme vous l'avez montré. Une autre façon de le faire pour une dimension arbitraire consiste à utiliser numpy.rollaxis () pour amener la dimension donnée à la première (le comportement par défaut), puis à utiliser le tableau renvoyé (qui est une vue, donc c'est rapide) comme itérateur .

In [1]: array = numpy.arange(24).reshape(2,3,4)

In [2]: for array_slice in np.rollaxis(array, 1):
   ....:     print array_slice.shape
   ....:
(2, 4)
(2, 4)
(2, 4)

EDIT: Je commenterai que j'ai soumis un PR à numpy pour adresser ceci ici: https://github.com/numpy/numpy/pull/3262 . Le consensus était que cela ne suffisait pas à ajouter à la base de code numpy. Je pense que l'utilisation de np.rollaxis est la meilleure façon de le faire, et si vous voulez un interateur, enveloppez-le dans iter ().

7
giessel

Je suppose qu'il n'y a pas de fonction. Lorsque j'ai écrit ma fonction, j'ai fini par prendre l'itération EOL également suggérée. Pour les futurs lecteurs, le voici:

def iterdim(a, axis=0) :
  a = numpy.asarray(a);
  leading_indices = (slice(None),)*axis
  for i in xrange(a.shape[axis]) :
    yield a[leading_indices+(i,)]
4
AFoglia

Vous pouvez utiliser numpy.shape pour obtenir les dimensions, puis varier pour les parcourir.

n0, n1, n2 = numpy.shape(c)

for r in range(n0):
    print(c[r,:,:])
0
rbfontana