python arrays sorting numpy

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