GIL's LAB

배열에서 k번째로 값이 큰(작은) 값 찾기 및 순위 계산 방법 본문

파이썬/데이터 분석을 위한 파이썬

배열에서 k번째로 값이 큰(작은) 값 찾기 및 순위 계산 방법

GIL~ 2022. 8. 21. 12:45

이번 포스팅에서는 파이썬에서 임의의 배열이 주어졌을 때 이 배열에서 값이 큰 k개의 값을 찾는 방법과 배열을 구성하는 값의 순위를 계산하는 방법을 알아보겠습니다.

 

여기서 고려하는 문제를 도식화하면 아래와 같습니다. 

각 문제에 대한 설명은 다음과 같으며, 각 문제를 푸는데 둘 이상의 문법을 활용할 수도 있습니다.

 

(1) 값이 작은 순위 계산: 배열에서 값이 작을수록 1에 가까운 순위를 갖도록 하는 것으로 위 예제에서는 값이 가장 작은 1이 1위, 2가 2위 3이 3위, 5가 4위, 8이 5위가 됩니다.

(2) 값이 큰 순위 계산: 배열에서 값이 클수록 1에 가까운 순위를 갖도록 하는 것으로 위 예제에서는 값이 가장 큰 8이 1위, 5가 2위, 3이 3위, 2가 4위, 1이 5위가 됩니다.

(3) 값이 큰 상위 k개 계산: 배열에서 값이 큰 k개를 반환하는 것으로 위 예제에서는 값이 가장 큰 세 개의 요소인 8, 5, 3을 반환합니다.

(4) 값이 작은 상위 3개 계산: 배열에서 값이 작은 k개를 반환하는 것으로 위 예제에서는 값이 가장 작은 세 개의 요소인 1, 2, 3을 반환합니다.

 

 

순위 계산

순위 계산에는 scipy.stats.rankdata를 활용할 수 있습니다.

이 함수는 각 요소의 순위를 반환하며, 값이 작을수록 높은 순위를 부여합니다.

간단한 예제를 살펴보겠습니다. 먼저 배열 arr을 다음과 같이 생성합니다.

import numpy as np
arr = np.array([5, 3, 1, 2, 8])

다음으로 rankdata를 사용하여 arr의 요소 순위를 계산합니다.

from scipy.stats import rankdata
rankdata(arr)

[실행 결과]

array([4., 3., 1., 2., 5.])

값이 가장 작은 1이 1위를, 2가 2위를, 3이 3위를, 5가 4위를, 8이 5위를 한 것을 볼 수 있습니다.

 

값이 클수록 높은 순위를 부여하고 싶다면 arr에 -1을 곱해서 순위를 계산하면 됩니다.

이는 argsort 등에서도 사용하는 테크닉입니다.

rankdata(-arr)

[실행 결과]

array([2., 3., 5., 4., 1.])

 

이번에는 같은 값이 있을 때 처리하는 방법을 알아보겠습니다.

다음과 같이 arr2를 만듭니다.

arr2 = np.array([4,5,6,5])

중복된 요소인 5의 순위를 계산하는 방법은 method 인자를 통해 설정할 수 있습니다.

많이 쓰이는 값은 "average" (default), "min", "max"가 있으며 각 값에 대한 설명은 다음과 같습니다.

  • "average": 동점에 대해 순위의 평균으로 반환 
  • "min": 동점에 대해 순위의 최솟값을 반환
  • "max": 동점에 대해 순위의 최댓값을 반환.

표현이 직관적이지 않으므로 위의 예제를 통해 살펴보겠습니다.

arr2의 각 요소의 순위는 [1,2,4,3]이라고 할 수 있습니다. 이 순위는 1번째와 3번째 요소가 5로 같지만, 편의상 각각 2등과 3등을 부여한 것입니다.

method의 값으로 "average"를 사용하면 2와 3의 평균인 1.5를, "min"을 사용하면 최솟값인 2를, "max"를 사용하면 최댓값인 3을 반환합니다.

print(rankdata(arr2, method = "average"))
print(rankdata(arr2, method = "min"))
print(rankdata(arr2, method = "max"))

[실행 결과]

[1.  2.5 4.  2.5]
[1 2 4 2]
[1 3 4 3]

실행 결과에서 보듯이, 값이 5인 요소의 인덱스의 순위가 method 인자에 따라 결정되는 것을 알 수 있습니다. 

 

 

값이 큰(작은) 상위 k개 값 계산

값이 가장 큰 값을 찾을 때는 max를 가장 작은 값을 찾을 때는 min을 사용하면 됩니다.

그렇지만 값이 가장 큰 값의 인덱스를 찾을 때는 numpy.argmax를, 가장 작은 값의 인덱스를 찾을 때는 numpy.argmin을 사용할 수 있습니다.

print(np.argmax(arr))
print(np.argmin(arr))

[실행 결과]

4
2

실행 결과를 보면 arr의 최댓값의 위치가 4, arr의 최솟값의 위치가 2임을 알 수 있습니다. 

가장 큰 값 혹은 가장 작은 값 자체가 필요한 경우도 있겠지만, 인덱스가 필요한 경우도 많습니다.

예를 들어, 한 샘플과 가장 가까운 샘플을 찾는 것은 가장 가까운 거리를 갖는 샘플을 찾는 것과 같습니다.

즉, 얼마나 가까운지가 중요하지 않고 가장 가까운 샘플이 중요합니다.

 

이번에는 값이 가장 작은 상위 k개 값을 찾아보겠습니다.

이때 활용할 수 있는 함수로 numpy.argsort가 있습니다. 

이 함수는 배열과 그 인덱스를 배열 기준 오름차순으로 정렬한 뒤, 인덱스를 반환합니다. 

말이 어려우니 간단한 그림을 통해 살펴보겠습니다.

좌측에는 정렬전의 배열을 보여주며, 그 배열은 [10, 30, 20, 50, 40]이며 인덱스가 [0, 1, 2, 3, 4]입니다. 

이 배열을 정렬하면 [10, 20, 30, 40, 50]이 됩니다. 이때 인덱스도 배열과 같이 정렬됩니다.

즉, 20이 1번째 위치에 오면서 20의 인덱스인 2가 1번째 위치에, 30이 2번째 위치에 오면서 30의 인덱스인 1이 2번째 위치에 옵니다.

직접 실행해보겠습니다.

arr = np.array([10,30,20,50,40])
sorted_idx = np.argsort(arr)
print(arr)
print(sorted_idx)

[실행 결과]

[10 30 20 50 40]
[0 2 1 4 3]

argsort(arr)을 sorted_idx에 저장하고 sorted_idx를 출력했으며 그 결과는 위 그림에서 "정렬 후"의 인덱스와 같습니다.

참고로 argsort를 하더라도 arr은 정렬 전 그대로임을 알 수 있습니다.

 

이 결과는 상위 k개 값을 인덱싱하는데 사용할 수 있습니다. 

왜냐하면 sorted_idx의 i번째 요소는 원 배열에서 값이 i번째로 작은 요소의 인덱스이기 때문입니다.

즉, 다음과 같은 슬라이싱을 통해 값이 가장 작은 k(k=3)개 요소를 가져올 수 있습니다.

arr[sorted_idx[:3]]

[실행 결과]

array([10, 20, 30])

마찬가지로 값이 가장 큰 k개 요소는 argsort를 할 때 -1을 곱해주면 됩니다.

r_sorted_idx = np.argsort(-arr)
arr[r_sorted_idx[:3]]

[실행 결과]

array([50, 40, 30])

데이터 분석 서비스가 필요한 분은 아래 링크로! 

https://kmong.com/gig/374194 

 

데이터사이언스 박사의 데이터 분석 서비스 드립니다. | 150000원부터 시작 가능한 총 평점 5점의 I

78개 총 작업 개수 완료한 총 평점 5점인 데이터사이언스박사의 IT·프로그래밍, 데이터 분석·시각화 서비스를 68개의 리뷰와 함께 확인해 보세요. IT·프로그래밍, 데이터 분석·시각화 제공 등 150

kmong.com

 

Comments