파이썬에서 NumPy argmax() 함수를 사용하는 방법

본 튜토리얼에서는 NumPy 라이브러리의 `argmax()` 함수를 활용하여 배열 내 최댓값 요소의 위치(인덱스)를 찾는 방법을 상세히 알아보겠습니다.

NumPy는 파이썬에서 과학적 계산을 수행하기 위한 핵심 라이브러리 중 하나입니다. 파이썬의 표준 리스트보다 월등한 성능을 제공하는 다차원 배열을 지원합니다. NumPy 배열을 다루다 보면 배열 내에서 최댓값을 찾는 작업이 자주 발생합니다. 하지만, 때로는 최댓값 자체보다는 그 값이 위치한 인덱스 정보가 필요할 때가 있습니다.

`argmax()` 함수는 이러한 요구를 충족시켜 1차원 및 다차원 배열 모두에서 최댓값의 인덱스를 효율적으로 찾아줍니다. 이제부터 이 함수의 작동 방식을 자세히 살펴보도록 하겠습니다.

NumPy 배열에서 최댓값의 인덱스 찾는 방법

본 튜토리얼을 실습하기 위해서는 파이썬과 NumPy가 설치되어 있어야 합니다. 파이썬 REPL이나 주피터 노트북을 이용하여 코드를 작성하고 실행할 수 있습니다.

가장 먼저 NumPy를 `np`라는 약칭으로 임포트합니다.

import numpy as np

NumPy의 `max()` 함수를 사용하면 배열의 최댓값을 손쉽게 얻을 수 있습니다. 필요에 따라 특정 축을 기준으로 최댓값을 구할 수도 있습니다.

array_1 = np.array([1,5,7,2,10,9,8,4])
print(np.max(array_1))

# 출력
10

위 코드에서 `np.max(array_1)`는 배열 `array_1`의 최댓값인 10을 정확하게 반환합니다.

만약 배열에서 최댓값이 처음으로 나타나는 인덱스를 알고 싶다면 다음과 같은 두 단계를 거쳐야 합니다.

  • 최댓값 자체를 찾습니다.
  • 해당 최댓값의 인덱스를 찾습니다.

`array_1` 배열에서, 0부터 시작하는 인덱스 체계에 따라 최댓값 10은 인덱스 4에 위치합니다. 즉, 첫 번째 요소는 인덱스 0, 두 번째 요소는 인덱스 1과 같이 대응됩니다.

최댓값이 나타나는 위치를 파악하기 위해 NumPy의 `where()` 함수를 활용할 수 있습니다. `np.where(condition)`는 주어진 조건이 참(True)인 모든 인덱스를 배열 형태로 반환합니다.

배열에 접근하여 첫 번째 인덱스의 항목을 사용해야 합니다. `array_1`에서 최댓값의 위치를 찾기 위해 조건으로 `array_1==10`을 사용합니다. 여기서 10은 `array_1`의 최댓값입니다.

print(int(np.where(array_1==10)[0]))

# 출력
4

`np.where()` 함수를 조건만 사용하여 호출했지만, 이는 권장되는 사용법은 아닙니다.

참고: NumPy `where()` 함수:
`np.where(조건, x, y)`는 다음을 반환합니다.

  • 조건이 참일 경우 `x`의 요소
  • 조건이 거짓일 경우 `y`의 요소

따라서 `np.max()` 함수와 `np.where()` 함수를 함께 사용하면 최댓값을 찾고 그 최댓값이 나타나는 인덱스를 알아낼 수 있습니다.

그러나 위에서 설명한 두 단계의 복잡한 과정을 거치는 대신, NumPy의 `argmax()` 함수를 사용하면 배열 내 최댓값의 인덱스를 직접 얻을 수 있습니다.

NumPy `argmax()` 함수의 구문

NumPy `argmax()` 함수의 일반적인 구문은 다음과 같습니다.

np.argmax(array,axis,out)
# numpy는 np라는 약칭으로 임포트되었습니다.

위 구문에서:

  • `array`는 유효한 NumPy 배열입니다.
  • `axis`는 선택적 매개변수입니다. 다차원 배열을 사용할 경우, `axis` 매개변수를 통해 특정 축을 기준으로 최댓값의 인덱스를 찾을 수 있습니다.
  • `out` 또한 선택적 매개변수입니다. `out` 매개변수를 NumPy 배열로 설정하여 `argmax()` 함수의 결과를 저장할 수 있습니다.

참고: NumPy 버전 1.22.0부터는 `keepdims`라는 추가 매개변수가 도입되었습니다. `argmax()` 함수 호출 시 `axis` 매개변수를 지정하면 배열이 해당 축을 따라 축소됩니다. 하지만 `keepdims` 매개변수를 True로 설정하면 반환된 출력의 형태가 입력 배열의 형태와 동일하게 유지됩니다.

NumPy `argmax()`를 사용하여 최댓값의 인덱스 찾기

#1. NumPy `argmax()` 함수를 이용하여 `array_1`에서 최댓값의 인덱스를 찾아보겠습니다.

array_1 = np.array([1,5,7,2,10,9,8,4])
print(np.argmax(array_1))

# 출력
4

`argmax()` 함수는 정확하게 4를 반환합니다. ✅

#2. 만약 `array_1`에 10이 두 번 나타나도록 변경한다면, `argmax()` 함수는 처음으로 10이 나타나는 인덱스만을 반환합니다.

array_1 = np.array([1,5,7,2,10,10,8,4])
print(np.argmax(array_1))

# 출력
4

이후 예제에서는 예제 #1에서 정의한 `array_1`을 다시 사용하겠습니다.

NumPy `argmax()`를 사용하여 2차원 배열에서 최댓값의 인덱스 찾기

NumPy 배열 `array_1`을 2개의 행과 4개의 열을 가지는 2차원 배열로 변형해보겠습니다.

array_2 = array_1.reshape(2,4)
print(array_2)

# 출력
[[ 1  5  7  2]
 [10  9  8  4]]

2차원 배열에서 축 0은 행을 의미하고, 축 1은 열을 의미합니다. NumPy 배열은 0-인덱싱을 따릅니다. 따라서 NumPy 배열 `array_2`의 행과 열 인덱스는 다음과 같습니다.

이제 2차원 배열 `array_2`에 대해 `argmax()` 함수를 호출해보겠습니다.

print(np.argmax(array_2))

# 출력
4

2차원 배열에서 `argmax()`를 호출했음에도 불구하고, 여전히 4가 반환됩니다. 이것은 이전 섹션의 1차원 배열 `array_1`의 출력과 동일합니다.

왜 이런 일이 발생할까요? 🤔

이는 `axis` 매개변수에 대한 값을 지정하지 않았기 때문입니다. `axis` 매개변수가 설정되지 않으면 기본적으로 `argmax()` 함수는 평탄화된 배열을 따라 최댓값의 인덱스를 반환합니다.

평탄화된 배열이란 무엇일까요? `d1 x d2 x … x dN` 형태의 N차원 배열이 있다면, 여기서 `d1`, `d2`, …, `dN`은 각 차원에 따른 배열의 크기이며, 평탄화된 배열은 크기가 `d1 * d2 * … * dN`인 긴 1차원 배열입니다.

`array_2`의 평탄화된 배열이 어떻게 생겼는지 확인하려면, 아래와 같이 `flatten()` 메소드를 호출할 수 있습니다.

array_2.flatten()

# 출력
array([ 1,  5,  7,  2, 10,  9,  8,  4])

행을 따라 최댓값 인덱스 찾기 (`axis = 0`)

이제 행(`axis = 0`)을 따라 최댓값의 인덱스를 찾아보겠습니다.

np.argmax(array_2,axis=0)

# 출력
array([1, 1, 1, 1])

이 출력이 다소 이해하기 어려울 수 있지만, 작동 방식에 대해 설명드리겠습니다.

행을 따라 최댓값의 인덱스를 찾고 있기 때문에 `axis` 매개변수를 0(`axis=0`)으로 설정했습니다. 따라서 `argmax()` 함수는 각 열에 대해 최댓값이 나타나는 행 번호를 반환합니다.

더 나은 이해를 위해 시각적으로 살펴보겠습니다.

위 그림과 `argmax()` 함수의 출력에서 다음을 확인할 수 있습니다.

  • 인덱스 0에 있는 첫 번째 열의 경우, 최댓값 10은 인덱스 1에 있는 두 번째 행에서 나타납니다.
  • 인덱스 1에 있는 두 번째 열의 경우, 최댓값 9는 인덱스 1에 있는 두 번째 행에서 나타납니다.
  • 인덱스 2와 3에 있는 세 번째와 네 번째 열의 경우, 최댓값 8과 4는 모두 인덱스 1에 있는 두 번째 행에서 나타납니다.

이것이 바로 출력 배열 `[1, 1, 1, 1]`이 나타나는 이유입니다. 행을 따라 최댓값이 두 번째 행(모든 열에 대해)에서 나타나기 때문입니다.

열을 따라 최댓값 인덱스 찾기 (`axis = 1`)

다음으로 `argmax()` 함수를 사용하여 열을 따라 최댓값의 인덱스를 찾아보겠습니다.

다음 코드를 실행하고 출력을 살펴보세요.

np.argmax(array_2,axis=1)
array([2, 0])

출력을 분석할 수 있나요?

열을 따라 최댓값의 인덱스를 계산하기 위해 `axis=1`로 설정했습니다.

`argmax()` 함수는 각 행에 대해 최댓값이 나타나는 열 번호를 반환합니다.

다음은 시각적인 설명입니다.

위 그림과 `argmax()` 함수의 출력에서 다음을 확인할 수 있습니다.

  • 인덱스 0에 있는 첫 번째 행의 경우, 최댓값 7은 인덱스 2에 있는 세 번째 열에서 나타납니다.
  • 인덱스 1에 있는 두 번째 행의 경우, 최댓값 10은 인덱스 0에 있는 첫 번째 열에서 나타납니다.

이제 출력 `array([2, 0])`의 의미를 이해할 수 있을 겁니다.

NumPy `argmax()`에서 선택적 `out` 매개변수 사용

NumPy `argmax()` 함수의 선택적 `out` 매개변수를 사용하면 결과를 NumPy 배열에 저장할 수 있습니다.

열을 따라 최댓값의 인덱스를 찾는 이전 `argmax()` 함수 호출의 출력을 저장하기 위해 0으로 초기화된 배열을 만들어 보겠습니다 (`axis=1`).

out_arr = np.zeros((2,))
print(out_arr)
[0. 0.]

이제 열(`axis = 1`)을 따라 최댓값의 인덱스를 찾는 이전 예제를 다시 살펴보면서, `out` 매개변수를 위에서 정의한 `out_arr`로 설정합니다.

np.argmax(array_2,axis=1,out=out_arr)

`out_arr`이 기본적으로 float 배열로 초기화되었기 때문에 파이썬 인터프리터가 `TypeError`를 발생시키는 것을 볼 수 있습니다.

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/usr/local/lib/python3.7/dist-packages/numpy/core/fromnumeric.py in _wrapfunc(obj, method, *args, **kwds)
     56     try:
---> 57         return bound(*args, **kwds)
     58     except TypeError:

TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe'

따라서 `out` 매개변수를 출력 배열로 설정할 때는, 출력 배열의 형태와 데이터 타입이 올바른지 확인하는 것이 중요합니다. 배열 인덱스는 항상 정수이므로 출력 배열을 정의할 때 `dtype` 매개변수를 `int`로 설정해야 합니다.

out_arr = np.zeros((2,),dtype=int)
print(out_arr)

# 출력
[0 0]

이제 `axis` 및 `out` 매개변수를 모두 사용하여 `argmax()` 함수를 호출할 수 있으며, 이번에는 오류 없이 실행됩니다.

np.argmax(array_2,axis=1,out=out_arr)

`argmax()` 함수의 출력은 이제 배열 `out_arr`에서 접근할 수 있습니다.

print(out_arr)
# 출력
[2 0]

결론

본 튜토리얼이 NumPy의 `argmax()` 함수를 사용하는 방법을 이해하는 데 도움이 되었기를 바랍니다. 주피터 노트북에서 코드 예제를 실행해 볼 수 있습니다.

배운 내용을 요약해 보겠습니다.

  • NumPy `argmax()` 함수는 배열의 최댓값의 인덱스를 반환합니다. 배열 `a`에서 최댓값이 두 번 이상 나타날 경우, `np.argmax(a)`는 해당 요소가 처음으로 나타나는 인덱스를 반환합니다.
  • 다차원 배열을 다룰 때는 선택적 `axis` 매개변수를 사용하여 특정 축을 따라 최댓값의 인덱스를 가져올 수 있습니다. 예를 들어, 2차원 배열에서 `axis=0` 및 `axis=1`로 설정하면 각각 행과 열을 따라 최댓값의 인덱스를 얻을 수 있습니다.
  • 반환된 값을 다른 배열에 저장하려면 선택적 `out` 매개변수를 출력 배열로 설정할 수 있습니다. 하지만 출력 배열은 형태와 데이터 타입이 호환되어야 합니다.

다음으로 파이썬 세트에 대한 자세한 가이드를 확인해보세요.