En realidad, hay una forma aún más eficiente de hacerlo ... La desventaja de usar vstack
, etc., es que está haciendo una copia de la matriz.
Por cierto, este es efectivamente idéntico al @ respuesta de Pablo, pero Quiero poner esto sólo para explicar las cosas en un poco más de detalle ...
Hay una manera de hacer esto con sólo puntos de vista de manera que no memoria está duplicada.
Estoy tomando prestado directamente de Erik Rigtorp's post to numpy-discussion, que a su vez, lo prestó de Bottleneck de Keith Goodman (¡lo cual es bastante útil!).
El truco básico es que manipular directamente (arrays para unidimensionales) strides of the array:
import numpy as np
def rolling(a, window):
shape = (a.size - window + 1, window)
strides = (a.itemsize, a.itemsize)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
a = np.arange(10)
print rolling(a, 3)
Dónde a
es su matriz de entrada y window
es la longitud de la ventana que desea (3, en su caso).
Este rendimientos:
[[0 1 2]
[1 2 3]
[2 3 4]
[3 4 5]
[4 5 6]
[5 6 7]
[6 7 8]
[7 8 9]]
Sin embargo, no hay absolutamente ninguna duplicación de memoria entre el original a
y la matriz devuelta. Esto significa que es rápido y escala mucho mejor que otras opciones.
Por ejemplo (usando a = np.arange(100000)
y window=3
):
%timeit np.vstack([a[i:i-window] for i in xrange(window)]).T
1000 loops, best of 3: 256 us per loop
%timeit rolling(a, window)
100000 loops, best of 3: 12 us per loop
Si generalizamos esta a una "ventana móvil" a lo largo del último eje para una matriz de N dimensiones, obtenemos la función "ventana móvil" de Erik Rigtorp :
import numpy as np
def rolling_window(a, window):
"""
Make an ndarray with a rolling window of the last dimension
Parameters
----------
a : array_like
Array to add rolling window to
window : int
Size of rolling window
Returns
-------
Array that is a view of the original array with a added dimension
of size w.
Examples
--------
>>> x=np.arange(10).reshape((2,5))
>>> rolling_window(x, 3)
array([[[0, 1, 2], [1, 2, 3], [2, 3, 4]],
[[5, 6, 7], [6, 7, 8], [7, 8, 9]]])
Calculate rolling mean of last dimension:
>>> np.mean(rolling_window(x, 3), -1)
array([[ 1., 2., 3.],
[ 6., 7., 8.]])
"""
if window < 1:
raise ValueError, "`window` must be at least 1."
if window > a.shape[-1]:
raise ValueError, "`window` is too long."
shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
strides = a.strides + (a.strides[-1],)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
lo tanto, vamos a ver en lo que está pasando aquí ... la manipulación puede parecer un poco mágica de strides
una matriz, pero una vez que entienda lo que está pasando, no es en absoluto. Los pasos de una matriz numpy describen el tamaño en bytes de los pasos que se deben seguir para incrementar un valor a lo largo de un eje dado. Por lo tanto, en el caso de una matriz unidimensional de flotantes de 64 bits, la longitud de cada elemento es de 8 bytes, y x.strides
es (8,)
.
x = np.arange(9)
print x.strides
Ahora bien, si formar de nuevo este en una 2D, matriz de 3x3, los pasos serán (3 * 8, 8)
, ya que habría que saltar 24 bytes para incrementar un paso a lo largo del primer eje, y 8 bytes para incrementar un paso a lo largo del segundo eje.
y = x.reshape(3,3)
print y.strides
mismo modo una transposición es lo mismo que simplemente invirtiendo los pasos de una matriz:
print y
y.strides = y.strides[::-1]
print y
Claramente, los pasos de una matriz y la forma de una matriz están íntimamente ligados. Si cambiamos uno, tenemos que cambiar el otro en consecuencia, de lo contrario no tendremos una descripción válida del búfer de memoria que realmente contiene los valores de la matriz.
Por lo tanto, si desea cambiar tanto la forma y el tamaño de una matriz al mismo tiempo, no puede hacerlo sólo mediante el establecimiento de x.strides
y x.shape
, incluso si los nuevos avances y la forma son compatibles.
Aquí es donde entra numpy.lib.as_strided
. En realidad, es una función muy simple que simplemente establece los pasos y la forma de una matriz al mismo tiempo.
Comprueba que los dos son compatibles, pero no que los pasos anteriores y la nueva forma sean compatibles, como sucedería si establece los dos de forma independiente. (De hecho, lo hace a través de numpy's __array_interface__
, que permite que clases arbitrarias describan un búfer de memoria como una matriz numpy.)
Así que, todo lo que hemos hecho se hace de modo que pasos un artículo adelante (8 bytes en el caso de una matriz de 64 bits) a lo largo de un eje, pero también solo pasos 8 bytes hacia adelante a lo largo del otro eje.
En otras palabras, en caso de un tamaño de "ventana" de 3, la matriz tiene una forma de (whatever, 3)
, pero en lugar de pisar un completo 3 * x.itemsize
para la segunda dimensión, se sólo unos pasos un elemento hacia adelante, haciendo efectivamente las filas de la nueva matriz una vista de "ventana móvil" en la matriz original.
(Esto también significa que x.shape[0] * x.shape[1]
no será el mismo que x.size
para su nueva matriz.)
En cualquier caso, es de esperar que hace las cosas un poco más claro ..
¿Puedes publicar tu solución vstack/loop? – eumiro
@wxbx: ¿Por favor, elabore más lo que pretende hacer? Tenga en cuenta que 'B = array ([1,2,3], [2,3,4], [3,4,5], [4,5,6])' no es válido 'numpy'! – eat
@wxbx: su solución es realmente desafortunada. Usted 'vstack' la matriz 10000 veces! Ver mi respuesta, lo "apilaré" una sola vez. – eumiro