arrays - Uso de la función numpy `as_strided` para crear parches, mosaicos, ventanas corredizas o deslizantes de dimensión arbitraria
convolution (1)
Pasé un tiempo esta mañana buscando una pregunta generalizada a la que apuntar duplicados para preguntas sobre
as_strided
y / o
cómo hacer funciones de ventana generalizadas
.
Parece que hay muchas
questions
sobre cómo (de forma segura) crear parches, ventanas deslizantes, ventanas enrollables, mosaicos o vistas en una matriz para aprendizaje automático, convolución, procesamiento de imágenes y / o integración numérica.
Estoy buscando una función generalizada que pueda aceptar un parámetro de
window
,
step
y
axis
y devolver una vista
as_strided
para dimensiones arbitrarias.
np.squeeze()
mi respuesta a continuación, pero estoy interesado si alguien puede hacer un método más eficiente, ya que no estoy seguro de que usar
np.squeeze()
sea el mejor método, no estoy seguro de que mis declaraciones de afirmación hagan que la función sea segura suficiente para escribir en la vista resultante, y no estoy seguro de cómo manejar el caso de borde del
axis
no está en orden ascendente.
DEBIDA DILIGENCIA
La función más generalizada que puedo encontrar es
sklearn.feature_extraction.image.extract_patches
escrita por @eickenberg (así como el aparentemente equivalente
skimage.util.view_as_windows
), pero no están bien documentados en la red y no pueden hacer ventanas menos ejes de los que hay en la matriz original (por ejemplo,
esta pregunta
solicita una ventana de cierto tamaño sobre un solo eje).
También, a menudo, las preguntas quieren una respuesta única.
@Divakar creó una función
numpy
generalizada para las entradas 1-d
here
, pero las entradas de mayor dimensión requieren un poco más de cuidado.
He creado una
ventana 2D básica sobre el método de entrada 3D
, pero no es muy extensible.
Aquí está la receta que tengo hasta ahora:
def window_nd(a, window, steps = None, axis = None, outlist = False):
"""
Create a windowed view over `n`-dimensional input that uses an
`m`-dimensional window, with `m <= n`
Parameters
-------------
a : Array-like
The array to create the view on
window : tuple or int
If int, the size of the window in `axis`, or in all dimensions if
`axis == None`
If tuple, the shape of the desired window. `window.size` must be:
equal to `len(axis)` if `axis != None`, else
equal to `len(a.shape)`, or
1
steps : tuple, int or None
The offset between consecutive windows in desired dimension
If None, offset is one in all dimensions
If int, the offset for all windows over `axis`
If tuple, the steps along each `axis`.
`len(steps)` must me equal to `len(axis)`
axis : tuple, int or None
The axes over which to apply the window
If None, apply over all dimensions
if tuple or int, the dimensions over which to apply the window
outlist : boolean
If output should be as list of windows.
If False, it will be an array with
`a.nidim + 1 <= a_view.ndim <= a.ndim *2`.
If True, output is a list of arrays with `a_view[0].ndim = a.ndim`
Warning: this is a memory-intensive copy and not a view
Returns
-------
a_view : ndarray
A windowed view on the input array `a`, or copied list of windows
"""
ashp = np.array(a.shape)
if axis != None:
axs = np.array(axis, ndmin = 1)
assert np.all(np.in1d(axs, np.arange(ashp.size))), "Axes out of range"
else:
axs = np.arange(ashp.size)
window = np.array(window, ndmin = 1)
assert (window.size == axs.size) | (window.size == 1), "Window dims and axes don''t match"
wshp = ashp.copy()
wshp[axs] = window
assert np.all(wshp <= ashp), "Window is bigger than input array in axes"
stp = np.ones_like(ashp)
if steps:
steps = np.array(steps, ndmin = 1)
assert np.all(steps > 0), "Only positive steps allowed"
assert (steps.size == axs.size) | (steps.size == 1), "Steps and axes don''t match"
stp[axs] = steps
astr = np.array(a.strides)
shape = tuple((ashp - wshp) // stp + 1) + tuple(wshp)
strides = tuple(astr * stp) + tuple(astr)
as_strided = np.lib.stride_tricks.as_strided
a_view = np.squeeze(as_strided(a,
shape = shape,
strides = strides))
if outlist:
return list(a_view.reshape((-1,) + tuple(wshp)))
else:
return a_view
Algunos casos de prueba:
a = np.arange(1000).reshape(10,10,10)
window_nd(a, 4).shape # sliding (4x4x4) window
Out: (7, 7, 7, 4, 4, 4)
window_nd(a, 2, 2).shape # (2x2x2) blocks
Out: (5, 5, 5, 2, 2, 2)
window_nd(a, 2, 1, 0).shape # sliding window of width 2 over axis 0
Out: (9, 2, 10, 10)
window_nd(a, 2, 2, (0,1)).shape # tiled (2x2) windows over first and second axes
Out: (5, 5, 2, 2, 10)
window_nd(a,(4,3,2)).shape # arbitrary sliding window
Out: (7, 8, 9, 4, 3, 2)
window_nd(a,(4,3,2),(1,5,2),(0,2,1)).shape #arbitrary windows, steps and axis
Out: (7, 5, 2, 4, 2, 3) # note shape[-3:] != window as axes are out of order