워로디스

Python. NumPy 인덱싱,필터링 체계 본문

개발/Python

Python. NumPy 인덱싱,필터링 체계

워로디스 2026. 6. 1. 22:52

1. 기본 인덱싱: 정수 인덱싱

특정 위치 하나를 직접 지정하는 방식입니다.

a = np.array([10, 20, 30, 40])

a[2]
# 30

2차원 배열에서는 보통 [행, 열] 구조입니다.

A = np.array([
    [10, 11, 12],
    [20, 21, 22],
    [30, 31, 32],
])

A[1, 2]
# 22

해석:

A[1, 2]
 1 -> 1번 행
 2 -> 2번 열

즉 좌표로 보면 (1, 2) 위치의 값을 가져오는 것입니다.

2. 슬라이스 인덱싱

:를 이용해서 범위를 선택하는 방식입니다.

a[1:3]

은 내부적으로 대략:

a[slice(1, 3, None)]

과 비슷합니다.

예:

a = np.array([10, 20, 30, 40])

a[1:3]
# array([20, 30])

stop은 포함되지 않으므로 1:3은 인덱스 1, 2를 선택합니다.

2차원에서는 축마다 슬라이스를 줄 수 있습니다.

A[:, 1]

해석:

: -> 모든 행
1 -> 1번 열

결과:

array([11, 21, 31])

다른 예:

A[1, :]      # 1번 행의 모든 열
A[:2, :]     # 0~1번 행, 모든 열
A[:, 1:3]    # 모든 행, 1~2번 열
A[:2, 1:3]   # 0~1번 행, 1~2번 열

정리하면:

:      -> 해당 축 전체
:3     -> 처음부터 3 전까지
1:3    -> 1부터 3 전까지
::2    -> 처음부터 끝까지 2칸씩
[::-1] -> 역순

3. :는 와일드카드가 아니라 slice 표기

예를 들어:

A[:, 1]

에서 :는 와일드카드처럼 보이지만, 정확히는 해당 축 전체를 선택하는 슬라이스입니다.

:

는 내부적으로:

slice(None, None, None)

에 해당합니다.

그래서:

A[:, 1]

은 개념적으로:

A.__getitem__((slice(None, None, None), 1))

처럼 이해할 수 있습니다.

A[::, 1]도 됩니다.

A[::, 1]

A[:, 1]과 같습니다. ::start, stop, step을 모두 생략한 슬라이스입니다.

4. 불리언 인덱싱 / 불리언 마스크

조건에 맞는 원소만 골라내는 방식입니다.

a = np.array([10, 20, 30, 40])

a[a > 20]
# array([30, 40])

여기서:

a > 20

의 결과는:

array([False, False, True, True])

입니다. 이 배열이 불리언 마스크입니다.

즉:

a[[False, False, True, True]]

는:

False -> 제외
False -> 제외
True  -> 선택
True  -> 선택

이라는 뜻입니다.

따라서:

a[[True, False, True, False]]
# array([10, 30])

여기서 [True, False, True, False]는 슬라이스가 아닙니다. 파이썬 리스트이거나 NumPy bool 배열이고, NumPy 인덱싱 문맥에서는 불리언 마스크로 해석됩니다.

조건을 조합할 수도 있습니다.

a[(a > 10) & (a < 40)]
# array([20, 30])

주의:

(a > 10) & (a < 40)    # 맞음
(a > 10) and (a < 40)  # NumPy에서는 보통 에러

NumPy 배열 조건 조합에서는 and, or, not 대신 &, |, ~를 씁니다.

a[(a < 20) | (a > 30)]
a[~(a > 20)]

5. 정수 배열 인덱싱 / fancy indexing

원하는 위치 번호를 직접 나열해서 선택하는 방식입니다.

a = np.array([10, 20, 30, 40])

a[[0, 2]]
# array([10, 30])

여기서 [0, 2]는 슬라이스가 아닙니다.
“0번 원소와 2번 원소를 가져와라”라는 정수 인덱스 목록입니다.

2차원에서도 쓸 수 있습니다.

A[[0, 2], :]

해석:

0번 행과 2번 행, 모든 열
A[:, [0, 2]]

해석:

모든 행, 0번 열과 2번 열

주의할 점:

A[[0, 2], [1, 2]]

이건 행과 열의 조합 전체를 가져오는 게 아니라, 좌표쌍으로 선택합니다.

(0, 1)
(2, 2)

즉 두 원소만 가져옵니다.

부분 행렬을 원하면 np.ix_를 씁니다.

A[np.ix_([0, 2], [1, 2])]

이건:

0번, 2번 행
1번, 2번 열

의 모든 조합을 가져옵니다.

6. 좌표 기반 선택

좌표 기반 선택은 값을 바로 고르는 것이 아니라, 먼저 조건을 만족하는 위치를 좌표로 찾고, 그 좌표를 이용해서 값을 선택하는 방식입니다.

예를 들어:

A = np.array([
    [0, 10, 0],
    [20, 0, 30],
])

이 배열의 좌표는 다음과 같습니다.

값:      0     10     0
좌표:  (0,0) (0,1) (0,2)

값:     20     0     30
좌표:  (1,0) (1,1) (1,2)

0이 아닌 값은:

10 -> (0, 1)
20 -> (1, 0)
30 -> (1, 2)

에 있습니다.

np.nonzero

rows, cols = np.nonzero(A)

rows
# array([0, 1, 1])

cols
# array([1, 0, 2])

이 둘을 짝지으면:

(0, 1)
(1, 0)
(1, 2)

입니다.

이 좌표를 다시 배열에 넣으면 값을 가져올 수 있습니다.

A[rows, cols]
# array([10, 20, 30])

즉:

rows, cols = np.nonzero(A)
A[rows, cols]

는 “0이 아닌 값들의 좌표를 찾아서, 그 위치의 값을 가져와라”입니다.

np.argwhere

np.argwhere는 좌표를 더 직관적으로 보여줍니다.

np.argwhere(A)

결과:

array([
    [0, 1],
    [1, 0],
    [1, 2]
])

즉:

0이 아닌 값은 (0,1), (1,0), (1,2)에 있다

라는 뜻입니다.

비교하면:

A[A != 0]

는 조건에 맞는 값 자체를 가져옵니다.

array([10, 20, 30])

반면:

np.argwhere(A != 0)

는 조건에 맞는 값의 위치를 가져옵니다.

array([
    [0, 1],
    [1, 0],
    [1, 2]
])

따라서 이렇게 구분하면 됩니다.

불리언 필터링:
조건에 맞는 값을 바로 가져옴

좌표 기반 선택:
조건에 맞는 위치를 먼저 찾음
그 위치를 이용해서 값을 가져올 수 있음

7. np.where

np.where는 두 가지 방식으로 자주 쓰입니다.

조건을 만족하는 위치 찾기

a = np.array([10, 20, 30, 40])

np.where(a > 20)
# (array([2, 3]),)

조건을 만족하는 위치가 2, 3이라는 뜻입니다.

그 위치를 이용해서 값을 가져올 수 있습니다.

a[np.where(a > 20)]
# array([30, 40])

조건에 따라 값 바꾸기

np.where(a > 20, "big", "small")
# array(['small', 'small', 'big', 'big'], dtype='<U5')

해석:

a > 20이면 "big"
아니면 "small"

이 경우는 필터링보다는 조건부 변환에 가깝습니다.

8. ... / Ellipsis 인덱싱

차원이 많은 배열에서 중간의 여러 :를 생략할 때 씁니다.

x[..., 0]

예를 들어 x가 3차원 배열이면:

x[:, :, 0]

과 비슷합니다.

x가 4차원 배열이면:

x[:, :, :, 0]

과 비슷합니다.

즉:

... -> 필요한 만큼 :를 채워 넣는 표현

예:

x[0, ...]   # 첫 번째 축은 0번, 나머지는 전부
x[..., 0]   # 마지막 축은 0번, 앞의 축들은 전부

9. None / np.newaxis

새 축을 추가하는 인덱싱입니다. 값을 필터링한다기보다는 배열의 모양을 바꿉니다.

a = np.array([10, 20, 30])

a.shape
# (3,)
a[:, None]

결과 모양:

(3, 1)

즉 세로 벡터처럼 됩니다.

array([
    [10],
    [20],
    [30]
])

반대로:

a[None, :]

결과 모양:

(1, 3)

즉 가로 벡터처럼 됩니다.

array([[10, 20, 30]])

np.newaxisNone과 같습니다.

a[:, np.newaxis]

10. 멤버십 필터링: np.isin

여러 값 중 특정 값들만 고르고 싶을 때 씁니다.

a = np.array([10, 20, 30, 40, 50])

np.isin(a, [20, 40])
# array([False, True, False, True, False])

이 결과는 불리언 마스크입니다.

a[np.isin(a, [20, 40])]
# array([20, 40])

반대로 제외하려면 ~를 씁니다.

a[~np.isin(a, [20, 40])]
# array([10, 30, 50])

11. NaN / inf 필터링

결측값이나 무한대를 제외할 때 자주 씁니다.

a = np.array([1.0, np.nan, 3.0, np.inf])

a[~np.isnan(a)]
# array([ 1.,  3., inf])

NaN만 제외합니다.

유한한 값만 남기려면:

a[np.isfinite(a)]
# array([1., 3.])

이건 NaN, inf, -inf를 제외합니다.

12. 축 기준 선택 함수: take, compress

np.take

정수 인덱스 목록으로 가져오는 함수형 방식입니다.

a = np.array([10, 20, 30, 40])

np.take(a, [0, 2])
# array([10, 30])

2차원에서는 축을 지정할 수 있습니다.

np.take(A, [0, 2], axis=0)

0번 행과 2번 행을 가져옵니다.

np.take(A, [0, 2], axis=1)

0번 열과 2번 열을 가져옵니다.

np.compress

불리언 마스크로 특정 축을 선택합니다.

np.compress([True, False, True], A, axis=0)

행 기준으로 0번 행과 2번 행을 선택합니다.

np.compress([True, False, True], A, axis=1)

열 기준으로 0번 열과 2번 열을 선택합니다.

13. 전체 분류 요약

NumPy 인덱싱/필터링은 크게 이렇게 나눌 수 있습니다.

1. 정수 인덱싱
   a[2], A[1, 2]

2. 슬라이스 인덱싱
   a[1:3], A[:, 1]

3. 불리언 인덱싱 / 불리언 마스크
   a[a > 20], A[A != 0]

4. 정수 배열 인덱싱 / fancy indexing
   a[[0, 2]], A[:, [0, 2]]

5. 좌표 기반 선택
   np.nonzero, np.argwhere, np.where

6. Ellipsis
   x[..., 0]

7. 새 축 추가
   a[:, None], a[np.newaxis, :]

8. 멤버십 필터링
   np.isin

9. 결측값 / 무한대 필터링
   np.isnan, np.isfinite

10. 축 기준 선택 함수
   np.take, np.compress

11. 부분 행렬 조합
   np.ix_
반응형