array - python numpy slice
Rápido numpy imaginación de lujo (3)
Mi código para cortar una matriz numpy (a través de la indexación elegante) es muy lento. Actualmente es un cuello de botella en el programa.
a.shape
(3218, 6)
ts = time.time(); a[rows][:, cols]; te = time.time(); print(''%.8f'' % (te-ts));
0.00200009
¿Cuál es la llamada numpy correcta para obtener una matriz que consta del subconjunto de filas ''filas'' y columnas ''col'' de la matriz a? (de hecho, necesito la transposición de este resultado)
Para mi sorpresa, el tipo de expresión larga, que calcula los primeros índices 1D lineales, es más del 50% más rápida que la indexación consecutiva de matriz presentada en la pregunta:
(a.ravel()[(
cols + (rows * a.shape[1]).reshape((-1,1))
).ravel()]).reshape(rows.size, cols.size)
ACTUALIZACIÓN: OP actualizó la descripción de la forma de la matriz inicial. Con el tamaño actualizado, la aceleración está ahora por encima del 99% :
In [93]: a = np.random.randn(3218, 1415)
In [94]: rows = np.random.randint(a.shape[0], size=2000)
In [95]: cols = np.random.randint(a.shape[1], size=6)
In [96]: timeit a[rows][:, cols]
10 loops, best of 3: 186 ms per loop
In [97]: timeit (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
1000 loops, best of 3: 1.56 ms per loop
RESPUESTA INICIAL: Aquí está la transcripción:
In [79]: a = np.random.randn(3218, 6)
In [80]: a.shape
Out[80]: (3218, 6)
In [81]: rows = np.random.randint(a.shape[0], size=2000)
In [82]: cols = np.array([1,3,4,5])
Método de tiempo 1:
In [83]: timeit a[rows][:, cols]
1000 loops, best of 3: 1.26 ms per loop
Método de tiempo 2:
In [84]: timeit (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
1000 loops, best of 3: 568 us per loop
Verifique que los resultados sean los mismos:
In [85]: result1 = a[rows][:, cols]
In [86]: result2 = (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
In [87]: np.sum(result1 - result2)
Out[87]: 0.0
Puede obtener un poco de velocidad si corta usando indización y transmisión sofisticadas:
from __future__ import division
import numpy as np
def slice_1(a, rs, cs) :
return a[rs][:, cs]
def slice_2(a, rs, cs) :
return a[rs[:, None], cs]
>>> rows, cols = 3218, 6
>>> rs = np.unique(np.random.randint(0, rows, size=(rows//2,)))
>>> cs = np.unique(np.random.randint(0, cols, size=(cols//2,)))
>>> a = np.random.rand(rows, cols)
>>> import timeit
>>> print timeit.timeit(''slice_1(a, rs, cs)'',
''from __main__ import slice_1, a, rs, cs'',
number=1000)
0.24083110865
>>> print timeit.timeit(''slice_2(a, rs, cs)'',
''from __main__ import slice_2, a, rs, cs'',
number=1000)
0.206566124519
Si piensas en términos de porcentajes, hacer algo 15% más rápido siempre es bueno, pero en mi sistema, para el tamaño de tu matriz, esto nos está restando 40 usd para hacer el corte, y es difícil creer que una operación 240 nosotros serán tu cuello de botella.
Permítanme tratar de resumir las excelentes respuestas de Jaime y TheodrosZelleke y mezclar en algunos comentarios.
- La indexación avanzada (de lujo) siempre devuelve una copia, nunca una vista.
-
a[rows][:,cols]
implica dos operaciones de indexación sofisticadas, por lo que se crea y descarta una copia intermediaa[rows]
. Práctico y legible, pero no muy eficiente. Además, tenga en cuenta que[:,cols]
generalmente genera una copia contigua de Fortran a partir de una C-cont. fuente. -
a[rows.reshape(-1,1),cols]
es una única expresión de indexación avanzada que se basa en el hecho de querows.reshape(-1,1)
ycols
se transmiten a la forma del resultado previsto. Una experiencia común es que la indexación en una matriz aplanada puede ser más eficiente que la indexación de lujo, por lo que otro enfoque es
indx = rows.reshape(-1,1)*a.shape[1] + cols a.take(indx)
o
a.take(indx.flat).reshape(rows.size,cols.size)
La eficiencia dependerá de los patrones de acceso a la memoria y de si la matriz inicial es C-contorneada o Fortran continua, por lo que se necesita experimentación.
Utilice la indexación sofisticada solo si realmente lo necesita: el corte básico de
a[rstart:rstop:rstep, cstart:cstop:cstep]
devuelve una vista (aunque no continua) y debe ser más rápido.