Czy to nie jest po prostu ids = np.array(avgDists).argsort()[-n:]?
Jaime
2
@Jaime: Nie, to nie działa. „poprawna odpowiedź” brzmi [3, 1, 2]. Twoja linia produkuje [2, 1, 3](jeśli n == 3 jako przykład)
dawg
2
@drewk Cóż, a następnie zrób to ids = np.array(avgDists).argsort()[-n:][::-1]. Chodzi o to, aby uniknąć robienia kopii całej listy, co dostajesz po dodaniu -przed nią. Nie dotyczy to małego przykładu PO, może dotyczyć większych przypadków.
Jaime
1
@Jaime: Masz rację. Zobacz moją zaktualizowaną odpowiedź. Składnia tho jest dokładnie przeciwna do komentarza do końcowego fragmentu: np.array(avgDists).argsort()[::-1][:n]zrobi to. Ponadto, jeśli zamierzasz używać numpy, pozostań w numpy. Najpierw przekonwertuj listę na tablicę: avgDist=np.array(avgDists)potem staje sięavgDist.argsort()[::-1][:n}
dawg
Odpowiedzi:
230
Jeśli zanegujesz tablicę, najniższe elementy stają się najwyższymi elementami i odwrotnie. Dlatego wskaźniki nnajwyższych elementów to:
(-avgDists).argsort()[:n]
Innym sposobem uzasadnienia tego, jak wspomniano w komentarzach , jest zaobserwowanie, że duże elementy pojawiają się na końcu w argsort. Tak więc możesz czytać z ogona argsort, aby znaleźć nnajwyższe elementy:
avgDists.argsort()[::-1][:n]
Obie metody mają złożoność czasową O (n log n) , ponieważ argsortwywołanie jest tutaj terminem dominującym. Ale drugie podejście ma dobrą zaletę: zastępuje negację O (n) tablicy wycięciem O (1) . Jeśli pracujesz z małymi tablicami wewnątrz pętli, możesz uzyskać pewien wzrost wydajności dzięki unikaniu tej negacji, a jeśli pracujesz z dużymi tablicami, możesz zaoszczędzić na zużyciu pamięci, ponieważ negacja tworzy kopię całej tablicy.
Zauważ, że metody te nie zawsze dają równoważne wyniki: jeśli wymagana jest stabilna implementacja sortowania argsort, np. Poprzez przekazanie argumentu słowa kluczowegokind='mergesort' , wówczas pierwsza strategia zachowa stabilność sortowania, ale druga strategia złamie stabilność (tj. Pozycje równe przedmioty zostaną odwrócone).
Przykładowe czasy:
Przy użyciu małej tablicy 100 pływaków i ogona o długości 30 metoda widoku była o około 15% szybsza
>>> 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)
W przypadku większych tablic dominuje argsort i nie ma znaczącej różnicy czasu
>>> 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)
Uwaga: poniższy komentarz nedim jest nieprawidłowy. To, czy obcinać przed czy po cofnięciu, nie ma różnicy w wydajności, ponieważ obie te operacje jedynie zmieniają widok tablicy inaczej i nie kopiują danych.
Jeszcze bardziej wydajne jest np.array(avgDists).argsort()[:-n][::-1]
krojenie
3
Te odpowiedzi nie są równoważne, jeśli oryginalna tablica zawiera nans. W takim przypadku pierwsze rozwiązanie wydaje się dawać bardziej naturalny wynik z nansami na końcu niż na początku.
feilchenfeldt
1
Jak je porównać, gdy pożądany jest stabilny sort? Prawdopodobnie strategia krojenia odwraca równe elementy?
Eric,
1
@ user3666197 Czułem, że to nie ma znaczenia dla odpowiedzi. To, czy negacja tworzy kopię, czy nie (nie robi), nie jest tutaj naprawdę ważne, istotną informacją jest to, że obliczenie negacji jest złożonością O (n) w porównaniu do pobrania innego wycinka, którym jest O (1) .
wim
1
@ user3666197 Tak, to dobra uwaga - jeśli tablica zajmuje 50% dostępnej pamięci, z pewnością będziemy chcieli uniknąć jej kopiowania i powodowania zamiany. Będę ponownie edytować, aby wspomnieć, że tam jest tworzona kopia.
wim
70
Podobnie jak Python, [::-1]odwraca tablicę zwracaną przez argsort()i [:n]daje ostatnie n elementów:
Ta odpowiedź jest dobra, ale czuję, że twoje sformułowania źle odzwierciedlają rzeczywistą charakterystykę wydajności: „nawet przy tym bardzo małym zestawie danych metoda wyświetlania jest znacznie szybsza” . W rzeczywistości negacją jest O (n), a argsort to O (n log n) . Oznacza to, że rozbieżność czasowa zmniejszy się dla większych zestawów danych - dominuje pojęcie O (n log n) , jednak twoja sugestia jest optymalizacją części O (n) . Tak więc złożoność pozostaje taka sama, a to dla tego małego zbioru danych , w szczególności , że widzimy żadnych znaczących różnic.
wim
2
Asymptotycznie równoważna złożoność może nadal oznaczać, że jeden algorytm jest asymptotycznie dwa razy szybszy niż inny. Wyrzucenie takich różnic może mieć konsekwencje. Na przykład, nawet jeśli rozbieżność czasu (w procentach) zbliża się do 0, byłbym skłonny założyć się, że algorytm z negacją nadal wykorzystuje dwa razy więcej pamięci.
błąd
@bug Może, ale w tym przypadku tak nie jest. Dodałem pewne czasy do mojej odpowiedzi. Liczby pokazują, że w przypadku większych tablic podejścia te mają podobne czasy, co potwierdza hipotezę, że argsort jest dominujący. Jeśli chodzi o negację, zgaduję, że masz rację co do wykorzystania pamięci, ale użytkownicy nadal wolą, jeśli zależy im na pozycji nan i / lub potrzebują stabilnego rodzaju.
wim
6
Możesz użyć poleceń odwracania numpy.flipud()lub numpy.fliplr()uzyskać indeksy w porządku malejącym po sortowaniu za pomocą argsortpolecenia. Tak zwykle robię.
Zamiast używać np.argsortmożesz użyćnp.argpartition - jeśli potrzebujesz tylko indeksów najniższych / najwyższych n elementów.
Nie wymaga to sortowania całej tablicy, ale tylko potrzebnej części, ale należy zauważyć, że „porządek wewnątrz partycji” jest niezdefiniowany, więc chociaż daje prawidłowe indeksy, może nie być poprawnie uporządkowany:
>>> 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)
Lub, jeśli używasz tych dwóch razem, tj. Argsort i argpartition, operację należy wykonać na operacji argpartition.
demongolem
3
Możesz utworzyć kopię tablicy, a następnie pomnożyć każdy element przez -1.
W efekcie pierwszeństwo przed największymi elementami byłyby najmniejsze.
Wersety n najmniejszych elementów w kopii są n największymi elementami w oryginale.
Innym sposobem jest użycie tylko „-” w argumencie dla argumentu argsort, jak w: „df [np.argsort (-df [:, 0])]”, pod warunkiem, że df jest ramką danych i chcesz ją posortować według pierwszej kolumna (reprezentowana przez numer kolumny „0”). Zmień odpowiednio nazwę kolumny. Oczywiście kolumna musi być liczbowa.
ids = np.array(avgDists).argsort()[-n:]
?[3, 1, 2]
. Twoja linia produkuje[2, 1, 3]
(jeśli n == 3 jako przykład)ids = np.array(avgDists).argsort()[-n:][::-1]
. Chodzi o to, aby uniknąć robienia kopii całej listy, co dostajesz po dodaniu-
przed nią. Nie dotyczy to małego przykładu PO, może dotyczyć większych przypadków.np.array(avgDists).argsort()[::-1][:n]
zrobi to. Ponadto, jeśli zamierzasz używać numpy, pozostań w numpy. Najpierw przekonwertuj listę na tablicę:avgDist=np.array(avgDists)
potem staje sięavgDist.argsort()[::-1][:n}
Odpowiedzi:
Jeśli zanegujesz tablicę, najniższe elementy stają się najwyższymi elementami i odwrotnie. Dlatego wskaźniki
n
najwyższych elementów to:Innym sposobem uzasadnienia tego, jak wspomniano w komentarzach , jest zaobserwowanie, że duże elementy pojawiają się na końcu w argsort. Tak więc możesz czytać z ogona argsort, aby znaleźć
n
najwyższe elementy:Obie metody mają złożoność czasową O (n log n) , ponieważ
argsort
wywołanie jest tutaj terminem dominującym. Ale drugie podejście ma dobrą zaletę: zastępuje negację O (n) tablicy wycięciem O (1) . Jeśli pracujesz z małymi tablicami wewnątrz pętli, możesz uzyskać pewien wzrost wydajności dzięki unikaniu tej negacji, a jeśli pracujesz z dużymi tablicami, możesz zaoszczędzić na zużyciu pamięci, ponieważ negacja tworzy kopię całej tablicy.Zauważ, że metody te nie zawsze dają równoważne wyniki: jeśli wymagana jest stabilna implementacja sortowania
argsort
, np. Poprzez przekazanie argumentu słowa kluczowegokind='mergesort'
, wówczas pierwsza strategia zachowa stabilność sortowania, ale druga strategia złamie stabilność (tj. Pozycje równe przedmioty zostaną odwrócone).Przykładowe czasy:
Przy użyciu małej tablicy 100 pływaków i ogona o długości 30 metoda widoku była o około 15% szybsza
W przypadku większych tablic dominuje argsort i nie ma znaczącej różnicy czasu
Uwaga: poniższy komentarz nedim jest nieprawidłowy. To, czy obcinać przed czy po cofnięciu, nie ma różnicy w wydajności, ponieważ obie te operacje jedynie zmieniają widok tablicy inaczej i nie kopiują danych.
źródło
np.array(avgDists).argsort()[:-n][::-1]
Podobnie jak Python,
[::-1]
odwraca tablicę zwracaną przezargsort()
i[:n]
daje ostatnie n elementów:Zaletą tej metody jest to, że
ids
jest to widok z avgDists:(„OWNDATA” to False oznacza, że jest to widok, a nie kopia)
Innym sposobem na to jest coś takiego:
Problem polega na tym, że sposób ten polega na tworzeniu negatywu dla każdego elementu w tablicy:
I tworzy kopię, aby to zrobić:
Więc jeśli czas, każdy z tego bardzo małego zestawu danych:
Metoda przeglądania jest znacznie szybsza (i zajmuje 1/2 pamięci ...)
źródło
Możesz użyć poleceń odwracania
numpy.flipud()
lubnumpy.fliplr()
uzyskać indeksy w porządku malejącym po sortowaniu za pomocąargsort
polecenia. Tak zwykle robię.źródło
Zamiast używać
np.argsort
możesz użyćnp.argpartition
- jeśli potrzebujesz tylko indeksów najniższych / najwyższych n elementów.Nie wymaga to sortowania całej tablicy, ale tylko potrzebnej części, ale należy zauważyć, że „porządek wewnątrz partycji” jest niezdefiniowany, więc chociaż daje prawidłowe indeksy, może nie być poprawnie uporządkowany:
źródło
Możesz utworzyć kopię tablicy, a następnie pomnożyć każdy element przez -1.
W efekcie pierwszeństwo przed największymi elementami byłyby najmniejsze.
Wersety n najmniejszych elementów w kopii są n największymi elementami w oryginale.
źródło
-array
Jak wskazał @Kanmani, można zastosować łatwiejszą do interpretacji implementację
numpy.flip
, jak poniżej:Używając wzorca gościa zamiast funkcji członka, łatwiej jest odczytać kolejność operacji.
źródło
Na przykład:
Uzyskaj indeksy n maksymalnych wartości:
Sortuj je w kolejności malejącej:
Uzyskaj wyniki (dla n = 4):
źródło
Innym sposobem jest użycie tylko „-” w argumencie dla argumentu argsort, jak w: „df [np.argsort (-df [:, 0])]”, pod warunkiem, że df jest ramką danych i chcesz ją posortować według pierwszej kolumna (reprezentowana przez numer kolumny „0”). Zmień odpowiednio nazwę kolumny. Oczywiście kolumna musi być liczbowa.
źródło