Cara mendapatkan indeks array yang diurutkan dalam Python

200

Saya punya daftar angka:

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

Sekarang jika saya mengurutkan daftar ini untuk mendapatkan [1, 2, 3, 5, 100]. Yang saya inginkan adalah indeks elemen dari daftar asli dalam urutan yang diurutkan yaitu [0, 1, 2, 4, 3] --- fungsi sortir MATLAB yang mengembalikan nilai dan indeks.

Gyan
sumber
@unutbu Ini bukan dupe (IMO). Pertanyaan itu tidak bertentangan dengan menggunakan Numpy.argsort ()
amit
@amit: Apa yang Anda maksud dengan "tidak bertentangan"?
unutbu
@unutbu Numpy.argsort () adalah jawaban yang bagus untuk pertanyaan ini, mungkin itu adalah penipuan ke utas lain yang ditautkan (yang juga Anda tutup dan saya kira Anda tidak seharusnya) tetapi tidak dengan yang Anda sebutkan, seperti Numpy. argsort () adalah jawaban yang bagus untuk keduanya, tetapi BUKAN untuk yang Anda referensikan.
amit
1
Sayangnya, pertanyaan ini memiliki kelemahan parah dalam pilihannya sebagai contoh, karena dua cara membaca pertanyaan yang berbeda akan memberikan jawaban yang sama ketika input hanya transposisi dari urutan yang diurutkan.

Jawaban:

147

Sesuatu seperti selanjutnya:

>>> 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) memberi Anda daftar yang berisi tupel (indeks, nilai):

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

Anda mengurutkan daftar dengan meneruskannya ke sorteddan menentukan fungsi untuk mengekstrak kunci pengurutan (elemen kedua dari setiap tuple; itulah gunanya lambda. Akhirnya, indeks asli dari setiap elemen yang diurutkan diekstraksi menggunakan [i[0] for i in ...]pemahaman daftar.

Bodnarchuk Romawi
sumber
7
Anda dapat menggunakan itemgetter(1)alih-alih fungsi lambda
John La Rooy
4
@gnibbler mengacu pada itemgetterfungsi dalam operatormodul, FYI. Begitu juga from operator import itemgetteruntuk menggunakannya.
Lauritz V. Thaulow
1
Anda bisa mendapatkan daftar dan indeks yang diurutkan dengan menggunakan zip:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
Charles L.
@RomanBodnarchuk ini tidak berfungsi, x = [3,1,2]; numpy.argsort(x)menghasilkan [1,2,0].
shahar_m
24

Jawabannya enumeratebagus, tapi saya pribadi tidak suka lambda yang digunakan untuk mengurutkan berdasarkan nilainya. Berikut ini hanya membalikkan indeks dan nilai, dan mengurutkannya. Jadi pertama-tama akan mengurutkan berdasarkan nilai, kemudian berdasarkan indeks.

sorted((e,i) for i,e in enumerate(myList))
Ant6n
sumber
11

Jawaban yang diperbarui dengan enumeratedan itemgetter:

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

Zip daftar bersama-sama: Elemen pertama dalam tuple akan indeks, yang kedua adalah nilai (kemudian urutkan menggunakan nilai kedua tuple x[1] , x adalah tuple)

Atau menggunakan itemgetterdari operatormodul`:

from operator import itemgetter
sorted(enumerate(a), key=itemgetter(1))
Mat
sumber
1
enumerasi tampaknya lebih tepat daripada zip dalam kasus ini
njzk2
10

Saya melakukan pemeriksaan kinerja cepat pada ini dengan perfplot (proyek saya) dan menemukan bahwa sulit untuk merekomendasikan hal lain selain numpy (perhatikan skala log):

masukkan deskripsi gambar di sini


Kode untuk mereproduksi plot:

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)",
)
Nico Schlömer
sumber
6

Jika Anda tidak ingin menggunakan numpy,

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

tercepat, seperti yang ditunjukkan di sini .

mab
sumber
5

Pada dasarnya Anda perlu melakukan argsort, implementasi apa yang Anda butuhkan tergantung jika Anda ingin menggunakan perpustakaan eksternal (misalnya NumPy) atau jika Anda ingin tetap murni-Python tanpa dependensi.

Pertanyaan yang perlu Anda tanyakan pada diri sendiri adalah: Apakah Anda menginginkan

  • indeks yang akan mengurutkan array / daftar
  • mengindeks bahwa elemen akan ada dalam array / daftar yang diurutkan

Sayangnya contoh dalam pertanyaan tidak menjelaskan apa yang diinginkan karena keduanya akan memberikan hasil yang sama:

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

Memilih argsort implementasinya

Jika Anda memiliki NumPy yang Anda inginkan, Anda cukup menggunakan fungsi numpy.argsortatau metode numpy.ndarray.argsort.

Implementasi tanpa NumPy telah disebutkan dalam beberapa jawaban lain, jadi saya akan rekap solusi tercepat sesuai dengan tolok ukur jawaban di sini

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

Mendapatkan indeks yang akan mengurutkan array / daftar

Untuk mendapatkan indeks yang akan mengurutkan array / daftar Anda cukup memanggil argsortarray atau daftar. Saya menggunakan versi NumPy di ​​sini tetapi implementasi Python harus memberikan hasil yang sama

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

Hasilnya berisi indeks yang diperlukan untuk mendapatkan array yang diurutkan.

Karena array yang diurutkan akan menjadi [1, 2, 3, 4]array yang diperdebatkan berisi indeks elemen-elemen ini dalam aslinya.

  • Nilai terkecil adalah 1dan berada pada indeks 1dalam dokumen asli sehingga elemen pertama dari hasilnya adalah1 .
  • The 2berada pada indeks 2dalam bahasa aslinya sehingga elemen kedua dari hasilnya adalah 2.
  • The 3berada pada indeks 0dalam bahasa aslinya sehingga elemen ketiga dari hasilnya adalah 0.
  • Nilai terbesar 4dan berada pada indeks 3dalam dokumen asli sehingga elemen terakhir dari hasilnya adalah 3.

Mendapatkan indeks yang akan dimiliki elemen-elemen dalam array / daftar yang diurutkan

Dalam hal ini, Anda perlu menerapkan argsort dua kali :

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

Pada kasus ini :

  • elemen pertama dari aslinya adalah 3, yang merupakan nilai terbesar ketiga sehingga akan memiliki indeks 2dalam array / daftar yang diurutkan sehingga elemen pertama adalah2 .
  • elemen kedua dari aslinya adalah 1, yang merupakan nilai terkecil sehingga akan memiliki indeks 0dalam array / daftar yang diurutkan sehingga elemen kedua adalah 0.
  • elemen ketiga dari aslinya adalah 2, yang merupakan nilai terkecil kedua sehingga akan memiliki indeks 1dalam array yang diurutkan / daftar sehingga elemen ketiga adalah 1.
  • elemen keempat dari aslinya adalah 4yang merupakan nilai terbesar sehingga akan memiliki indeks 3dalam array / daftar yang diurutkan sehingga elemen terakhir adalah 3.
MSeifert
sumber
4

Jawaban lainnya SALAH.

Berjalan argsortsekali bukanlah solusi. Misalnya, kode berikut:

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

hasil array([1, 2, 0], dtype=int64)yang bukan apa yang kita inginkan.

Jawabannya harus dijalankan argsortdua kali:

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

memberi array([2, 0, 1], dtype=int64)seperti yang diharapkan.

shahar_m
sumber
Klaim Anda menjadikan x[2](3) elemen terkecil, dan x[1](1) elemen terbesar (sejak pengurutan integer memesannya dari nilai terkecil hingga nilai terbesar). Juga, dengan contoh OP, satu np.argsort([1, 2, 3, 100, 5])hasil array([0, 1, 2, 4, 3]), yang tampaknya merupakan indeks yang diinginkan OP.
0 0
1
@ 0 0 contoh Anda adalah kasus khusus. Jika kita lari arr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res)maka kita mendapatkan [0 1 2 4 5 3]yang salah.
shahar_m
Saya tidak jelas apa yang salah: arr[res]hasil array([ 1, 2, 3, 5, 9, 100]), yang tampaknya baik-baik saja, karena array yang dihasilkan dalam urutan (meningkat).
0 0
@ 0 0 untuk arr=[1,2,3,100, 5, 9], saya mengharapkan output menjadi inds=[0,1,2,5,3,4], karena ini adalah urutan di mana Anda akan memesan elemen (semakin) - 1 di tempat 0s, 2 di tempat 1, ...., 5 di Juara 3 dan 9 Juara 4. Untuk mendapatkan output ( inds) saya harus menjalankan argsortdua kali, seperti yang saya sebutkan.
shahar_m
Jadi indeks-indeks tersebut adalah semacam peringkat elemen-elemen array (tempat 0, tempat 1, dll). Dengan menyebutkan OP ke MATLABsort , saya rasa OP menginginkan fungsionalitas lain, seperti np.argsortyang biasa digunakan (di mana orang dapat menggunakan arr[np.argsort[arr]]untuk mendapatkan array yang diurutkan, seperti pada contoh MATLAB terakhir). Jawaban Anda berlaku untuk kasus / pertanyaan ini sebagai gantinya.
0 0
0

Impor numpy sebagai np

UNTUK INDEKS

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 Mengembalikan indeks S dalam urutan terurut

UNTUK NILAI

np.sort(S)

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

Kami akan membuat array indeks lain dari 0 hingga n-1. Kemudian pos ini ke array asli dan kemudian mengurutkannya berdasarkan nilai asli

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

`

Jai dewani
sumber