Argsort'u azalan sırada kullanmak mümkün müdür?


181

Aşağıdaki kodu göz önünde bulundurun:

avgDists = np.array([1, 8, 6, 9, 4])
ids = avgDists.argsort()[:n]

Bu bana en nküçük elementlerin endekslerini veriyor . En yüksek elementlerin argsortindekslerini almak için bunu azalan sırada kullanmak mümkün müdür n?


3
Basitçe değil mi ids = np.array(avgDists).argsort()[-n:]?
Jaime

2
@Jaime: Hayır, bu işe yaramıyor. 'doğru cevap' dır [3, 1, 2]. Hattınız üretir [2, 1, 3](örnek olarak n == 3 ise)
dawg

2
@ drewk Peki, öyleyse yap ids = np.array(avgDists).argsort()[-n:][::-1]. Mesele, tüm listenin bir kopyasını yapmaktan kaçınmaktır -. OP'nin küçük örneğiyle ilgili olmayan, daha büyük vakalar için olabilir.
Jaime

1
@Jaime: Haklısın. Güncellenmiş cevabımı görün. Sözdizimi tho, biten dilimle ilgili yorumunuzun tam tersidir: np.array(avgDists).argsort()[::-1][:n]yapacak. Ayrıca, numpy kullanacaksanız numpy'de kalın. Önce listeyi bir diziye dönüştürün: avgDist=np.array(avgDists)sonra oluravgDist.argsort()[::-1][:n}
dawg

Yanıtlar:


230

Bir diziyi reddederseniz, en düşük öğeler en yüksek öğeler olur veya tersi de geçerlidir. Bu nedenle, en nyüksek elementlerin endeksleri :

(-avgDists).argsort()[:n]

Bu konuda akıl yürütmenin bir başka yolu, yorumlarda belirtildiği gibi , büyük öğelerin argsort'ta son geldiğini gözlemlemektir. Böylece, en nyüksek öğeleri bulmak için argsort'un kuyruğundan okuyabilirsiniz :

avgDists.argsort()[::-1][:n]

Her iki yöntem de zaman karmaşıklığında O (n log n) şeklindedir , çünkü argsortçağrı burada baskın terimdir. Ancak ikinci yaklaşımın güzel bir avantajı vardır: dizinin O (n) negatifliğini O (1) dilimi ile değiştirir. Döngülerin içindeki küçük dizilerle çalışıyorsanız, bu olumsuzlamadan kaçınmaktan bazı performans kazanımları elde edebilirsiniz ve büyük dizilerle çalışıyorsanız, olumsuzlama tüm dizinin bir kopyasını oluşturduğundan bellek kullanımından tasarruf edebilirsiniz.

Bu yöntemlerin her zaman eşdeğer sonuçlar vermediğini unutmayın: argsortörneğin, anahtar kelime bağımsız değişkenini geçerek kararlı bir sıralama uygulaması istenirse kind='mergesort', ilk strateji sıralama kararlılığını koruyacaktır, ancak ikinci strateji istikrarı bozacaktır (yani eşit konumlar öğeler ters alacak).

Örnek zamanlamalar:

100 şamandıra ve uzunluk 30 kuyruktan oluşan küçük bir dizi kullanarak, görüntüleme yöntemi yaklaşık% 15 daha hızlıydı

>>> avgDists = np.random.rand(100)
>>> n = 30
>>> timeit (-avgDists).argsort()[:n]
1.93 µs ± 6.68 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit avgDists.argsort()[::-1][:n]
1.64 µs ± 3.39 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit avgDists.argsort()[-n:][::-1]
1.64 µs ± 3.66 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Daha büyük diziler için argsort baskındır ve önemli bir zamanlama farkı yoktur

>>> avgDists = np.random.rand(1000)
>>> n = 300
>>> timeit (-avgDists).argsort()[:n]
21.9 µs ± 51.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> timeit avgDists.argsort()[::-1][:n]
21.7 µs ± 33.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> timeit avgDists.argsort()[-n:][::-1]
21.9 µs ± 37.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Aşağıdaki nedim'den gelen yorumun yanlış olduğunu lütfen unutmayın . Bu işlemlerin her ikisi de dizinin yalnızca farklı bir görünümünden geçiyor ve gerçekte veri kopyalamıyor olduğundan, tersine dönmeden önce veya sonra kesip kesmemek verimlilikte bir fark yaratmıyor.


14
np.array(avgDists).argsort()[:-n][::-1]
Tersine

3
Orijinal dizi nans içeriyorsa bu yanıtlar eşdeğer değildir. Böyle bir durumda, ilk çözüm nans ile başlangıçtansa daha doğal bir sonuç veriyor gibi görünüyor.
feilchenfeldt

1
İstikrarlı bir sıralama istendiğinde bunlar nasıl karşılaştırılır? Muhtemelen dilimleme stratejisi eşit öğeleri tersine çevirir mi?
Eric

1
@ user3666197 Cevabın uygun olmadığını hissettim. Olumsuzlama bir kopya oluştursun ya da yaratmasın (önemli değil) burada gerçekten önemli değildir, ilgili bilgi, olumsuzlamanın hesaplanması O (n) karmaşıklığı vs O (1) olan başka bir dilim almaktır .
wim

1
@ user3666197 Evet, bu iyi bir nokta - eğer bir dizi% 50 kullanılabilir bellek alıyorsa, kesinlikle kopyalamaktan ve takas etmekten kaçınmak isteyeceğiz. Orada bir kopyasının oluşturulduğunu belirtmek için tekrar düzenleyeceğim.
wim

70

Tıpkı Python gibi, [::-1]geri döndürülen diziyi tersine çevirir argsort()ve [:n]son n öğeyi verir:

>>> avgDists=np.array([1, 8, 6, 9, 4])
>>> n=3
>>> ids = avgDists.argsort()[::-1][:n]
>>> ids
array([3, 1, 2])

Bu yöntemin avantajı , avgDists'in idsbir görünümüdür :

>>> ids.flags
  C_CONTIGUOUS : False
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

('OWNDATA' False, bunun bir kopya değil görünüm olduğunu gösterir)

Bunu yapmanın başka bir yolu şudur:

(-avgDists).argsort()[:n]

Sorun, bu çalışma şeklinin dizideki her öğenin negatifini oluşturmaktır:

>>> (-avgDists)
array([-1, -8, -6, -9, -4])

ANd bunun için bir kopya oluşturur:

>>> (-avgDists_n).flags['OWNDATA']
True

Yani her birine zaman ayırırsanız, bu çok küçük veri setiyle:

>>> import timeit
>>> timeit.timeit('(-avgDists).argsort()[:3]', setup="from __main__ import avgDists")
4.2879798610229045
>>> timeit.timeit('avgDists.argsort()[::-1][:3]', setup="from __main__ import avgDists")
2.8372560259886086

Görüntüleme yöntemi önemli ölçüde daha hızlıdır (ve hafızanın 1 / 2'sini kullanır ...)


4
Bu cevap iyidir, ancak ifadelerinizin gerçek performans özelliklerini yanlış temsil ettiğini hissediyorum: "bu çok küçük veri kümesinde bile, görüntüleme yöntemi önemli ölçüde daha hızlı" . Gerçekte, olumsuzlama O (n) ve argsort O (n log n) 'dir . Bu , daha büyük veri kümeleri için zamanlama tutarsızlığının azalacağı anlamına gelir - O (n log n) terimi baskındır, ancak öneriniz O (n) parçasının bir optimizasyonudur . Bu yüzden karmaşıklık aynı kalır ve özellikle bu küçük veri seti için önemli farklılıklar görüyoruz.
wim

2
Asimptotik olarak eşdeğer karmaşıklık, bir algoritmanın asimptotik olarak diğerinden iki kat daha hızlı olduğu anlamına gelebilir. Bu tür ayrımları atmanın sonuçları olabilir. Örneğin, zaman uyuşmazlığı (yüzde olarak) 0'a yaklaşsa bile, negatif olan algoritmanın hala iki kat daha fazla bellek kullandığına bahse girmeye istekli olurum.
hata

@bug Olabilir, ancak bu durumda değildir. Cevabıma bazı zamanlamalar ekledim. Rakamlar, daha büyük diziler için bu yaklaşımların benzer zamanlamalara sahip olduğunu ve bu da argümanın baskın olduğu hipotezini desteklediğini göstermektedir. Olumsuzlama için, bellek kullanımı konusunda haklı olduğunuzu tahmin ediyorum, ancak kullanıcılar yine de nan'ın konumunu önemsiyorlarsa ve / veya kararlı bir türe ihtiyaç duyarlarsa bunu tercih edebilirler.
wim


5

Kullanmak yerine np.argsortkullanabilirsiniz np.argpartition- sadece en düşük / en yüksek n öğelerinin indekslerine ihtiyacınız varsa.

Bu, tüm diziyi sıralamak zorunda değil, sadece ihtiyacınız olan kısmı ancak "bölümünüzdeki sipariş" undefined olduğunu unutmayın, bu nedenle doğru indeksleri verirken doğru şekilde sıralanamayabilirler:

>>> avgDists = [1, 8, 6, 9, 4]
>>> np.array(avgDists).argpartition(2)[:2]  # indices of lowest 2 items
array([0, 4], dtype=int64)

>>> np.array(avgDists).argpartition(-2)[-2:]  # indices of highest 2 items
array([1, 3], dtype=int64)

Ya da, ikisini birlikte kullanıyorsanız, yani argsort ve argpartition ise, işlem argpartition işleminde gerçekleştirilmelidir.
demongolem

3

Dizinin bir kopyasını oluşturabilir ve sonra her bir öğeyi -1 ile çarpabilirsiniz.
Sonuç olarak, en büyük unsurlar en küçüğü olur.
Kopyadaki en küçük öğelerin uçları, orijinaldeki en büyük öğedir.


Bu, diğer cevaplarda belirtildiği gibi diziyi kolayca -array
reddederek yapılır

1

Örneğinizle:

avgDists = np.array([1, 8, 6, 9, 4])

En fazla n değer içeren dizinler elde edin:

ids = np.argpartition(avgDists, -n)[-n:]

Bunları azalan düzende sıralayın:

ids = ids[np.argsort(avgDists[ids])[::-1]]

Sonuçları alın (n = 4 için):

>>> avgDists[ids]
array([9, 8, 6, 4])

1

@Kanmani'nin ima ettiği gibi, yorumlanması daha kolay bir uygulama numpy.flip, aşağıdaki gibi kullanılabilir:

import numpy as np

avgDists = np.array([1, 8, 6, 9, 4])
ids = np.flip(np.argsort(avgDists))
print(ids)

Üye işlevleri yerine ziyaretçi kalıbını kullanarak işlem sırasını okumak daha kolaydır.


-1

Başka bir yol, dgs veri çerçevesi olması ve ilk önce sıralamak istediğinizde, "df [np.argsort (-df [:, 0])]" gibi argsort argümanında yalnızca '-' kullanmaktır. sütun ('0' sütun numarası ile gösterilir). Sütun adını uygun şekilde değiştirin. Tabii ki, sütun sayısal bir sütun olmalıdır.

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.