¿Hay función para obtener un iterador sobre una dimensión arbitraria de una matriz numpy?

Iterar sobre la primera dimensión es fácil ...

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]]

Pero iterar sobre otras dimensiones es más difícil. Por ejemplo, la última dimensión:

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]]

Estoy haciendo un generador para hacer esto yo mismo, pero me sorprende que no haya una función llamada algo como numpy.ndarray.iterdim (axis = 0) para hacer esto automáticamente.

63
AFoglia 19 oct. 2009 a las 20:27

5 respuestas

La mejor respuesta

Lo que propone es bastante rápido, pero la legibilidad se puede mejorar con formas más claras:

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

O mejor (más rápido, más general y más explícito):

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

Sin embargo, el primer enfoque anterior parece ser aproximadamente el doble de lento que el enfoque 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

Supongo que esto se debe a que swapaxes() no copia ningún dato, y porque el manejo de c[:,:,i] podría hacerse a través de un código general (que maneja el caso donde : se reemplaza por un más rebanada complicada).

Sin embargo, tenga en cuenta que la segunda solución más explícita c[...,i] es bastante legible y bastante rápida:

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
58
Eric O Lebigot 7 jul. 2017 a las 17:35

Puede usar numpy.shape para obtener dimensiones, y luego rango para iterar sobre ellas.

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

for r in range(n0):
    print(c[r,:,:])
2
rbfontana 8 ago. 2019 a las 16:39

Supongo que no hay función. Cuando escribí mi función, terminé tomando la iteración que EOL también sugirió. Para futuros lectores, aquí está:

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 20 oct. 2009 a las 14:10

Entonces, uno puede iterar sobre la primera dimensión fácilmente, como ha mostrado. Otra forma de hacer esto para una dimensión arbitraria es usar numpy.rollaxis () para llevar la dimensión dada a la primera (el comportamiento predeterminado), y luego usar la matriz devuelta (que es una vista, por lo que esto es rápido) como un iterador .

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)

EDITAR: comentaré que envié un PR a numpy para abordar esto aquí: https: // github .com / numpy / numpy / pull / 3262. El consenso fue que esto no fue suficiente para agregar a la base de código numpy. Creo que usar np.rollaxis es la mejor manera de hacer esto, y si quieres un interator, envuélvelo en iter ().

7
giessel 28 may. 2013 a las 19:48

Yo usaría lo siguiente:

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

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

La función rollaxis crea una nueva vista en la matriz. En este caso, está moviendo el eje 2 al frente, equivalente a la operación c.transpose(2, 0, 1).

28
Eryk Sun 7 may. 2011 a las 19:05