python - Ordenar invariante para numpy.argsort con mĂșltiples dimensiones
arrays sorting (3)
El problema numpy # 8708 tiene una implementación de muestra de take_along_axis que hace lo que necesito; No estoy seguro de si es eficiente para matrices grandes, pero parece funcionar.
def take_along_axis(arr, ind, axis):
"""
... here means a "pack" of dimensions, possibly empty
arr: array_like of shape (A..., M, B...)
source array
ind: array_like of shape (A..., K..., B...)
indices to take along each 1d slice of `arr`
axis: int
index of the axis with dimension M
out: array_like of shape (A..., K..., B...)
out[a..., k..., b...] = arr[a..., inds[a..., k..., b...], b...]
"""
if axis < 0:
if axis >= -arr.ndim:
axis += arr.ndim
else:
raise IndexError(''axis out of range'')
ind_shape = (1,) * ind.ndim
ins_ndim = ind.ndim - (arr.ndim - 1) #inserted dimensions
dest_dims = list(range(axis)) + [None] + list(range(axis+ins_ndim, ind.ndim))
# could also call np.ix_ here with some dummy arguments, then throw those results away
inds = []
for dim, n in zip(dest_dims, arr.shape):
if dim is None:
inds.append(ind)
else:
ind_shape_dim = ind_shape[:dim] + (-1,) + ind_shape[dim+1:]
inds.append(np.arange(n).reshape(ind_shape_dim))
return arr[tuple(inds)]
cuyos rendimientos
>>> A = np.array([[3,2,1],[4,0,6]])
>>> B = np.array([[3,1,4],[1,5,9]])
>>> i = A.argsort(axis=-1)
>>> take_along_axis(A,i,axis=-1)
array([[1, 2, 3],
[0, 4, 6]])
>>> take_along_axis(B,i,axis=-1)
array([[4, 1, 3],
[5, 1, 9]])
numpy.argsort
docs state
Devoluciones:
index_array: ndarray, int Matriz de índices que ordenan a lo largo del eje especificado. Si a es unidimensional,a[index_array]
produce un a ordenado.
¿Cómo puedo aplicar el resultado de
numpy.argsort
para una matriz multidimensional para recuperar una matriz ordenada?
(NO solo una matriz 1-D o 2-D; podría ser una matriz N-dimensional donde N solo se conoce en tiempo de ejecución)
>>> import numpy as np
>>> np.random.seed(123)
>>> A = np.random.randn(3,2)
>>> A
array([[-1.0856306 , 0.99734545],
[ 0.2829785 , -1.50629471],
[-0.57860025, 1.65143654]])
>>> i=np.argsort(A,axis=-1)
>>> A[i]
array([[[-1.0856306 , 0.99734545],
[ 0.2829785 , -1.50629471]],
[[ 0.2829785 , -1.50629471],
[-1.0856306 , 0.99734545]],
[[-1.0856306 , 0.99734545],
[ 0.2829785 , -1.50629471]]])
Para mí no es solo una cuestión de usar
sort()
lugar;
Tengo otra matriz
B
y quiero ordenar
B
usando los resultados de
np.argsort(A)
largo del eje apropiado.
Considere el siguiente ejemplo:
>>> A = np.array([[3,2,1],[4,0,6]])
>>> B = np.array([[3,1,4],[1,5,9]])
>>> i = np.argsort(A,axis=-1)
>>> BsortA = ???
# should result in [[4,1,3],[5,1,9]]
# so that corresponding elements of B and sort(A) stay together
Parece que esta funcionalidad ya es una solicitud de mejora en numpy .
Este argsort produce una matriz (3,2)
In [453]: idx=np.argsort(A,axis=-1)
In [454]: idx
Out[454]:
array([[0, 1],
[1, 0],
[0, 1]], dtype=int32)
Como
np.sort(A, axis=-1)
aplicar esto a
A
para obtener el equivalente de
np.sort(A, axis=-1)
no es obvio.
La solución iterativa es ordenar cada fila (un caso 1d) con:
In [459]: np.array([x[i] for i,x in zip(idx,A)])
Out[459]:
array([[-1.0856306 , 0.99734545],
[-1.50629471, 0.2829785 ],
[-0.57860025, 1.65143654]])
Aunque probablemente no sea el más rápido, es probablemente la solución más clara y un buen punto de partida para conceptualizar una mejor solución.
La
tuple(inds)
de la solución
take
es:
(array([[0],
[1],
[2]]),
array([[0, 1],
[1, 0],
[0, 1]], dtype=int32))
In [470]: A[_]
Out[470]:
array([[-1.0856306 , 0.99734545],
[-1.50629471, 0.2829785 ],
[-0.57860025, 1.65143654]])
En otras palabras:
In [472]: A[np.arange(3)[:,None], idx]
Out[472]:
array([[-1.0856306 , 0.99734545],
[-1.50629471, 0.2829785 ],
[-0.57860025, 1.65143654]])
La primera parte es lo que
np.ix_
construiría, pero no le gusta el 2d
idx
.
Parece que exploré este tema hace un par de años
argsort para un ndarray multidimensional
a[np.arange(np.shape(a)[0])[:,np.newaxis], np.argsort(a)]
Traté de explicar lo que está pasando.
La función
take
hace el mismo tipo de cosas, pero construye la tupla de indexación para un caso más general (dimensiones y eje).
Generalizar a más dimensiones, pero aún con
axis=-1
debería ser fácil.
Para el primer eje,
A[np.argsort(A,axis=0),np.arange(2)]
funciona.
Solo necesitamos usar
advanced-indexing
a lo largo de todos los ejes con esa matriz de índices.
Podemos usar
np.ogrid
para crear cuadrículas abiertas de matrices de rango a lo largo de todos los ejes y luego reemplazar solo por el eje de entrada con los índices de entrada.
Finalmente, indexe en la matriz de datos con esos índices para la salida deseada.
Así, esencialmente, tendríamos -
# Inputs : arr, ind, axis
idx = np.ogrid[tuple(map(slice, ind.shape))]
idx[axis] = ind
out = arr[tuple(idx)]
Solo para hacerlo funcional y hacer verificaciones de errores, creemos dos funciones: una para obtener esos índices y la otra para alimentar la matriz de datos y simplemente indexar. La idea con la primera función es obtener los índices que podrían reutilizarse para indexar en cualquier matriz arbitraria que admitiría el número necesario de dimensiones y longitudes a lo largo de cada eje.
Por lo tanto, las implementaciones serían:
def advindex_allaxes(ind, axis):
axis = np.core.multiarray.normalize_axis_index(axis,ind.ndim)
idx = np.ogrid[tuple(map(slice, ind.shape))]
idx[axis] = ind
return tuple(idx)
def take_along_axis(arr, ind, axis):
return arr[advindex_allaxes(ind, axis)]
Ejecuciones de muestra:
In [161]: A = np.array([[3,2,1],[4,0,6]])
In [162]: B = np.array([[3,1,4],[1,5,9]])
In [163]: i = A.argsort(axis=-1)
In [164]: take_along_axis(A,i,axis=-1)
Out[164]:
array([[1, 2, 3],
[0, 4, 6]])
In [165]: take_along_axis(B,i,axis=-1)
Out[165]:
array([[4, 1, 3],
[5, 1, 9]])
Relevant one