Python'da sıralı bir dizinin indeksleri nasıl alınır


200

Sayısal bir listem var:

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

Şimdi bu listeyi almak için sıralarsam [1, 2, 3, 5, 100]. Ne istediğim sıralı düzende orijinal listeden öğelerin endeksleri yani [0, 1, 2, 4, 3] --- ala MATLAB'ın hem değerleri hem de indeksleri döndüren sıralama işlevi.



@unutbu Bu bir dupe (IMO) değil. Soru Numpy.argsort ()
amit ile

@amit: "Çelişmez" ile ne demek istiyorsun?
unutbu

@unutbu Numpy.argsort () bu soruya iyi bir cevaptır, Numpy olarak bahsettiğinize değil, bağlı olan diğer konuya (da kapattınız ve yapmamalısınız) bir dupe olabilir. argsort () bu ikisi için iyi cevaptır, ancak bahsettiğiniz kişi için DEĞİLDİR.
amit

1
Ne yazık ki, bu soru örnek seçiminde ciddi bir kusura sahiptir, çünkü girdi sadece sıralı sıradan bir aktarım olduğunda soru aynı cevabı verecektir.

Yanıtlar:



148

Sonraki gibi bir şey:

>>> 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) size (dizin, değer) gruplarını içeren bir liste verir:

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

Listeyi geçirerek sortedve sıralama anahtarını ayıklamak için bir işlev belirterek sıralarsınız (her bir grubun ikinci öğesi; bunun lambdaiçin kullanılır. Son olarak, sıralanan her öğenin orijinal dizini [i[0] for i in ...]liste kavrama kullanılarak çıkarılır .


7
itemgetter(1)lambda işlevi yerine kullanabilirsiniz
John La Rooy

4
@gnibbler , FYI modülündeki itemgetterişleve atıfta bulunuyor operator. Bunu from operator import itemgetterkullanmak için yapın.
Lauritz V.Thaulow

1
sıralanmış listeyi ve göstergeleri zip kullanarak alabilirsiniz:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
Charles L.

@RomanBodnarchuk bu işe yaramıyor, x = [3,1,2]; numpy.argsort(x)verim veriyor [1,2,0].
shahar_m


24

Cevapları enumerategüzel, ama şahsen değere göre sıralamak için kullanılan lambda sevmiyorum. Aşağıdaki dizin ve değeri tersine çevirir ve sıralar. Bu yüzden önce değere, sonra dizine göre sıralar.

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

11

Yanıt enumerateve ile güncellendi itemgetter:

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

Listeleri birlikte sıkıştırın: Tupledaki ilk eleman indeks olur, ikincisi değerdir (daha sonra demetin ikinci değerini kullanarak sıralayın x[1], x demettir)

Ya kullanarak itemgettergelen operatormodule`:

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

1
enumerate bu durumda zip daha uygun görünüyor
njzk2

10

Perfplot (benim bir proje) ile bunlar üzerinde hızlı bir performans kontrolü yaptım ve numpy dışında başka bir şey tavsiye etmek zor olduğunu gördüm (günlük ölçeğini not edin):

resim açıklamasını buraya girin


Grafiği yeniden oluşturmak için kod:

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

Numpy kullanmak istemiyorsanız,

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

burada gösterildiği gibi en hızlısıdır .


5

Temel olarak yapmanız gereken bir argsortuygulama, harici kütüphaneleri (örneğin NumPy) kullanmak veya bağımlılık olmadan saf Python'da kalmak isteyip istemediğinize bağlıdır.

Kendinize sormanız gereken soru şudur:

  • diziyi / listeyi sıralayan dizinler
  • öğelerin sıralı dizide / listede bulunacağı dizinler

Maalesef sorudaki örnek, neyin arzu edildiğini netleştirmiyor çünkü her ikisi de aynı sonucu verecek:

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

seçimi argsortuygulanmasını

Emrinizde NumPy varsa, işlevi numpy.argsortveya yöntemi kullanabilirsiniz numpy.ndarray.argsort.

NumPy içermeyen bir uygulama zaten diğer bazı cevaplarda belirtildi, bu yüzden burada kıyaslama cevabına göre en hızlı çözümü özetleyeceğim.

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

Diziyi / listeyi sıralayacak dizinleri alma

Diziyi / listeyi sıralayacak dizinleri almak için argsortdiziyi veya listeyi arayabilirsiniz . Burada NumPy sürümleri kullanıyorum ama Python uygulaması aynı sonuçları vermelidir

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

Sonuç, sıralı diziyi almak için gereken dizinleri içerir.

Sıralanan dizi olacağı için [1, 2, 3, 4], argsorted dizi orijinalde bu öğelerin dizinlerini içerir.

  • En küçük değer 1ve 1orijinalde indeks içindedir, böylece sonucun ilk öğesi olur 1.
  • 2İndeksi olan 2sonucun ikinci unsurdur böylece orijinal 2.
  • 3İndeksi olan 0sonucun üçüncü unsurdur böylece orijinal 0.
  • En büyük değer 4ve 3orijinalde indeks içindedir, böylece sonucun son öğesi olur 3.

Öğelerin sıralı dizide / listede bulunacağı dizinleri alma

Bu durumda argsort iki kez başvurmanız gerekir :

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

Bu durumda :

  • orijinalin ilk öğesi, en 3büyük üçüncü değerdir, böylece 2sıralanan dizide / listede dizinin ilk öğe olması gerekir 2.
  • orijinalin ikinci öğesi 1, en küçük değerdir, böylece 0sıralanan dizide / listede dizin olur, böylece ikinci öğe olur 0.
  • orijinalin üçüncü öğesi, en 2küçük ikinci değerdir, bu nedenle 1sıralanan dizi / listede dizin , böylece üçüncü öğe olur 1.
  • orijinalin dördüncü öğesi 4en büyük değerdir, bu nedenle 3sıralanan dizide / listede dizinin son öğe olması gerekir 3.

4

Diğer cevaplar YANLIŞ.

argsortBir kere koşmak çözüm değildir. Örneğin, aşağıdaki kod:

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

verimleri array([1, 2, 0], dtype=int64)bizim istediğimiz değil.

Cevap argsortiki kez çalışmak olmalı :

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

array([2, 0, 1], dtype=int64)beklendiği gibi verir .


Talebiniz x[2](3) en küçük öğe ve x[1](1) en büyük öğe (tamsayıların sıralanması bunları en küçük değerden en büyük değere doğru sıraladığından) yapar. Ayrıca, OP'ler örneğinde, OP'nin istediği endeksler gibi görünen tek bir np.argsort([1, 2, 3, 100, 5])verim array([0, 1, 2, 4, 3])vardır.
0 0

1
@ 0 0 örneğiniz özel bir durumdur. Eğer koşarsak , yanlış olanı arr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res)alırız [0 1 2 4 5 3].
shahar_m

Neyin yanlış olduğunu net değilim: arr[res]ortaya array([ 1, 2, 3, 5, 9, 100])çıkan dizi (artan) sırada olduğundan, gayet iyi görünüyor, verim .
0 0

@ 0 0 için arr=[1,2,3,100, 5, 9], çıktının olmasını bekliyorum inds=[0,1,2,5,3,4], çünkü bu öğeleri (artan bir şekilde) sipariş edeceğiniz sıra - 1 0'ların yerinde, 2 1. sırada, ...., 5 3'üncü ve 9'uncu sırada. Bu çıktıyı ( inds) elde etmek için argsortbahsettiğim gibi iki kez çalıştırmam gerekiyor .
shahar_m

Yani bu endeksler bir dizi dizi öğenin sıralamasıdır (0. sıra, 1. sıra, vb.). OP'nin MATLAB'larasort bahsettiği göz önüne alındığında, OP'nin np.argsortnormalde kullanılana benzer ( diğer arr[np.argsort[arr]]MATLAB örneğinde olduğu gibi sıralı diziyi elde etmek için kullanılabileceği gibi) diğer işlevselliği istediğini düşünüyorum . Cevabınız bunun yerine bu vaka / soru için geçerlidir .
0 0

0

Numpy'ı np olarak içe aktar

ENDEKS İÇİN

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 S dizinlerini sıralı olarak döndürür

DEĞER İÇİN

np.sort(S)

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

0

0'dan n-1'e kadar başka bir dizin dizini oluşturacağız Sonra bunu orijinal diziye sıkıştırıp orijinal değerlerine göre sıralayacağız

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

'

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.