[python] 내림차순으로 argsort를 사용할 수 있습니까?

다음 코드를 고려하십시오.

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

이것은 나에게 n가장 작은 요소의 지표를 제공 합니다. 가장 높은 원소 argsort의 인덱스를 얻기 위해 이것을 내림차순 으로 사용할 수 n있습니까?



답변

배열을 무효화하면 가장 낮은 요소가 가장 높은 요소가되고 그 반대도 마찬가지입니다. 따라서 n가장 높은 요소 의 지수는 다음 과 같습니다.

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

의견 에서 언급했듯이 이것에 대해 추론하는 또 다른 방법 은 큰 요소가 argsort 에서 마지막 에 오는 것을 관찰하는 것입니다. 따라서 argsort의 꼬리에서 읽어 n가장 높은 요소 를 찾을 수 있습니다.

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

두 방법 모두 시간 복잡성에서 O (n log n) 입니다 argsort. 여기서 호출은 지배적 인 용어 이기 때문 입니다. 그러나 두 번째 접근 방식은 배열 의 O (n) 부정을 O (1) 슬라이스로 대체합니다 . 루프 내부의 작은 배열을 사용하는 경우 해당 부정을 피함으로써 성능이 약간 향상 될 수 있으며, 큰 배열을 사용하는 경우 부정이 전체 배열의 복사본을 생성하므로 메모리 사용량을 절약 할 수 있습니다.

이러한 메소드가 항상 동등한 결과를 제공하지는 않습니다. argsort예를 들어 키워드 인수를 전달하여 안정적인 정렬 구현이 요청 된 kind='mergesort'경우 첫 번째 전략은 정렬 안정성을 유지하지만 두 번째 전략은 안정성을 잃습니다 (예 : 항목이 반전됩니다).

타이밍 예 :

100 개의 부동 소수점과 길이가 30 인 작은 배열을 사용하면 뷰 방법이 약 15 % 빨랐습니다.

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

더 큰 배열의 경우, argsort가 지배적이며 상당한 타이밍 차이가 없습니다.

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

유의하시기 바랍니다 네딤에서 주석 아래 올바르지 않습니다. 역전 전후에 절단해야하는지 여부는 효율성에 차이가 없습니다. 두 작업 모두 어레이의보기 만 다르게 진행하고 실제로 데이터를 복사하지 않기 때문입니다.


답변

그냥 파이썬과 같은, 즉에 [::-1]의해 반환 된 배열을 반전 argsort()하고 [:n]마지막 n 개의 요소를 제공합니다 :

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

이 방법의 장점은 avgDists ids관점 입니다.

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

( ‘거짓’인 ‘OWNDATA’는 이것이 사본이 아니라보기임을 나타냅니다)

이를 수행하는 다른 방법은 다음과 같습니다.

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

문제는 이것이 작동하는 방식이 배열의 각 요소에 대해 음수를 만드는 것입니다.

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

ANd는이를 위해 사본을 작성합니다.

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

따라서 매우 작은 데이터 세트로 각각 시간을 정하면 다음과 같습니다.

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

보기 방법이 훨씬 빠릅니다 (메모리의 1/2 사용 …)


답변

당신은 플립 명령을 사용할 수 있습니다 numpy.flipud()또는 numpy.fliplr()사용하여 정렬 한 후 내림차순으로 인덱스를 얻기 위해 argsort명령을 사용합니다. 그게 내가 보통하는 일입니다.


답변

가장 낮은 / 가장 높은 n 요소의 인덱스 만 필요한 경우 np.argsort사용 하는 대신 사용할 수 있습니다 np.argpartition.

전체 배열을 정렬 할 필요는 없지만 필요한 부분 만 정렬 할 필요가 있지만 “파티션 내부의 순서”는 정의되어 있지 않으므로 올바른 인덱스를 제공하지만 올바르게 정렬되지 않을 수 있습니다.

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


답변

배열의 복사본을 만든 다음 각 요소에 -1을 곱할 수 있습니다.
결과적으로 이전의 가장 큰 요소가 가장 작아집니다.
사본에서 n 개의 가장 작은 요소의 수는 원본에서 n 개의 가장 큰 요소입니다.


답변

@ Kanmani가 암시 한 것처럼 numpy.flip다음과 같이 구현을 해석하기가 더 쉽습니다 .

import numpy as np

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

멤버 함수가 아닌 방문자 패턴을 사용하면 작업 순서를 쉽게 읽을 수 있습니다.


답변

예를 들면 다음과 같습니다.

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

n 최대 값의 인덱스를 구하십시오.

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

내림차순으로 정렬하십시오.

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

결과 얻기 (n = 4의 경우) :

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