파이썬 Python/NumPy를 사용하여 배열의 항목 순위 지정




numpy 사용법 (8)

배열 배열을 가지고 있고 첫 번째 배열의 각 항목의 순위를 나타내는 다른 배열을 만들고 싶습니다. 파이썬과 NumPy를 사용하고 있습니다.

예 :

array = [4,2,7,1]
ranks = [2,1,3,0]

다음은 내가 제안한 최선의 방법입니다.

array = numpy.array([4,2,7,1])
temp = array.argsort()
ranks = numpy.arange(len(array))[temp.argsort()]

배열을 두 번 정렬하는 것을 피하는 더 나은 / 더 빠른 방법이 있습니까?


Answer #1

위의 방법을 시도했지만 실패했습니다. 그렇습니다. 부유물이 있어도 복제물이 중요 할 수 있습니다.

그래서 넥타이 검사 단계를 추가하여 수정 된 1D 솔루션을 작성했습니다.

def ranks (v):
    import numpy as np
    t = np.argsort(v)
    r = np.empty(len(v),int)
    r[t] = np.arange(len(v))
    for i in xrange(1, len(r)):
        if v[t[i]] <= v[t[i-1]]: r[t[i]] = r[t[i-1]]
    return r

# test it
print sorted(zip(ranks(v), v))

나는 그것이 가능한 한 효율적이라고 믿습니다.


Answer #2

이 질문은 몇 년 전의 일이며 받아 들여진 답변은 훌륭하지만 다음 내용은 여전히 ​​가치가 있다고 생각합니다. scipy 대한 의존성에 신경 쓸 필요가 scipy.stats.rankdata 를 사용할 수 있습니다 :

In [22]: from scipy.stats import rankdata

In [23]: a = [4, 2, 7, 1]

In [24]: rankdata(a)
Out[24]: array([ 3.,  2.,  4.,  1.])

In [25]: (rankdata(a) - 1).astype(int)
Out[25]: array([2, 1, 3, 0])

rankdata 의 좋은 특징은 method 인수가 타이를 처리하기위한 몇 가지 옵션을 제공한다는 것입니다. 예를 들어, 20 개의 세 번 발생하고 b 에 40 개의 두 번 발생합니다.

In [26]: b = [40, 20, 70, 10, 20, 50, 30, 40, 20]

기본값은 평균 순위를 묶인 값에 할당합니다.

In [27]: rankdata(b)
Out[27]: array([ 6.5,  3. ,  9. ,  1. ,  3. ,  8. ,  5. ,  6.5,  3. ])

method='ordinal' 연속되는 등급을 지정합니다.

In [28]: rankdata(b, method='ordinal')
Out[28]: array([ 6.,  2.,  9.,  1.,  3.,  8.,  5.,  7.,  4.])

method='min' 은 모든 묶인 값에 연결된 값의 최소 순위를 지정합니다.

In [29]: rankdata(b, method='min')
Out[29]: array([ 6.,  2.,  9.,  1.,  2.,  8.,  5.,  6.,  2.])

더 많은 옵션은 문서화 문자열을 참조하십시오.


Answer #3

마지막 단계의 왼쪽에서 슬라이스 사용 :

array = numpy.array([4,2,7,1])
temp = array.argsort()
ranks = numpy.empty_like(temp)
ranks[temp] = numpy.arange(len(array))

이렇게하면 마지막 단계에서 순열을 뒤집어서 두 번 정렬하는 것을 피할 수 있습니다.


Answer #4

우아함과 솔루션의 부족함과는 별도로 성능 문제도 있습니다. 다음은 약간의 벤치 마크입니다.

import numpy as np
from scipy.stats import rankdata
l = list(reversed(range(1000)))

%%timeit -n10000 -r5
x = (rankdata(l) - 1).astype(int)
>>> 128 µs ± 2.72 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)

%%timeit -n10000 -r5
a = np.array(l)
r = a.argsort().argsort()
>>> 69.1 µs ± 464 ns per loop (mean ± std. dev. of 5 runs, 10000 loops each)

%%timeit -n10000 -r5
a = np.array(l)
temp = a.argsort()
r = np.empty_like(temp)
r[temp] = np.arange(len(a))
>>> 63.7 µs ± 1.27 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)

Answer #5

argsort ()를 두 번 사용하면됩니다 :

>>> array = [4,2,7,1]
>>> ranks = numpy.array(array).argsort().argsort()
>>> ranks
array([2, 1, 3, 0])

Answer #6

k.rooijers의 방법이 좋았지 만 rcoup가 쓴 것처럼 반복되는 숫자는 배열 위치에 따라 순위가 매겨집니다. 이것은 나에게는 좋지 않았기 때문에 버전을 수정하여 순위를 후 처리하고 반복 된 숫자를 결합 된 평균 순위로 병합했습니다.

import numpy as np
a = np.array([4,2,7,2,1])
r = np.array(a.argsort().argsort(), dtype=float)
f = a==a
for i in xrange(len(a)):
   if not f[i]: continue
   s = a == a[i]
   ls = np.sum(s)
   if ls > 1:
      tr = np.sum(r[s])
      r[s] = float(tr)/ls
   f[s] = False

print r  # array([ 3. ,  1.5,  4. ,  1.5,  0. ])

나는 이것이 다른 사람들을 도울 수 있기를 바란다. 나는 이것에 대한 또 다른 해결책을 찾으려고 노력했지만 어떤 것도 찾을 수 없었다.


Answer #7

먼저 argsort를 두 번 사용하여 배열의 순서를 얻은 다음 순위를 얻습니다.

array = numpy.array([4,2,7,1])
order = array.argsort()
ranks = order.argsort()

2D (또는 고차원) 배열을 다룰 때 argsort에 축 인수를 전달하여 올바른 축 위에 정렬해야합니다.


Answer #8

평균 순위의 벡터화 된 버전은 아래를 참조하십시오. 나는 np.unique를 좋아합니다. 정말 코드의 범위를 넓히고 효과적으로 벡터화 할 수 없습니다. 파이썬 for-loops를 피하는 것 외에도이 접근법은 'a'에 대한 암시 적 이중 루프를 피합니다.

import numpy as np

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

a = np.array([4,2,7,2,1])
rank = a.argsort().argsort()

unique, inverse = np.unique(a, return_inverse = True)

unique_rank_sum = np.zeros_like(unique)
np.add.at(unique_rank_sum, inverse, rank)
unique_count = np.zeros_like(unique)
np.add.at(unique_count, inverse, 1)

unique_rank_mean = unique_rank_sum.astype(np.float) / unique_count

rank_mean = unique_rank_mean[inverse]

print rank_mean




numpy