Cómo obtener índices de una matriz ordenada en Python


200

Tengo una lista numérica:

myList = [1, 2, 3, 100, 5]

Ahora si ordeno esta lista para obtener [1, 2, 3, 5, 100]. Lo que quiero son los índices de los elementos de la lista original en el orden ordenado, es decir, [0, 1, 2, 4, 3] la función de clasificación de ala MATLAB que devuelve valores e índices.



@unutbu Esto no es un engaño (IMO). La pregunta no contradice el uso de Numpy.argsort ()
amit

@amit: ¿Qué quieres decir con "no se contradice"?
unutbu

@unutbu Numpy.argsort () es una buena respuesta a esta pregunta, podría ser un engaño al otro hilo vinculado (que también cerró y creo que no debería haberlo hecho) pero no al que mencionó, como Numpy. argsort () es una buena respuesta para estos dos, pero NO para la que mencionó.
amit

1
Desafortunadamente, esta pregunta tiene un defecto grave en su elección de ejemplo, ya que dos formas diferentes de leer la pregunta darían la misma respuesta cuando la entrada es solo una transposición fuera de orden.

Respuestas:



147

Algo así como el siguiente:

>>> myList = [1, 2, 3, 100, 5]
>>> [i[0] for i in sorted(enumerate(myList), key=lambda x:x[1])]
[0, 1, 2, 4, 3]

enumerate(myList) le da una lista que contiene tuplas de (índice, valor):

[(0, 1), (1, 2), (2, 3), (3, 100), (4, 5)]

Ordena la lista pasándola sortedy especificando una función para extraer la clave de clasificación (el segundo elemento de cada tupla; para eso lambdasirve. Finalmente, el índice original de cada elemento ordenado se extrae usando la [i[0] for i in ...]comprensión de la lista.


77
puede usar en itemgetter(1)lugar de la función lambda
John La Rooy

44
@gnibbler se refiere a la itemgetterfunción en el operatormódulo, FYI. Entonces hazlo from operator import itemgetterpara usarlo.
Lauritz V. Thaulow

1
puede obtener la lista ordenada y las indicaciones utilizando zip:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
Charles L.

@RomanBodnarchuk esto no funciona, x = [3,1,2]; numpy.argsort(x)produce [1,2,0].
shahar_m


24

Las respuestas con enumerateson agradables, pero personalmente no me gusta la lambda utilizada para ordenar por el valor. Lo siguiente solo invierte el índice y el valor, y lo ordena. Por lo tanto, primero se ordenará por valor, luego por índice.

sorted((e,i) for i,e in enumerate(myList))

11

Respuesta actualizada con enumeratey itemgetter:

sorted(enumerate(a), key=lambda x: x[1])
# [(0, 1), (1, 2), (2, 3), (4, 5), (3, 100)]

Comprima las listas juntas: el primer elemento en la tupla será el índice, el segundo es el valor (luego ordénelo usando el segundo valor de la tupla x[1] , x es la tupla)

O usando itemgetterdesde el operatormódulo`:

from operator import itemgetter
sorted(enumerate(a), key=itemgetter(1))

1
enumerar parece más apropiado que zip en este caso
njzk2

10

Hice una comprobación rápida del rendimiento de estos con perfplot (un proyecto mío) y descubrí que es difícil recomendar algo más que numpy (tenga en cuenta la escala de registro):

ingrese la descripción de la imagen aquí


Código para reproducir la trama:

import perfplot
import numpy


def sorted_enumerate(seq):
    return [i for (v, i) in sorted((v, i) for (i, v) in enumerate(seq))]


def sorted_enumerate_key(seq):
    return [x for x, y in sorted(enumerate(seq), key=lambda x: x[1])]


def sorted_range(seq):
    return sorted(range(len(seq)), key=seq.__getitem__)


def numpy_argsort(x):
    return numpy.argsort(x)


perfplot.save(
    "argsort.png",
    setup=lambda n: numpy.random.rand(n),
    kernels=[sorted_enumerate, sorted_enumerate_key, sorted_range, numpy_argsort],
    n_range=[2 ** k for k in range(15)],
    xlabel="len(x)",
)

6

Si no quieres usar numpy,

sorted(range(len(seq)), key=seq.__getitem__)

es más rápido, como se demuestra aquí .


5

Esencialmente necesitas hacer un argsort , qué implementación necesita depende si desea usar bibliotecas externas (por ejemplo, NumPy) o si desea permanecer en Python puro sin dependencias.

La pregunta que debe hacerse es: ¿quiere el

  • índices que ordenarían la matriz / lista
  • índices que los elementos tendrían en la matriz / lista ordenada

Desafortunadamente, el ejemplo en la pregunta no deja en claro lo que se desea porque ambos darán el mismo resultado:

>>> arr = np.array([1, 2, 3, 100, 5])

>>> np.argsort(np.argsort(arr))
array([0, 1, 2, 4, 3], dtype=int64)

>>> np.argsort(arr)
array([0, 1, 2, 4, 3], dtype=int64)

Elegir el argsort implementación

Si tiene NumPy a su disposición, simplemente puede usar la función numpy.argsorto el método numpy.ndarray.argsort.

Ya se mencionó una implementación sin NumPy en algunas otras respuestas, así que resumiré la solución más rápida de acuerdo con la respuesta de referencia aquí

def argsort(l):
    return sorted(range(len(l)), key=l.__getitem__)

Obteniendo los índices que ordenarían la matriz / lista

Para obtener los índices que ordenarían la matriz / lista, simplemente puede llamar argsorta la matriz o lista. Estoy usando las versiones de NumPy aquí, pero la implementación de Python debería dar los mismos resultados

>>> arr = np.array([3, 1, 2, 4])
>>> np.argsort(arr)
array([1, 2, 0, 3], dtype=int64)

El resultado contiene los índices necesarios para obtener la matriz ordenada.

Dado que la matriz ordenada sería [1, 2, 3, 4]la matriz ordenada contiene los índices de estos elementos en el original.

  • El valor más pequeño es 1y está en el índice 1del original, por lo que el primer elemento del resultado es 1.
  • El 2está en el índice 2en el original, por lo que el segundo elemento del resultado es 2.
  • El 3está en el índice 0en el original, por lo que el tercer elemento del resultado es 0.
  • El valor más grande 4y está en el índice 3en el original, por lo que el último elemento del resultado es 3.

Obtener los índices que tendrían los elementos en la matriz / lista ordenada

En este caso, deberá aplicar argsort dos veces :

>>> arr = np.array([3, 1, 2, 4])
>>> np.argsort(np.argsort(arr))
array([2, 0, 1, 3], dtype=int64)

En este caso :

  • el primer elemento del original es 3, que es el tercer valor más grande, por lo que tendría un índice 2en la matriz / lista ordenada, por lo que el primer elemento es2 .
  • el segundo elemento del original es 1, que es el valor más pequeño, por lo que tendría un índice 0en la matriz / lista ordenada para que el segundo elemento sea0 .
  • el tercer elemento del original es 2, que es el segundo valor más pequeño, por lo que tendría un índice 1en la matriz / lista ordenada para que el tercer elemento sea1 .
  • el cuarto elemento del original es 4 cuál es el valor más grande, por lo que tendría un índice 3en la matriz / lista ordenada, por lo que el último elemento es 3.

4

Las otras respuestas son incorrectas.

Ejecutar argsortuna vez no es la solución. Por ejemplo, el siguiente código:

import numpy as np
x = [3,1,2]
np.argsort(x)

rinde lo array([1, 2, 0], dtype=int64)que no es lo que queremos.

La respuesta debería ser correr argsortdos veces:

import numpy as np
x = [3,1,2]
np.argsort(np.argsort(x))

da array([2, 0, 1], dtype=int64)como se esperaba.


Su reclamo hace que x[2](3) sea el elemento más pequeño y x[1](1) el elemento más grande (ya que la clasificación de enteros los ordena de menor a mayor valor). Además, con el ejemplo de OP, un solo np.argsort([1, 2, 3, 100, 5])rendimiento array([0, 1, 2, 4, 3]), que parece ser el índice que quiere el OP.
0 0

1
@ 0 0 su ejemplo es un caso específico. Si corremos arr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res), obtenemos [0 1 2 4 5 3]cuál está mal.
shahar_m

No estoy claro qué está mal: arr[res]rendimientos array([ 1, 2, 3, 5, 9, 100]), que parecen estar perfectamente bien, ya que esa matriz resultante está en orden (creciente).
0 0

@ 0 0 para arr=[1,2,3,100, 5, 9], espero que la salida sea inds=[0,1,2,5,3,4], porque este es el orden en el que ordenará los elementos (cada vez más): 1 está en el lugar de 0, 2 en el primer lugar, ..., 5 en el 3er lugar y 9 en el 4to lugar. Para obtener esa salida ( inds) necesito ejecutar argsortdos veces, como mencioné.
shahar_m

Entonces, esos índices son una especie de clasificación de los elementos de la matriz (0 ° lugar, 1 ° lugar, etc.). Dada la mención del OP a MATLABsort , creo que el OP quiere la otra funcionalidad, como np.argsortse usa normalmente (donde se puede usar arr[np.argsort[arr]]para obtener la matriz ordenada, como en el último ejemplo de MATLAB). Su respuesta se aplica a este caso / pregunta en su lugar.
0 0

0

Importar numpy como np

PARA ÍNDICE

S=[11,2,44,55,66,0,10,3,33]

r=np.argsort(S)

[output]=array([5, 1, 7, 6, 0, 8, 2, 3, 4])

argsort Devuelve los índices de S en orden ordenado

POR VALOR

np.sort(S)

[output]=array([ 0,  2,  3, 10, 11, 33, 44, 55, 66])

0

Crearemos otra matriz de índices de 0 a n-1. Luego comprima esto en la matriz original y luego ordénelo según los valores originales.

ar = [1,2,3,4,5]
new_ar = list(zip(ar,[i for i in range(len(ar))]))
new_ar.sort()

``

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.