Come ottenere indici di un array ordinato in Python


200

Ho un elenco numerico:

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

Ora, se ordino questo elenco per ottenere [1, 2, 3, 5, 100]. Quello che voglio sono gli indici degli elementi dall'elenco originale nell'ordine ordinato, ovvero [0, 1, 2, 4, 3] --- la funzione di ordinamento di MATLAB che restituisce valori e indici.



@unutbu Questo non è un duplicato (IMO). La questione non si contraddicono con Numpy.argsort ()
Amit

@amit: Cosa intendi con "non contraddire"?
unutbu,

@unutbu Numpy.argsort () è una buona risposta a questa domanda, potrebbe essere una copia dell'altro thread collegato (che hai anche chiuso e che non dovresti avere) ma non a quello che hai menzionato, come Numpy. argsort () è un'ottima risposta per questi due, ma NON per quello a cui ti riferivi.
Amit

1
Sfortunatamente, questa domanda ha un grave difetto nella sua scelta dell'esempio, poiché due diversi modi di leggere la domanda darebbero la stessa risposta quando l'input è solo una trasposizione fuori dall'ordinamento.

Risposte:



147

Qualcosa come il prossimo:

>>> 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) ti dà un elenco contenente tuple di (indice, valore):

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

Puoi ordinare l'elenco passandolo a sortede specificando una funzione per estrarre la chiave di ordinamento (il secondo elemento di ogni tupla; ecco a cosa lambdaserve. Infine, l'indice originale di ciascun elemento ordinato viene estratto usando la [i[0] for i in ...]comprensione dell'elenco.


7
puoi usare al itemgetter(1)posto della funzione lambda
John La Rooy,

4
@gnibbler si riferisce alla itemgetterfunzione nel operatormodulo, FYI. Quindi fallo from operator import itemgetterper usarlo.
Lauritz V. Thaulow,

1
è possibile ottenere l'elenco ordinato e le indicazioni utilizzando zip:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
Charles L.

@RomanBodnarchuk questo non funziona, x = [3,1,2]; numpy.argsort(x)produce [1,2,0].
shahar_m


24

Le risposte enumeratesono buone, ma personalmente non mi piace il lambda usato per ordinare in base al valore. Quanto segue inverte semplicemente l'indice e il valore e lo ordina. Quindi ordinerà prima per valore, quindi per indice.

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

11

Risposta aggiornata con enumeratee itemgetter:

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

Comprimi gli elenchi: il primo elemento nella tupla indicizzerà, il secondo è il valore (quindi ordinalo utilizzando il secondo valore della tupla x[1], x è la tupla)

O usando itemgetterdal operatormodulo`:

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

1
enumerare sembra più appropriato di zip in questo caso
njzk2

10

Ho fatto un rapido controllo delle prestazioni su questi con perfplot (un mio progetto) e ho scoperto che è difficile raccomandare nient'altro che intorpidito (notare la scala del registro):

inserisci qui la descrizione dell'immagine


Codice per riprodurre 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

Se non si desidera utilizzare numpy,

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

è più veloce, come dimostrato qui .


5

Fondamentalmente devi fare un argsort, quale implementazione ti serve se vuoi usare librerie esterne (es. NumPy) o se vuoi rimanere pure-Python senza dipendenze.

La domanda che devi porti è: vuoi il

  • indici che ordinerebbero la matrice / lista
  • indici che gli elementi avrebbero nella matrice / lista ordinata

Sfortunatamente l'esempio nella domanda non chiarisce ciò che si desidera perché entrambi daranno lo stesso risultato:

>>> 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)

Scelta argsortdell'implementazione

Se hai NumPy a tua disposizione, puoi semplicemente utilizzare la funzione numpy.argsorto il metodo numpy.ndarray.argsort.

Un'implementazione senza NumPy è già stata menzionata in alcune altre risposte, quindi ricapitolerò la soluzione più veloce in base alla risposta del benchmark qui

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

Ottenere gli indici che ordinerebbero la matrice / lista

Per ottenere gli indici che ordinerebbero l'array / list, puoi semplicemente chiamare argsortl'array o l'elenco. Sto usando le versioni di NumPy qui, ma l'implementazione di Python dovrebbe dare gli stessi risultati

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

Il risultato contiene gli indici necessari per ottenere l'array ordinato.

Poiché la matrice ordinata sarebbe [1, 2, 3, 4]la matrice argsorted contiene gli indici di questi elementi nell'originale.

  • Il valore più piccolo è 1ed è all'indice 1dell'originale, quindi il primo elemento del risultato è 1.
  • L' 2indice è 2nell'originale, quindi il secondo elemento del risultato è 2.
  • L' 3indice è 0nell'originale quindi il terzo elemento del risultato è 0.
  • Il valore più grande 4ed è all'indice 3dell'originale, quindi l'ultimo elemento del risultato è 3.

Ottenere gli indici che gli elementi avrebbero nella matrice / lista ordinata

In questo caso dovrai applicare argsort due volte :

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

In questo caso :

  • il primo elemento dell'originale è 3, che è il terzo valore più grande, quindi dovrebbe essere indicizzato 2nella matrice / lista ordinata, quindi il primo elemento è 2.
  • il secondo elemento dell'originale è 1, che è il valore più piccolo, quindi dovrebbe essere indicizzato 0nella matrice / lista ordinata, quindi il secondo elemento è 0.
  • il terzo elemento dell'originale è 2, che è il secondo valore più piccolo, quindi avrebbe l'indice 1nella matrice / lista ordinata, quindi il terzo elemento è 1.
  • il quarto elemento dell'originale è 4il valore più grande, quindi dovrebbe essere indicizzato 3nella matrice / lista ordinata, quindi l'ultimo è 3.

4

Le altre risposte sono ERRATE.

Eseguire argsortuna volta non è la soluzione. Ad esempio, il seguente codice:

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

rese array([1, 2, 0], dtype=int64)che non è ciò che vogliamo.

La risposta dovrebbe essere quella di eseguire argsortdue volte:

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

array([2, 0, 1], dtype=int64)come previsto.


La tua richiesta rende x[2](3) l'elemento più piccolo e x[1](1) l'elemento più grande (poiché l'ordinamento di numeri interi li ordina dal valore più piccolo al valore più grande). Inoltre, con l'esempio dei PO, un singolo np.argsort([1, 2, 3, 100, 5])rendimento array([0, 1, 2, 4, 3]), che sembra essere l'indice che il PO desidera.
0 0

1
@ 0 0 il tuo esempio è un caso specifico. Se corriamo, arr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res)allora otteniamo [0 1 2 4 5 3]quale è sbagliato.
shahar_m

Non sono chiaro cosa sia sbagliato: i arr[res]rendimenti array([ 1, 2, 3, 5, 9, 100]), che sembrano andare benissimo, dato che l'array risultante è in ordine (crescente).
0 0

@ 0 0 per arr=[1,2,3,100, 5, 9], mi aspetto che l'output sia inds=[0,1,2,5,3,4], perché questo è l'ordine in cui ordinerai gli elementi (sempre più) - 1 è al posto di 0, 2 al 1 ° posto, ...., 5 al 3 ° posto e 9 al 4 ° posto. Per ottenere quell'output ( inds) devo eseguire argsortdue volte, come ho già detto.
shahar_m,

Quindi quegli indici sono una specie di classifica degli elementi dell'array (0 ° posto, 1 ° posto, ecc.). Data la menzione dell'OP a MATLABsort , credo che l'OP voglia l'altra funzionalità, molto simile a quella np.argsortnormalmente usata (dove si può usare arr[np.argsort[arr]]per ottenere l'array ordinato, come nell'ultimo esempio MATLAB). La tua risposta si applica invece a questo caso / domanda .
0 0

0

Importa numpy come np

PER INDICE

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 Restituisce gli indici di S in ordine ordinato

PER VALORE

np.sort(S)

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

0

Creeremo un altro array di indici da 0 a n-1 Quindi comprimeremo l'array originale e lo ordineremo in base ai valori originali

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

`

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.